From 00db1460fb2e62a5a8cda42012ee6f19a36d7947 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Apr 10 2018 05:45:10 +0000 Subject: import keepalived-1.3.5-6.el7 --- diff --git a/SOURCES/bz1477563-fix-keepalived_script-user.patch b/SOURCES/bz1477563-fix-keepalived_script-user.patch new file mode 100644 index 0000000..49ee81a --- /dev/null +++ b/SOURCES/bz1477563-fix-keepalived_script-user.patch @@ -0,0 +1,729 @@ +From 99f31a89d2f5803fe2d6229f2557e72efb3ef95f Mon Sep 17 00:00:00 2001 +From: Quentin Armitage +Date: Tue, 28 Mar 2017 09:11:44 +0100 +Subject: [PATCH] Make dynamic flag bool + +Signed-off-by: Quentin Armitage + +Fix releasing malloc'd memory for saved core pattern + +Signed-off-by: Quentin Armitage + +Fix detecting default script uid/gid + +Signed-off-by: Quentin Armitage + +Don't complain about keepalived_script user if not needed + +keepalived logged a warning every time if the keepalived_script user +didn't exist. We only need that warning if there is a script that uses +the default user, and an alternative defult user isn't specified. + +Signed-off-by: Quentin Armitage +--- + doc/keepalived.conf.SYNOPSIS | 30 +++++---- + doc/man/man5/keepalived.conf.5 | 4 +- + keepalived/check/check_misc.c | 72 ++++++++++++++++----- + keepalived/check/check_parser.c | 9 +-- + keepalived/core/global_parser.c | 49 +------------- + keepalived/core/main.c | 15 +---- + keepalived/include/check_misc.h | 2 +- + keepalived/include/main.h | 2 - + keepalived/vrrp/vrrp_data.c | 3 +- + keepalived/vrrp/vrrp_parser.c | 46 ++++++++++--- + keepalived/vrrp/vrrp_print.c | 1 + + lib/notify.c | 140 ++++++++++++++++++++++++++++++++++------ + lib/notify.h | 7 +- + 13 files changed, 242 insertions(+), 138 deletions(-) + +diff --git a/doc/keepalived.conf.SYNOPSIS b/doc/keepalived.conf.SYNOPSIS +index 5b1dfb8..90eb83d 100644 +--- a/doc/keepalived.conf.SYNOPSIS ++++ b/doc/keepalived.conf.SYNOPSIS +@@ -568,12 +568,14 @@ virtual_server group { # VS group declaration + weight # weight to use (default: 1) + inhibit_on_failure # Set weight to 0 on healthchecker + # failure +- notify_up | # Script to launch when +- # healthchecker consider service +- # as up. +- notify_down | # Script to launch when +- # healthchecker consider service +- # as down. ++ notify_up | [username [groupname]] ++ # Script to launch when ++ # healthchecker consider service ++ # as up. ++ notify_down | [username [groupname]] ++ # Script to launch when ++ # healthchecker consider service ++ # as down. + + HTTP_GET|SSL_GET { # HTTP and SSL healthcheckers + url { # A set of url to test +@@ -603,8 +605,8 @@ virtual_server group { # VS group declaration + real_server { # Idem + weight # Idem + inhibit_on_failure # Idem +- notify_up | # Idem +- notify_down | # Idem ++ notify_up | [username [groupname]] # Idem ++ notify_down | [username [groupname]] # Idem + + TCP_CHECK { # TCP healthchecker + connect_ip # IP address to connect +@@ -620,8 +622,8 @@ virtual_server group { # VS group declaration + real_server { # Idem + weight # Idem + inhibit_on_failure # Idem +- notify_up | # Idem +- notify_down | # Idem ++ notify_up | [username [groupname]] # Idem ++ notify_down | [username [groupname]] # Idem + + SMTP_CHECK { # SMTP healthchecker + connect_ip # Optional IP address to connect to +@@ -658,8 +660,8 @@ virtual_server group { # VS group declaration + real_server { # Idem + weight # Idem + inhibit_on_failure # Idem +- notify_up | # Idem +- notify_down | # Idem ++ notify_up | [username [groupname]] # Idem ++ notify_down | [username [groupname]] # Idem + + DNS_CHECK { # DNS healthchecker + connect_ip # Optional IP address to connect to +@@ -677,8 +679,8 @@ virtual_server group { # VS group declaration + real_server { # Idem + weight # Idem + inhibit_on_failure # Idem +- notify_up | # Idem +- notify_down | # Idem ++ notify_up | [username [groupname]] # Idem ++ notify_down | [username [groupname]] # Idem + + MISC_CHECK { # MISC healthchecker + misc_path | # External system script or program +diff --git a/doc/man/man5/keepalived.conf.5 b/doc/man/man5/keepalived.conf.5 +index 9ce2206..be33063 100644 +--- a/doc/man/man5/keepalived.conf.5 ++++ b/doc/man/man5/keepalived.conf.5 +@@ -711,10 +711,10 @@ A virtual_server can be a declaration of one of + + # Script to execute when healthchecker + # considers service as up. +- notify_up | ++ notify_up | [username [groupname]] + # Script to execute when healthchecker + # considers service as down. +- notify_down | ++ notify_down | [username [groupname]] + + uthreshold # maximum number of connections to server + lthreshold # minimum number of connections to server +diff --git a/keepalived/check/check_misc.c b/keepalived/check/check_misc.c +index ccb9b63..a041d81 100644 +--- a/keepalived/check/check_misc.c ++++ b/keepalived/check/check_misc.c +@@ -44,6 +44,11 @@ static int misc_check_thread(thread_t *); + static int misc_check_child_thread(thread_t *); + static int misc_check_child_timeout_thread(thread_t *); + ++static bool script_user_set; ++static bool remove_script; ++static misc_checker_t *misck_checker; ++ ++ + /* Configuration stream handling */ + static void + free_misc_check(void *data) +@@ -70,49 +75,83 @@ dump_misc_check(void *data) + static void + misc_check_handler(__attribute__((unused)) vector_t *strvec) + { +- misc_checker_t *misck_checker = (misc_checker_t *) MALLOC(sizeof (misc_checker_t)); +- +- misck_checker->uid = default_script_uid; +- misck_checker->gid = default_script_gid; ++ misck_checker = (misc_checker_t *) MALLOC(sizeof (misc_checker_t)); + +- /* queue new checker */ +- queue_checker(free_misc_check, dump_misc_check, misc_check_thread, +- misck_checker, NULL); ++ script_user_set = false; + } + + static void + misc_path_handler(vector_t *strvec) + { +- misc_checker_t *misck_checker = CHECKER_GET(); ++ if (!misck_checker) ++ return; ++ + misck_checker->path = CHECKER_VALUE_STRING(strvec); + } + + static void + misc_timeout_handler(vector_t *strvec) + { +- misc_checker_t *misck_checker = CHECKER_GET(); ++ if (!misck_checker) ++ return; ++ + misck_checker->timeout = CHECKER_VALUE_UINT(strvec) * TIMER_HZ; + } + + static void + misc_dynamic_handler(__attribute__((unused)) vector_t *strvec) + { +- misc_checker_t *misck_checker = CHECKER_GET(); +- misck_checker->dynamic = 1; ++ if (!misck_checker) ++ return; ++ ++ misck_checker->dynamic = true; + } + + static void + misc_user_handler(vector_t *strvec) + { +- misc_checker_t *misck_checker = CHECKER_GET(); ++ if (!misck_checker) ++ return; + + if (vector_size(strvec) < 2) { + log_message(LOG_INFO, "No user specified for misc checker script %s", misck_checker->path); + return; + } + +- if (set_script_uid_gid(strvec, 1, &misck_checker->uid, &misck_checker->gid)) +- log_message(LOG_INFO, "Failed to set uid/gid for misc checker script %s", misck_checker->path); ++ if (set_script_uid_gid(strvec, 1, &misck_checker->uid, &misck_checker->gid)) { ++ log_message(LOG_INFO, "Failed to set uid/gid for misc checker script %s - removing", misck_checker->path); ++ FREE(misck_checker); ++ misck_checker = NULL; ++ } ++ else ++ script_user_set = true; ++} ++ ++static void ++misc_end_handler(void) ++{ ++ if (!misck_checker) ++ return; ++ ++ if (!script_user_set) ++ { ++log_message(LOG_INFO, "remove_script 1 %d", remove_script); ++ if ( set_default_script_user(NULL, NULL, global_data->script_security)) { ++ log_message(LOG_INFO, "Unable to set default user for misc script %s - removing", misck_checker->path); ++ FREE(misck_checker); ++ misck_checker = NULL; ++ return; ++ } ++ ++log_message(LOG_INFO, "Setting uid.gid"); ++ misck_checker->uid = default_script_uid; ++ misck_checker->gid = default_script_gid; ++ } ++ ++ /* queue new checker */ ++ queue_checker(free_misc_check, dump_misc_check, misc_check_thread, misck_checker, NULL); ++ misck_checker = NULL; ++log_message(LOG_INFO, "Leaving misc_end_handler"); + } + + void +@@ -125,6 +164,7 @@ install_misc_check_keyword(void) + install_keyword("misc_dynamic", &misc_dynamic_handler); + install_keyword("warmup", &warmup_handler); + install_keyword("user", &misc_user_handler); ++ install_sublevel_end_handler(&misc_end_handler); + install_sublevel_end(); + } + +@@ -251,13 +291,13 @@ misc_check_child_thread(thread_t * thread) + int status; + status = WEXITSTATUS(wait_status); + if (status == 0 || +- (misck_checker->dynamic == 1 && status >= 2 && status <= 255)) { ++ (misck_checker->dynamic && status >= 2 && status <= 255)) { + /* + * The actual weight set when using misc_dynamic is two less than + * the exit status returned. Effective range is 0..253. + * Catch legacy case of status being 0 but misc_dynamic being set. + */ +- if (misck_checker->dynamic == 1 && status != 0) ++ if (misck_checker->dynamic && status != 0) + update_svr_wgt(status - 2, checker->vs, + checker->rs, true); + +diff --git a/keepalived/check/check_parser.c b/keepalived/check/check_parser.c +index 2adac98..382861c 100644 +--- a/keepalived/check/check_parser.c ++++ b/keepalived/check/check_parser.c +@@ -322,14 +322,7 @@ inhibit_handler(__attribute__((unused)) vector_t *strvec) + static inline notify_script_t* + set_check_notify_script(vector_t *strvec) + { +- notify_script_t *script = notify_script_init(strvec, default_script_uid, default_script_gid); +- +- if (vector_size(strvec) > 2 ) { +- if (set_script_uid_gid(strvec, 2, &script->uid, &script->gid)) +- log_message(LOG_INFO, "Invalid user/group for quorum/notify script %s", script->name); +- } +- +- return script; ++ return notify_script_init(strvec, "quorum/notify", global_data->script_security); + } + static void + notify_up_handler(vector_t *strvec) +diff --git a/keepalived/core/global_parser.c b/keepalived/core/global_parser.c +index 45d4cfb..a59fbc0 100644 +--- a/keepalived/core/global_parser.c ++++ b/keepalived/core/global_parser.c +@@ -701,53 +701,6 @@ use_pid_dir_handler(__attribute__((unused)) vector_t *strvec) + use_pid_dir = true; + } + +-bool +-set_script_uid_gid(vector_t *strvec, unsigned keyword_offset, uid_t *uid_p, gid_t *gid_p) +-{ +- char *username; +- char *groupname; +- uid_t uid; +- gid_t gid; +- struct passwd pwd; +- struct passwd *pwd_p; +- struct group grp; +- struct group *grp_p; +- int ret; +- char buf[getpwnam_buf_len]; +- +- username = strvec_slot(strvec, keyword_offset); +- +- if ((ret = getpwnam_r(username, &pwd, buf, sizeof(buf), &pwd_p))) { +- log_message(LOG_INFO, "Unable to resolve script username '%s' - ignoring", username); +- return true; +- } +- if (!pwd_p) { +- log_message(LOG_INFO, "Script user '%s' does not exist", username); +- return true; +- } +- +- uid = pwd.pw_uid; +- gid = pwd.pw_gid; +- +- if (vector_size(strvec) > keyword_offset + 1) { +- groupname = strvec_slot(strvec, keyword_offset + 1); +- if ((ret = getgrnam_r(groupname, &grp, buf, sizeof(buf), &grp_p))) { +- log_message(LOG_INFO, "Unable to resolve script group name '%s' - ignoring", groupname); +- return true; +- } +- if (!grp_p) { +- log_message(LOG_INFO, "Script group '%s' does not exist", groupname); +- return true; +- } +- gid = grp.gr_gid; +- } +- +- *uid_p = uid; +- *gid_p = gid; +- +- return false; +-} +- + static void + script_user_handler(vector_t *strvec) + { +@@ -756,7 +709,7 @@ script_user_handler(vector_t *strvec) + return; + } + +- if (set_script_uid_gid(strvec, 1, &default_script_uid, &default_script_gid)) ++ if (set_default_script_user(strvec_slot(strvec, 1), vector_size(strvec) > 2 ? strvec_slot(strvec, 2) : NULL, true)) + log_message(LOG_INFO, "Error setting global script uid/gid"); + } + +diff --git a/keepalived/core/main.c b/keepalived/core/main.c +index 55eb263..95bb76a 100644 +--- a/keepalived/core/main.c ++++ b/keepalived/core/main.c +@@ -119,6 +119,9 @@ free_parent_mallocs_startup(bool am_child) + #else + free(syslog_ident); + #endif ++ ++ if (orig_core_dump_pattern) ++ FREE_PTR(orig_core_dump_pattern); + } + + if (free_main_pidfile) { +@@ -765,7 +768,6 @@ keepalived_main(int argc, char **argv) + bool report_stopped = true; + struct utsname uname_buf; + char *end; +- long buf_len; + + /* Init debugging level */ + debug = 0; +@@ -814,17 +816,6 @@ keepalived_main(int argc, char **argv) + + netlink_set_recv_buf_size(); + +- set_default_script_user(&default_script_uid, &default_script_gid); +- +- /* Get buffer length needed for getpwnam_r/getgrnam_r */ +- if ((buf_len = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) +- getpwnam_buf_len = 1024; /* A safe default if no value is returned */ +- else +- getpwnam_buf_len = (size_t)buf_len; +- if ((buf_len = sysconf(_SC_GETGR_R_SIZE_MAX)) != -1 && +- (size_t)buf_len > getpwnam_buf_len) +- getpwnam_buf_len = (size_t)buf_len; +- + /* Some functionality depends on kernel version, so get the version here */ + if (uname(&uname_buf)) + log_message(LOG_INFO, "Unable to get uname() information - error %d", errno); +diff --git a/keepalived/include/check_misc.h b/keepalived/include/check_misc.h +index ed0d962..ec97149 100644 +--- a/keepalived/include/check_misc.h ++++ b/keepalived/include/check_misc.h +@@ -36,7 +36,7 @@ + typedef struct _misc_checker { + char *path; + unsigned long timeout; +- int dynamic; /* 0: old-style, 1: exit code from checker affects weight */ ++ bool dynamic; /* false: old-style, true: exit code from checker affects weight */ + bool forcing_termination; /* Set if we have sent the process a SIGTERM */ + uid_t uid; /* uid for script execution */ + gid_t gid; /* gid for script execution */ +diff --git a/keepalived/include/main.h b/keepalived/include/main.h +index d125566..eebde77 100644 +--- a/keepalived/include/main.h ++++ b/keepalived/include/main.h +@@ -73,8 +73,6 @@ extern bool namespace_with_ipsets; /* override for namespaces with ipsets on Lin + #endif + extern char *instance_name; /* keepalived instance name */ + extern bool use_pid_dir; /* pid files in /var/run/keepalived */ +-extern uid_t default_script_uid; /* Default user/group for script execution */ +-extern gid_t default_script_gid; + extern unsigned os_major; /* Kernel version */ + extern unsigned os_minor; + extern unsigned os_release; +diff --git a/keepalived/vrrp/vrrp_data.c b/keepalived/vrrp/vrrp_data.c +index 76f17a4..b5c59df 100644 +--- a/keepalived/vrrp/vrrp_data.c ++++ b/keepalived/vrrp/vrrp_data.c +@@ -160,8 +160,7 @@ dump_vscript(void *data) + str = (vscript->result >= vscript->rise) ? "GOOD" : "BAD"; + } + log_message(LOG_INFO, " Status = %s", str); +- if (vscript->uid || vscript->gid) +- log_message(LOG_INFO, " Script uid:gid = %d:%d", vscript->uid, vscript->gid); ++ log_message(LOG_INFO, " Script uid:gid = %d:%d", vscript->uid, vscript->gid); + + } + +diff --git a/keepalived/vrrp/vrrp_parser.c b/keepalived/vrrp/vrrp_parser.c +index 7a38315..c774dec 100644 +--- a/keepalived/vrrp/vrrp_parser.c ++++ b/keepalived/vrrp/vrrp_parser.c +@@ -48,6 +48,9 @@ + #include "bitops.h" + #include "notify.h" + ++static bool script_user_set; ++static bool remove_script; ++ + /* Static addresses handler */ + static void + static_addresses_handler(__attribute__((unused)) vector_t *strvec) +@@ -120,14 +123,7 @@ vrrp_group_handler(vector_t *strvec) + static inline notify_script_t* + set_vrrp_notify_script(vector_t *strvec) + { +- notify_script_t *script = notify_script_init(strvec, default_script_uid, default_script_gid); +- +- if (vector_size(strvec) > 2) { +- if (set_script_uid_gid(strvec, 2, &script->uid, &script->gid)) +- log_message(LOG_INFO, "Invalid user/group for notify script %s", script->name); +- } +- +- return script; ++ return notify_script_init(strvec, "notify", global_data->script_security); + } + + static void +@@ -680,6 +676,8 @@ static void + vrrp_script_handler(vector_t *strvec) + { + alloc_vrrp_script(strvec_slot(strvec, 1)); ++ script_user_set = false; ++ remove_script = false; + } + static void + vrrp_vscript_script_handler(vector_t *strvec) +@@ -731,8 +729,35 @@ static void + vrrp_vscript_user_handler(vector_t *strvec) + { + vrrp_script_t *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); +- if (set_script_uid_gid(strvec, 1, &vscript->uid, &vscript->gid)) +- log_message(LOG_INFO, "Unable to set uid/gid for script %s", vscript->script); ++ if (set_script_uid_gid(strvec, 1, &vscript->uid, &vscript->gid)) { ++ log_message(LOG_INFO, "Unable to set uid/gid for script %s - disabling", vscript->script); ++ remove_script = true; ++ } ++ else ++ script_user_set = true; ++} ++static void ++vrrp_vscript_end_handler(void) ++{ ++ vrrp_script_t *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); ++ ++ if (script_user_set) ++ return; ++ ++ if (!remove_script && ++ set_default_script_user(NULL, NULL, global_data->script_security)) { ++ log_message(LOG_INFO, "Unable to set default user for track script %s - removing", vscript->script); ++ remove_script = true; ++ } ++ ++ if (remove_script) { ++ free_list_element(vrrp_data->vrrp_script, vrrp_data->vrrp_script->tail); ++ return; ++ } ++ ++ vscript->uid = default_script_uid; ++ vscript->gid = default_script_gid; ++ + } + static void + vrrp_vscript_init_fail_handler(__attribute__((unused)) vector_t *strvec) +@@ -964,6 +989,7 @@ init_vrrp_keywords(bool active) + install_keyword("fall", &vrrp_vscript_fall_handler); + install_keyword("user", &vrrp_vscript_user_handler); + install_keyword("init_fail", &vrrp_vscript_init_fail_handler); ++ install_sublevel_end_handler(&vrrp_vscript_end_handler); + } + + vector_t * +diff --git a/keepalived/vrrp/vrrp_print.c b/keepalived/vrrp/vrrp_print.c +index 7adb701..54da044 100644 +--- a/keepalived/vrrp/vrrp_print.c ++++ b/keepalived/vrrp/vrrp_print.c +@@ -92,6 +92,7 @@ vscript_print(FILE *file, void *data) + fprintf(file, " Rise = %d\n", vscript->rise); + fprintf(file, " Full = %d\n", vscript->fall); + fprintf(file, " Insecure = %s\n", vscript->insecure ? "yes" : "no"); ++ fprintf(file, " uid:gid = %d:%d\n", vscript->uid, vscript->gid); + + switch (vscript->result) { + case VRRP_SCRIPT_STATUS_INIT: +diff --git a/lib/notify.c b/lib/notify.c +index d92c50d..a8742fe 100644 +--- a/lib/notify.c ++++ b/lib/notify.c +@@ -44,10 +44,15 @@ + #include "vector.h" + #include "parser.h" + +-size_t getpwnam_buf_len; /* Buffer length needed for getpwnam_r/getgrname_r */ + ++uid_t default_script_uid; /* Default user/group for script execution */ ++gid_t default_script_gid; ++static bool default_script_uid_set = false; ++static bool default_user_fail = false; /* Set if failed to set default user, ++ unless it defaults to root */ + static char *path; + static bool path_is_malloced; ++static size_t getpwnam_buf_len; /* Buffer length needed for getpwnam_r/getgrname_r */ + + /* perform a system call */ + static int +@@ -565,40 +570,135 @@ check_notify_script_secure(notify_script_t **script_p, bool script_security, boo + return flags; + } + +-/* The default script user/group is keepalived_script if it exists, or root otherwise */ +-void +-set_default_script_user(uid_t *uid, gid_t *gid) ++static void ++set_pwnam_buf_len(void) ++{ ++ long buf_len; ++ ++ /* Get buffer length needed for getpwnam_r/getgrnam_r */ ++ if ((buf_len = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) ++ getpwnam_buf_len = 1024; /* A safe default if no value is returned */ ++ else ++ getpwnam_buf_len = (size_t)buf_len; ++ if ((buf_len = sysconf(_SC_GETGR_R_SIZE_MAX)) != -1 && ++ (size_t)buf_len > getpwnam_buf_len) ++ getpwnam_buf_len = (size_t)buf_len; ++} ++ ++bool ++set_uid_gid(const char *username, const char *groupname, uid_t *uid_p, gid_t *gid_p, bool default_user) + { +- char buf[getpwnam_buf_len]; +- char *default_user_name = "keepalived_script"; ++ uid_t uid; ++ gid_t gid; + struct passwd pwd; + struct passwd *pwd_p; ++ struct group grp; ++ struct group *grp_p; ++ int ret; ++ bool using_default_default_user = false; ++ ++ if (!getpwnam_buf_len) ++ set_pwnam_buf_len(); + +- if (getpwnam_r(default_user_name, &pwd, buf, sizeof(buf), &pwd_p)) { +- log_message(LOG_INFO, "Unable to resolve default script username '%s' - ignoring", default_user_name); +- return; ++ { ++ char buf[getpwnam_buf_len]; ++ ++ if (default_user && !username) { ++ using_default_default_user = true; ++ username = "keepalived_script"; ++ } ++ ++ if ((ret = getpwnam_r(username, &pwd, buf, sizeof(buf), &pwd_p))) { ++ log_message(LOG_INFO, "Unable to resolve %sscript username '%s' - ignoring", default_user ? "default " : "", username); ++ return true; ++ } ++ if (!pwd_p) { ++ if (using_default_default_user) ++ log_message(LOG_INFO, "WARNING - default user '%s' for script execution does not exist - please create.", username); ++ else ++ log_message(LOG_INFO, "%script user '%s' does not exist", default_user ? "Default s" : "S", username); ++ return true; ++ } ++ ++ uid = pwd.pw_uid; ++ gid = pwd.pw_gid; ++ ++ if (groupname) { ++ if ((ret = getgrnam_r(groupname, &grp, buf, sizeof(buf), &grp_p))) { ++ log_message(LOG_INFO, "Unable to resolve %sscript group name '%s' - ignoring", default_user ? "default " : "", groupname); ++ return true; ++ } ++ if (!grp_p) { ++ log_message(LOG_INFO, "%script group '%s' does not exist", default_user ? "Default s" : "S", groupname); ++ return true; ++ } ++ gid = grp.gr_gid; ++ } ++ ++ *uid_p = uid; ++ *gid_p = gid; + } +- if (!pwd_p) { +- /* The username does not exist */ +- log_message(LOG_INFO, "WARNING - default user '%s' for script execution does not exist - please create.", default_user_name); +- return; ++ ++ return false; ++} ++ ++bool ++set_default_script_user(const char *username, const char *groupname, bool script_security) ++{ ++ if (!default_script_uid_set || username) { ++ /* Even if we fail to set it, there is no point in trying again */ ++ default_script_uid_set = true; ++ ++ if (set_uid_gid(username, groupname, &default_script_uid, &default_script_gid, true)) { ++ if (username || script_security) ++ default_user_fail = true; ++ } ++ else ++ default_user_fail = false; + } + +- *uid = pwd.pw_uid; +- *gid = pwd.pw_gid; ++ return default_user_fail; ++} ++ ++bool ++set_script_uid_gid(vector_t *strvec, unsigned keyword_offset, uid_t *uid_p, gid_t *gid_p) ++{ ++ char *username; ++ char *groupname; ++ ++ username = strvec_slot(strvec, keyword_offset); ++ if (vector_size(strvec) > keyword_offset + 1) ++ groupname = strvec_slot(strvec, keyword_offset + 1); ++ else ++ groupname = NULL; + +- log_message(LOG_INFO, "Setting default script user to '%s', uid:gid %d:%d", default_user_name, pwd.pw_uid, pwd.pw_gid); ++ return set_uid_gid(username, groupname, uid_p, gid_p, false); + } + + notify_script_t* +-notify_script_init(vector_t *strvec, uid_t uid, gid_t gid) ++notify_script_init(vector_t *strvec, const char *type, bool script_security) + { + notify_script_t *script = MALLOC(sizeof(notify_script_t)); + + script->name = set_value(strvec); +- script->uid = uid; +- script->gid = gid; ++ ++ if (vector_size(strvec) > 2) { ++ if (set_script_uid_gid(strvec, 2, &script->uid, &script->gid)) { ++ log_message(LOG_INFO, "Invalid user/group for %s script %s - ignoring", type, script->name); ++ FREE(script); ++ return NULL; ++ } ++ } ++ else { ++ if (set_default_script_user(NULL, NULL, script_security)) { ++ log_message(LOG_INFO, "Failed to set default user for %s script %s - ignoring", type, script->name); ++ FREE(script); ++ return NULL; ++ } ++ ++ script->uid = default_script_uid; ++ script->gid = default_script_gid; ++ } + + return script; + } +- +diff --git a/lib/notify.h b/lib/notify.h +index ac07edb..3d092ea 100644 +--- a/lib/notify.h ++++ b/lib/notify.h +@@ -56,7 +56,8 @@ free_notify_script(notify_script_t **script) + } + + /* Global variables */ +-extern size_t getpwnam_buf_len; /* Buffer length needed for getpwnam_r/getgrnam_r */ ++extern uid_t default_script_uid; /* Default user/group for script execution */ ++extern gid_t default_script_gid; + + /* prototypes */ + extern int system_call_script(thread_master_t *, int (*) (thread_t *), void *, unsigned long, const char*, uid_t, gid_t); +@@ -64,7 +65,7 @@ extern int notify_exec(const notify_script_t *); + extern void script_killall(thread_master_t *, int); + extern int check_script_secure(notify_script_t *, bool, bool); + extern int check_notify_script_secure(notify_script_t **, bool, bool); +-extern void set_default_script_user(uid_t *, gid_t *); +-extern notify_script_t* notify_script_init(vector_t *, uid_t, gid_t); ++extern bool set_default_script_user(const char *, const char *, bool); ++extern notify_script_t* notify_script_init(vector_t *, const char *, bool); + + #endif +-- +2.9.4 + diff --git a/SOURCES/bz1477572-fix-man-page-vrrp_ipsets.patch b/SOURCES/bz1477572-fix-man-page-vrrp_ipsets.patch new file mode 100644 index 0000000..55ba6c3 --- /dev/null +++ b/SOURCES/bz1477572-fix-man-page-vrrp_ipsets.patch @@ -0,0 +1,29 @@ +From 431ba58daf11771195d0b494b0b0b3655a9b25cd Mon Sep 17 00:00:00 2001 +From: Quentin Armitage +Date: Wed, 2 Aug 2017 16:18:32 +0100 +Subject: [PATCH] Fix keepalived.doc(5) man page + +Ryan O'Hara identified that 'vrrp_ipset' should be 'vrrp_ipsets' +in the man page. + +Signed-off-by: Quentin Armitage +--- + doc/man/man5/keepalived.conf.5 | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/doc/man/man5/keepalived.conf.5 b/doc/man/man5/keepalived.conf.5 +index 5f044af..c7b9d2e 100644 +--- a/doc/man/man5/keepalived.conf.5 ++++ b/doc/man/man5/keepalived.conf.5 +@@ -151,7 +151,7 @@ and + # If no names are specified, ipsets will not be used, otherwise any omitted + # names will be constructed by adding "_if" and/or "6" to previously specified + # names. +- vrrp_ipset [keepalived [keepalived6 [keepalived_if6]]] ++ vrrp_ipsets [keepalived [keepalived6 [keepalived_if6]]] + + # The following enables checking that when in unicast mode, the source + # address of a VRRP packet is one of our unicast peers. +-- +2.9.4 + diff --git a/SOURCES/bz1477587-exclude-mismatch-vips.patch b/SOURCES/bz1477587-exclude-mismatch-vips.patch new file mode 100644 index 0000000..abbb6c2 --- /dev/null +++ b/SOURCES/bz1477587-exclude-mismatch-vips.patch @@ -0,0 +1,38 @@ +From f42914b07166db123b55a4acd31f4a6eb280d0f8 Mon Sep 17 00:00:00 2001 +From: Ryan O'Hara +Date: Mon, 11 Dec 2017 12:36:26 -0600 +Subject: [PATCH] Exclude mixed family VIPs from VRRP advertisements + +In previous releases of keepalived, it was allowed to have both IPv5 +and IPv6 addresses in the 'virtual_ipaddress' block. The address count +in VRRP advertisements would reflect all VIPS, both IPv4 and IPv6, but +the address list would contain empty addresses for any VIP that did +not match the VRRP instance family. In VRRPv3 this is not allowed, and +recent releases will simply ignore any VIP that does not match the +instance family causing those VIPs to not be created in the interface. + +Rather than ignore the VIP of mismatched family, this patch will move +non-matching VIP to the excluded list causing them to not be included +in the VRRP advertisements but still create them when in MASTER state. +--- + keepalived/vrrp/vrrp_parser.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/keepalived/vrrp/vrrp_parser.c b/keepalived/vrrp/vrrp_parser.c +index 7a383153..d2cd720a 100644 +--- a/keepalived/vrrp/vrrp_parser.c ++++ b/keepalived/vrrp/vrrp_parser.c +@@ -641,8 +641,9 @@ vrrp_vip_handler(__attribute__((unused)) vector_t *strvec) + if (vrrp->family == AF_UNSPEC) + vrrp->family = address_family; + else if (address_family != vrrp->family) { +- log_message(LOG_INFO, "(%s): address family must match VRRP instance [%s] - ignoring", vrrp->iname, str); ++ log_message(LOG_WARNING, "(%s): address family does not match VRRP instance [%s]", vrrp->iname, str); + free_list_element(vrrp->vip, vrrp->vip->tail); ++ alloc_vrrp_evip(vec); + } + } + +-- +2.14.2 + diff --git a/SOURCES/bz1508435-load-ip-tables-handling.patch b/SOURCES/bz1508435-load-ip-tables-handling.patch new file mode 100644 index 0000000..27b82a2 --- /dev/null +++ b/SOURCES/bz1508435-load-ip-tables-handling.patch @@ -0,0 +1,1154 @@ +From dd9a0db422716dd9bbc329a6e2f23ee3d55d2008 Mon Sep 17 00:00:00 2001 +From: Quentin Armitage +Date: Mon, 27 Mar 2017 11:26:44 +0100 +Subject: [PATCH 1/3] Handle not being able to load ip_tables or ip6_tables + modules + +When running in a docker container it isn't possible to load kernel +modules, so we need to cleanly handle a failure to load the modules. + +Signed-off-by: Quentin Armitage +--- + keepalived/core/global_data.c | 2 - + keepalived/include/global_data.h | 2 - + keepalived/include/vrrp.h | 5 + + keepalived/include/vrrp_ipset.h | 4 +- + keepalived/include/vrrp_iptables.h | 7 +- + keepalived/include/vrrp_iptables_calls.h | 2 +- + keepalived/vrrp/vrrp.c | 53 ++++++-- + keepalived/vrrp/vrrp_daemon.c | 13 +- + keepalived/vrrp/vrrp_ipaddress.c | 137 ++++++++++++++++--- + keepalived/vrrp/vrrp_ipset.c | 73 +++++++--- + keepalived/vrrp/vrrp_iptables.c | 225 +++++++++++++++++++------------ + keepalived/vrrp/vrrp_iptables_calls.c | 108 +++++++-------- + 12 files changed, 421 insertions(+), 210 deletions(-) + +diff --git a/keepalived/core/global_data.c b/keepalived/core/global_data.c +index d934f286..21c06c1c 100644 +--- a/keepalived/core/global_data.c ++++ b/keepalived/core/global_data.c +@@ -98,8 +98,6 @@ set_vrrp_defaults(data_t * data) + data->vrrp_higher_prio_send_advert = false; + data->vrrp_version = VRRP_VERSION_2; + strcpy(data->vrrp_iptables_inchain, "INPUT"); +- data->block_ipv4 = false; +- data->block_ipv6 = false; + #ifdef _HAVE_LIBIPSET_ + data->using_ipsets = true; + strcpy(data->vrrp_ipset_address, "keepalived"); +diff --git a/keepalived/include/global_data.h b/keepalived/include/global_data.h +index 6547ddac..eea34f75 100644 +--- a/keepalived/include/global_data.h ++++ b/keepalived/include/global_data.h +@@ -99,8 +99,6 @@ typedef struct _data { + int vrrp_version; /* VRRP version (2 or 3) */ + char vrrp_iptables_inchain[XT_EXTENSION_MAXNAMELEN]; + char vrrp_iptables_outchain[XT_EXTENSION_MAXNAMELEN]; +- bool block_ipv4; +- bool block_ipv6; + #ifdef _HAVE_LIBIPSET_ + bool using_ipsets; + char vrrp_ipset_address[IPSET_MAXNAMELEN]; +diff --git a/keepalived/include/vrrp.h b/keepalived/include/vrrp.h +index 6e874689..ab757277 100644 +--- a/keepalived/include/vrrp.h ++++ b/keepalived/include/vrrp.h +@@ -313,6 +313,10 @@ typedef struct _vrrp_t { + + #define VRRP_ISUP(V) (VRRP_IF_ISUP(V) && VRRP_SCRIPT_ISUP(V)) + ++/* Global variables */ ++extern bool block_ipv4; ++extern bool block_ipv6; ++ + /* prototypes */ + extern vrrphdr_t *vrrp_get_header(sa_family_t, char *, unsigned *); + extern int open_vrrp_send_socket(sa_family_t, int, ifindex_t, bool); +@@ -327,6 +331,7 @@ extern void vrrp_state_backup(vrrp_t *, char *, ssize_t); + extern void vrrp_state_goto_master(vrrp_t *); + extern void vrrp_state_leave_master(vrrp_t *); + extern bool vrrp_complete_init(void); ++extern void vrrp_restore_interfaces_startup(void); + extern void restore_vrrp_interfaces(void); + extern void shutdown_vrrp_instances(void); + extern void clear_diff_vrrp(void); +diff --git a/keepalived/include/vrrp_ipset.h b/keepalived/include/vrrp_ipset.h +index 9ee1095a..6cc320ef 100644 +--- a/keepalived/include/vrrp_ipset.h ++++ b/keepalived/include/vrrp_ipset.h +@@ -27,8 +27,8 @@ + #include + #include "vrrp_ipaddress.h" + +-int add_ipsets(bool); +-int remove_ipsets(void); ++bool add_ipsets(bool); ++bool remove_ipsets(void); + bool has_ipset_setname(struct ipset_session*, const char *); + bool ipset_init(void); + struct ipset_session* ipset_session_start(void); +diff --git a/keepalived/include/vrrp_iptables.h b/keepalived/include/vrrp_iptables.h +index 2a7681b3..dbbd6fe7 100644 +--- a/keepalived/include/vrrp_iptables.h ++++ b/keepalived/include/vrrp_iptables.h +@@ -37,12 +37,11 @@ struct ipt_handle; + #define IPTABLES_MAX_TRIES 3 /* How many times to try adding/deleting when get EAGAIN */ + + #ifdef _LIBIPTC_DYNAMIC_ +-extern bool using_libiptc; /* Set if using libiptc - for dynamic linking */ ++extern bool using_libip4tc; /* Set if using lib4iptc - for dynamic linking */ ++extern bool using_libip6tc; /* Set if using lib4iptc - for dynamic linking */ + #endif +-extern bool use_ip4tables; /* Set if using iptables */ +-extern bool use_ip6tables; /* Set if using ip6tables */ + +-bool iptables_init_lib(void); ++void iptables_init_lib(void); + void iptables_fini(void); + void iptables_startup(bool); + void iptables_cleanup(void); +diff --git a/keepalived/include/vrrp_iptables_calls.h b/keepalived/include/vrrp_iptables_calls.h +index 34052fac..ef4e0183 100644 +--- a/keepalived/include/vrrp_iptables_calls.h ++++ b/keepalived/include/vrrp_iptables_calls.h +@@ -34,7 +34,6 @@ + + #ifdef _HAVE_LIBIPTC_ + #ifdef _LIBXTABLES_DYNAMIC_ +-extern bool xtables_load(void); + extern void xtables_unload(void); + #endif + extern bool load_xtables_module(const char *, const char *); +@@ -50,6 +49,7 @@ extern int ip6tables_is_chain(struct ip6tc_handle* handle, const char* chain_nam + extern int ip6tables_process_entry( struct ip6tc_handle* handle, const char* chain_name, unsigned int rulenum, const char* target_name, const ip_address_t* src_ip_address, const ip_address_t* dst_ip_address, const char* in_iface, const char* out_iface, uint16_t protocol, uint8_t type, int cmd, bool force); + extern int ip4tables_add_rules(struct iptc_handle* handle, const char* chain_name, unsigned int rulenum, uint8_t dim, uint8_t src_dst, const char* target_name, const char* set_name, uint16_t protocol, uint8_t param, int cmd, bool ignore_errors); + extern int ip6tables_add_rules(struct ip6tc_handle* handle, const char* chain_name, unsigned int rulenum, uint8_t dim, uint8_t src_dst, const char* target_name, const char* set_name, uint16_t protocol, uint8_t param, int cmd, bool ignore_errors); ++extern void check_chains_exist_lib(void); + #ifdef _LIBIPTC_DYNAMIC_ + extern bool iptables_lib_init(void); + #endif +diff --git a/keepalived/vrrp/vrrp.c b/keepalived/vrrp/vrrp.c +index 3d2bfe41..38895af8 100644 +--- a/keepalived/vrrp/vrrp.c ++++ b/keepalived/vrrp/vrrp.c +@@ -72,6 +72,10 @@ + #include + #include + ++/* Set if need to block ip addresses and are able to do so */ ++bool block_ipv4; ++bool block_ipv6; ++ + /* add/remove Virtual IP addresses */ + static bool + vrrp_handle_ipaddress(vrrp_t * vrrp, int cmd, int type) +@@ -124,7 +128,11 @@ vrrp_handle_accept_mode(vrrp_t *vrrp, int cmd, bool force) + + #ifdef _HAVE_LIBIPTC_ + do { +- h = iptables_open(); ++#ifdef _LIBIPTC_DYNAMIC_ ++ if ((vrrp->family == AF_INET && using_libip4tc) || ++ (vrrp->family == AF_INET6 && using_libip6tc)) ++#endif ++ h = iptables_open(); + #endif + /* As accept is false, add iptable rule to drop packets destinated to VIPs and eVIPs */ + if (!LIST_ISEMPTY(vrrp->vip)) +@@ -132,7 +140,10 @@ vrrp_handle_accept_mode(vrrp_t *vrrp, int cmd, bool force) + if (!LIST_ISEMPTY(vrrp->evip)) + handle_iptable_rule_to_iplist(h, vrrp->evip, cmd, force); + #ifdef _HAVE_LIBIPTC_ +- res = iptables_close(h); ++#ifdef _LIBIPTC_DYNAMIC_ ++ if (h) ++#endif ++ res = iptables_close(h); + } while (res == EAGAIN && ++tries < IPTABLES_MAX_TRIES); + #endif + vrrp->iptable_rules_set = (cmd == IPADDRESS_ADD); +@@ -2363,10 +2374,12 @@ vrrp_complete_instance(vrrp_t * vrrp) + if (vrrp->base_priority != VRRP_PRIO_OWNER && !vrrp->accept) { + //TODO = we have a problem since SNMP may change accept mode + //it can also change priority +- if (vrrp->saddr.ss_family == AF_INET) +- global_data->block_ipv4 = true; ++ if (!global_data->vrrp_iptables_inchain[0]) ++ log_message(LOG_INFO, "(%s): Unable to set no_accept mode since iptables chain name unset", vrrp->iname); ++ else if (vrrp->family == AF_INET) ++ block_ipv4 = true; + else +- global_data->block_ipv6 = true; ++ block_ipv6 = true; + } + if (!LIST_ISEMPTY(vrrp->vip)) { + for (e = LIST_HEAD(vrrp->vip); e; ELEMENT_NEXT(e)) { +@@ -2390,7 +2403,6 @@ vrrp_complete_instance(vrrp_t * vrrp) + if (!reload && interface_already_existed) { + // TODO - consider reload + vrrp->vipset = true; /* Set to force address removal */ +- vrrp_restore_interface(vrrp, false, true); + } + + /* See if we need to set promote_secondaries */ +@@ -2452,12 +2464,6 @@ vrrp_complete_init(void) + if (global_data->vrrp_garp_interval || global_data->vrrp_gna_interval) + set_default_garp_delay(); + +-#ifdef _HAVE_LIBIPTC_ +- /* Make sure we don't have any old iptables/ipsets settings left around */ +- if (!reload) +- iptables_cleanup(); +-#endif +- + /* Mark any scripts as insecure */ + check_vrrp_script_security(); + +@@ -2623,6 +2629,18 @@ vrrp_complete_init(void) + return true; + } + ++void vrrp_restore_interfaces_startup(void) ++{ ++ element e; ++ vrrp_t *vrrp; ++ ++ for (e = LIST_HEAD(vrrp_data->vrrp); e; ELEMENT_NEXT(e)) { ++ vrrp = ELEMENT_DATA(e); ++ if (vrrp->vipset) ++ vrrp_restore_interface(vrrp, false, true); ++ } ++} ++ + /* Try to find a VRRP instance */ + static vrrp_t * + vrrp_exist(vrrp_t *old_vrrp) +@@ -2690,12 +2708,19 @@ clear_diff_vrrp_vip(vrrp_t *old_vrrp, vrrp_t *vrrp) + + #ifdef _HAVE_LIBIPTC_ + do { +- h = iptables_open(); ++#ifdef _LIBIPTC_DYNAMIC_ ++ if ((vrrp->family == AF_INET && using_libip4tc) || ++ (vrrp->family == AF_INET6 && using_libip6tc)) ++#endif ++ h = iptables_open(); + #endif + clear_diff_vrrp_vip_list(vrrp, h, old_vrrp->vip, vrrp->vip); + clear_diff_vrrp_vip_list(vrrp, h, old_vrrp->evip, vrrp->evip); + #ifdef _HAVE_LIBIPTC_ +- res = iptables_close(h); ++#ifdef _LIBIPTC_DYNAMIC_ ++ if (h) ++#endif ++ res = iptables_close(h); + } while (res == EAGAIN && ++tries < IPTABLES_MAX_TRIES); + #endif + } +diff --git a/keepalived/vrrp/vrrp_daemon.c b/keepalived/vrrp/vrrp_daemon.c +index 1c2f9e89..1e9282a7 100644 +--- a/keepalived/vrrp/vrrp_daemon.c ++++ b/keepalived/vrrp/vrrp_daemon.c +@@ -202,8 +202,6 @@ start_vrrp(void) + if (global_data->vrrp_no_swap) + set_process_dont_swap(4096); /* guess a stack size to reserve */ + +- iptables_init(); +- + #ifdef _WITH_SNMP_ + if (!reload && (global_data->enable_snmp_keepalived || global_data->enable_snmp_rfcv2 || global_data->enable_snmp_rfcv3)) { + vrrp_snmp_agent_init(global_data->snmp_socket); +@@ -266,10 +264,21 @@ start_vrrp(void) + return; + } + ++ /* We need to delay the init of iptables to after vrrp_complete_init() ++ * has been called so we know whether we want IPv4 and/or IPv6 */ ++ iptables_init(); ++ ++ /* Make sure we don't have any old iptables/ipsets settings left around */ + #ifdef _HAVE_LIBIPTC_ ++ if (!reload) ++ iptables_cleanup(); ++ + iptables_startup(reload); + #endif + ++ if (!reload) ++ vrrp_restore_interfaces_startup(); ++ + /* clear_diff_vrrp must be called after vrrp_complete_init, since the latter + * sets ifa_index on the addresses, which is used for the address comparison */ + if (reload) +diff --git a/keepalived/vrrp/vrrp_ipaddress.c b/keepalived/vrrp/vrrp_ipaddress.c +index eb332e72..c72ea736 100644 +--- a/keepalived/vrrp/vrrp_ipaddress.c ++++ b/keepalived/vrrp/vrrp_ipaddress.c +@@ -27,6 +27,7 @@ + #ifdef _HAVE_LIBIPTC_ + #include "vrrp_iptables.h" + #endif ++#include "vrrp.h" + #include "keepalived_netlink.h" + #include "vrrp_data.h" + #include "logger.h" +@@ -38,11 +39,16 @@ + #if !defined _HAVE_LIBIPTC_ || defined _LIBIPTC_DYNAMIC_ + #include "utils.h" + #endif ++#ifdef _LIBIPTC_DYNAMIC_ ++#include "vrrp_iptables.h" ++#endif + + #define INFINITY_LIFE_TIME 0xFFFFFFFF + +-bool iptables_cmd_available = true; +-bool ip6tables_cmd_available = true; ++#if !defined _HAVE_LIBIPTC_ || defined _LIBIPTC_DYNAMIC_ ++static bool iptables_cmd_available; ++static bool ip6tables_cmd_available; ++#endif + + char * + ipaddresstos(char *buf, ip_address_t *ipaddress) +@@ -250,9 +256,6 @@ handle_iptable_rule_to_vip_cmd(ip_address_t *ipaddress, int cmd, bool force) + char *addr_str; + char *ifname = NULL; + +- if (global_data->vrrp_iptables_inchain[0] == '\0') +- return; +- + if (IP_IS6(ipaddress)) { + if (!ip6tables_cmd_available) + return; +@@ -316,9 +319,18 @@ handle_iptable_rule_to_vip(ip_address_t *ipaddr, int cmd, + #endif + bool force) + { ++ if (IP_IS6(ipaddr)) { ++ if (!block_ipv6) ++ return; ++ } else { ++ if (!block_ipv4) ++ return; ++ } ++ + #ifdef _HAVE_LIBIPTC_ + #ifdef _LIBIPTC_DYNAMIC_ +- if (using_libiptc) ++ if ((IP_IS6(ipaddr) && using_libip6tc) || ++ (!IP_IS6(ipaddr) && using_libip4tc)) + #endif + { + handle_iptable_rule_to_vip_lib(ipaddr, cmd, h, force); +@@ -612,31 +624,122 @@ clear_diff_saddresses(void) + clear_diff_address(NULL, old_vrrp_data->static_addresses, vrrp_data->static_addresses); + } + ++static void ++check_chains_exist(void) ++{ ++#ifdef _HAVE_LIBIPTC_ ++#ifdef _LIBIPTC_DYNAMIC_ ++ if (using_libip4tc || using_libip6tc) ++#endif ++ check_chains_exist_lib(); ++#endif ++ ++#if !defined _HAVE_LIBIPTC_ || defined _LIBIPTC_DYNAMIC_ ++ char *argv[4]; ++ ++ argv[1] = "-nL"; ++ argv[2] = global_data->vrrp_iptables_inchain; ++ argv[3] = NULL; ++ ++ if (block_ipv4) ++ { ++#ifdef _LIBIPTC_DYNAMIC_ ++ if (!using_libip4tc) ++#endif ++ { ++ argv[0] = "iptables"; ++ ++ if (fork_exec(argv) < 0) { ++ log_message(LOG_INFO, "iptables chain %s does not exist", global_data->vrrp_iptables_inchain); ++ block_ipv4 = false; ++ } ++ else if (global_data->vrrp_iptables_outchain[0]) { ++ argv[2] = global_data->vrrp_iptables_outchain; ++ if (fork_exec(argv) < 0) { ++ log_message(LOG_INFO, "iptables chain %s does not exist", global_data->vrrp_iptables_outchain); ++ block_ipv4 = false; ++ } ++ } ++ } ++ } ++ ++ if (block_ipv6) ++ { ++#ifdef _LIBIPTC_DYNAMIC_ ++ if (!using_libip6tc) ++#endif ++ { ++ argv[0] = "ip6tables"; ++ argv[2] = global_data->vrrp_iptables_inchain; ++ ++ if (fork_exec(argv) < 0) { ++ log_message(LOG_INFO, "ip6tables chain %s does not exist", global_data->vrrp_iptables_inchain); ++ block_ipv6 = false; ++ } ++ else if (global_data->vrrp_iptables_outchain[0]) { ++ argv[2] = global_data->vrrp_iptables_outchain; ++ if (fork_exec(argv) < 0) { ++ log_message(LOG_INFO, "ip6tables chain %s does not exist", global_data->vrrp_iptables_outchain); ++ block_ipv6 = false; ++ } ++ } ++ } ++ } ++#endif ++} ++ + void + iptables_init(void) + { +-#ifdef _HAVE_LIBIPTC_ +- if (iptables_init_lib()) ++ if (!block_ipv4 && !block_ipv6) { ++#ifdef _HAVE_LIBIPSET_ ++ global_data->using_ipsets = false; ++#endif + return; ++ } ++ ++#ifdef _HAVE_LIBIPTC_ ++ iptables_init_lib(); + #endif + + #if !defined _HAVE_LIBIPTC_ || defined _LIBIPTC_DYNAMIC_ + char *argv[3]; + +- /* We can't use libiptc, so check iptables command available */ +- argv[0] = "iptables"; ++ /* If can't use libiptc, check iptables command available */ + argv[1] = "-V"; + argv[2] = NULL; + +- if (fork_exec(argv) < 0) { +- log_message(LOG_INFO, "iptables command not available - can't filter IPv4 VIP address destinations"); +- iptables_cmd_available = false; ++ if (block_ipv4 ++#ifdef _LIBIPTC_DYNAMIC_ ++ && !using_libip4tc ++#endif ++ ) ++ { ++ argv[0] = "iptables"; ++ if (!(iptables_cmd_available = (fork_exec(argv) >= 0))) { ++ log_message(LOG_INFO, "iptables command not available - can't filter IPv4 VIP address destinations"); ++ block_ipv4 = false; ++ } + } + +- argv[0] = "ip6tables"; +- if (fork_exec(argv) < 0) { +- log_message(LOG_INFO, "ip6tables command not available - can't filter IPv6 VIP address destinations"); +- ip6tables_cmd_available = false; ++ if (block_ipv6 ++#ifdef _LIBIPTC_DYNAMIC_ ++ && !using_libip6tc ++#endif ++ ) ++ { ++ argv[0] = "ip6tables"; ++ if (!(ip6tables_cmd_available = (fork_exec(argv) >= 0))) { ++ log_message(LOG_INFO, "ip6tables command not available - can't filter IPv6 VIP address destinations"); ++ block_ipv6 = false; ++ } + } + #endif ++ ++ if (block_ipv4 || block_ipv6) ++ check_chains_exist(); ++#ifdef _HAVE_LIBIPSET_ ++ else ++ global_data->using_ipsets = false; ++#endif + } +diff --git a/keepalived/vrrp/vrrp_ipset.c b/keepalived/vrrp/vrrp_ipset.c +index 194809f0..1c3e4990 100644 +--- a/keepalived/vrrp/vrrp_ipset.c ++++ b/keepalived/vrrp/vrrp_ipset.c +@@ -38,12 +38,14 @@ + #include /* For __beXX types in userland */ + #include /* For nf_inet_addr */ + #include ++#include + + #include "logger.h" + #include "global_data.h" + #include "vrrp_iptables.h" + #include "vrrp_ipset.h" + #include "vrrp_ipaddress.h" ++#include "vrrp.h" + #include "main.h" + + #ifdef _LIBIPSET_DYNAMIC_ +@@ -146,7 +148,7 @@ has_ipset_setname(struct ipset_session* session, const char *setname) + return ipset_cmd1(session, IPSET_CMD_HEADER, 0) == 0; + } + +-static int create_sets(const char* addr4, const char* addr6, const char* addr_if6, bool reload) ++static bool create_sets(const char* addr4, const char* addr6, const char* addr_if6, bool reload) + { + struct ipset_session *session; + +@@ -161,12 +163,12 @@ static int create_sets(const char* addr4, const char* addr6, const char* addr_if + if (!reload) + ipset_envopt_parse(session, IPSET_ENV_EXIST, NULL); + +- if (use_ip4tables) { ++ if (block_ipv4) { + if (!reload || !has_ipset_setname(session, addr4)) + ipset_create(session, addr4, "hash:ip", NFPROTO_IPV4); + } + +- if (use_ip6tables) { ++ if (block_ipv6) { + if (!reload || !has_ipset_setname(session, addr6)) + ipset_create(session, addr6, "hash:ip", NFPROTO_IPV6); + if (!reload || !has_ipset_setname(session, addr_if6)) { +@@ -184,6 +186,30 @@ static int create_sets(const char* addr4, const char* addr6, const char* addr_if + return true; + } + ++static ++bool set_match_loaded(void) ++{ ++ char buf[XT_FUNCTION_MAXNAMELEN+1]; ++ FILE *fp; ++ bool found = false; ++ ++ fp = fopen( "/proc/net/ip_tables_matches", "r"); ++ if (!fp) ++ return false; ++ ++ while (fgets(buf, sizeof(buf), fp)) { ++ if ((buf[3] == '\0' || buf[3] == '\n') && ++ !strncmp(buf, "set", 3)) { ++ found = true; ++ break; ++ } ++ } ++ ++ fclose(fp); ++ ++ return found; ++} ++ + bool ipset_init(void) + { + #ifdef _LIBIPSET_DYNAMIC_ +@@ -213,44 +239,47 @@ bool ipset_init(void) + return false; + } + +- int err = false; +- if (!(ipset_session_init_addr = dlsym(libipset_handle, "ipset_session_init"))) {log_message(LOG_INFO, "Failed to dynamic link ipset_session_init"); err = true;} +- if (!(ipset_session_fini_addr = dlsym(libipset_handle, "ipset_session_fini"))) {log_message(LOG_INFO, "Failed to dynamic link ipset_session_fini"); err = true;} +- if (!(ipset_session_data_addr = dlsym(libipset_handle,"ipset_session_data"))) {log_message(LOG_INFO, "Failed to dynamic link ipset_session_data"); err = true;} +- if (!(ipset_session_error_addr = dlsym(libipset_handle,"ipset_session_error"))) {log_message(LOG_INFO, "Failed to dynamic link ipset_session_error"); err = true;} +- if (!(ipset_envopt_parse_addr = dlsym(libipset_handle,"ipset_envopt_parse"))) {log_message(LOG_INFO, "Failed to dynamic link ipset_envopt_parse"); err = true;} +- if (!(ipset_type_get_addr = dlsym(libipset_handle,"ipset_type_get"))) {log_message(LOG_INFO, "Failed to dynamic link ipset_type_get"); err = true;} +- if (!(ipset_data_set_addr = dlsym(libipset_handle,"ipset_data_set"))) {log_message(LOG_INFO, "Failed to dynamic link ipset_data_set"); err = true;} +- if (!(ipset_cmd_addr = dlsym(libipset_handle,"ipset_cmd"))) {log_message(LOG_INFO, "Failed to dynamic link ipset_cmd"); err = true;} +- if (!(ipset_load_types_addr = dlsym(libipset_handle,"ipset_load_types"))) {log_message(LOG_INFO, "Failed to dynamic link ipset_load_types"); err = true;} +- if (err) { +- log_message(LOG_INFO, "Failed to dynamic link an ipset function"); ++ if (!(ipset_session_init_addr = dlsym(libipset_handle, "ipset_session_init")) || ++ !(ipset_session_fini_addr = dlsym(libipset_handle, "ipset_session_fini")) || ++ !(ipset_session_data_addr = dlsym(libipset_handle,"ipset_session_data")) || ++ !(ipset_session_error_addr = dlsym(libipset_handle,"ipset_session_error")) || ++ !(ipset_envopt_parse_addr = dlsym(libipset_handle,"ipset_envopt_parse")) || ++ !(ipset_type_get_addr = dlsym(libipset_handle,"ipset_type_get")) || ++ !(ipset_data_set_addr = dlsym(libipset_handle,"ipset_data_set")) || ++ !(ipset_cmd_addr = dlsym(libipset_handle,"ipset_cmd")) || ++ !(ipset_load_types_addr = dlsym(libipset_handle,"ipset_load_types"))) { ++ log_message(LOG_INFO, "Failed to dynamic link an ipset function - %s", dlerror()); + return false; + } + #endif + + ipset_load_types(); + +- if (!load_xtables_module("xt_set", "ipsets")) ++ if (!set_match_loaded() && !load_xtables_module("xt_set", "ipsets")) { ++ log_message(LOG_INFO, "Unable to load module xt_set - not using ipsets"); + return false; ++ } + + return true; + } + +-int remove_ipsets(void) ++bool remove_ipsets(void) + { + struct ipset_session *session; + ++ if (!global_data->using_ipsets) ++ return true; ++ + session = ipset_session_init(printf); + if (!session) { + log_message(LOG_INFO, "Cannot initialize ipset session."); + return false; + } + +- if (use_ip4tables) ++ if (block_ipv4) + ipset_destroy(session, global_data->vrrp_ipset_address); + +- if (use_ip6tables) { ++ if (block_ipv6) { + ipset_destroy(session, global_data->vrrp_ipset_address6); + ipset_destroy(session, global_data->vrrp_ipset_address_iface6); + } +@@ -260,7 +289,7 @@ int remove_ipsets(void) + return true; + } + +-int add_ipsets(bool reload) ++bool add_ipsets(bool reload) + { + return create_sets(global_data->vrrp_ipset_address, global_data->vrrp_ipset_address6, global_data->vrrp_ipset_address_iface6, reload); + } +@@ -281,12 +310,12 @@ void ipset_entry(struct ipset_session* session, int cmd, const ip_address_t* add + char *iface = NULL; + + if (addr->ifa.ifa_family == AF_INET) { +- if (!use_ip4tables) ++ if (!block_ipv4) + return; + set = global_data->vrrp_ipset_address; + } + else if (IN6_IS_ADDR_LINKLOCAL(&addr->u.sin6_addr)) { +- if (!use_ip6tables) ++ if (!block_ipv6) + return; + + set = global_data->vrrp_ipset_address_iface6; +diff --git a/keepalived/vrrp/vrrp_iptables.c b/keepalived/vrrp/vrrp_iptables.c +index 2c218cac..60ff6c8c 100644 +--- a/keepalived/vrrp/vrrp_iptables.c ++++ b/keepalived/vrrp/vrrp_iptables.c +@@ -59,6 +59,10 @@ + #include + #include + #endif ++#include ++#include ++#include ++#include + + #include "vrrp_iptables.h" + #include "vrrp_iptables_calls.h" +@@ -81,10 +85,9 @@ struct ipt_handle { + + /* If the chains don't exist, or modules not loaded, we can't use iptables/ip6tables */ + #ifdef _LIBIPTC_DYNAMIC_ +-bool using_libiptc = true; ++bool using_libip4tc = false; ++bool using_libip6tc = false; + #endif +-bool use_ip4tables = true; +-bool use_ip6tables = true; + + #ifdef _HAVE_LIBIPSET_ + static +@@ -105,10 +108,13 @@ void add_del_rules(int cmd, bool ignore_errors) + struct iptc_handle *h4; + struct ip6tc_handle *h6; + +- if (use_ip4tables && +- global_data->block_ipv4 && ++ if (block_ipv4 && + (global_data->vrrp_iptables_inchain[0] || +- global_data->vrrp_iptables_outchain[0])) { ++ global_data->vrrp_iptables_outchain[0]) ++#ifdef _LIBIPTC_DYNAMIC_ ++ && using_libip4tc ++#endif ++ ) { + if ((h4 = ip4tables_open("filter"))) { + if (global_data->vrrp_iptables_inchain[0]) + ip4tables_add_rules(h4, global_data->vrrp_iptables_inchain, APPEND_RULE, IPSET_DIM_ONE, 0, XTC_LABEL_DROP, global_data->vrrp_ipset_address, IPPROTO_NONE, 0, cmd, ignore_errors); +@@ -118,10 +124,13 @@ void add_del_rules(int cmd, bool ignore_errors) + } + } + +- if (use_ip6tables && +- global_data->block_ipv6 && ++ if (block_ipv6 && + (global_data->vrrp_iptables_inchain[0] || +- global_data->vrrp_iptables_outchain[0])) { ++ global_data->vrrp_iptables_outchain[0]) ++#ifdef _LIBIPTC_DYNAMIC_ ++ && using_libip6tc ++#endif ++ ) { + if ((h6 = ip6tables_open("filter"))) { + if (global_data->vrrp_iptables_inchain[0]) { + #ifdef HAVE_IPSET_ATTR_IFACE +@@ -185,58 +194,64 @@ int iptables_close(struct ipt_handle* h) + return res; + } + +-static void check_chains_exist(void) ++void check_chains_exist_lib(void) + { + struct iptc_handle *h4; + struct ip6tc_handle *h6; + +- if (global_data->block_ipv4) { +- h4 = ip4tables_open("filter"); +- +- if (!h4) { +- log_message(LOG_INFO, "WARNING, ip_tables module not installed - can't filter IPv4 addresses"); +- use_ip4tables = false; +- } else { +- if (global_data->vrrp_iptables_inchain[0] && +- !ip4tables_is_chain(h4, global_data->vrrp_iptables_inchain)) { +- log_message(LOG_INFO, "iptables chain %s doesn't exist", global_data->vrrp_iptables_inchain); +- use_ip4tables = false; +- } +- if (global_data->vrrp_iptables_outchain[0] && +- !ip4tables_is_chain(h4, global_data->vrrp_iptables_outchain)) { +- log_message(LOG_INFO, "iptables chain %s doesn't exist", global_data->vrrp_iptables_outchain); +- use_ip4tables = false; ++ if (block_ipv4) { ++#ifdef _LIBIPTC_DYNAMIC_ ++ if (using_libip4tc) ++#endif ++ { ++ h4 = ip4tables_open("filter"); ++ ++ if (!h4) { ++ log_message(LOG_INFO, "WARNING, ip_tables module not installed - can't filter IPv4 addresses"); ++ block_ipv4 = false; ++ } else { ++ if (global_data->vrrp_iptables_inchain[0] && ++ !ip4tables_is_chain(h4, global_data->vrrp_iptables_inchain)) { ++ log_message(LOG_INFO, "iptables chain %s doesn't exist", global_data->vrrp_iptables_inchain); ++ block_ipv4 = false; ++ } ++ if (global_data->vrrp_iptables_outchain[0] && ++ !ip4tables_is_chain(h4, global_data->vrrp_iptables_outchain)) { ++ log_message(LOG_INFO, "iptables chain %s doesn't exist", global_data->vrrp_iptables_outchain); ++ block_ipv4 = false; ++ } ++ ++ ip4tables_close(h4, false); + } +- +- ip4tables_close(h4, false); + } + } +- else +- use_ip4tables = false; +- +- if (global_data->block_ipv6) { +- h6 = ip6tables_open("filter"); +- +- if (!h6) { +- log_message(LOG_INFO, "WARNING, ip6_tables module not installed - can't filter IPv6 addresses"); +- use_ip6tables = false; +- } else { +- if (global_data->vrrp_iptables_inchain[0] && +- !ip6tables_is_chain(h6, global_data->vrrp_iptables_inchain)) { +- log_message(LOG_INFO, "ip6tables chain %s doesn't exist", global_data->vrrp_iptables_inchain); +- use_ip6tables = false; +- } +- if (global_data->vrrp_iptables_outchain[0] && +- !ip6tables_is_chain(h6, global_data->vrrp_iptables_outchain)) { +- log_message(LOG_INFO, "ip6tables chain %s doesn't exist", global_data->vrrp_iptables_outchain); +- use_ip6tables = false; +- } + +- ip6tables_close(h6, false); ++ if (block_ipv6) { ++#ifdef _LIBIPTC_DYNAMIC_ ++ if (using_libip6tc) ++#endif ++ { ++ h6 = ip6tables_open("filter"); ++ ++ if (!h6) { ++ log_message(LOG_INFO, "WARNING, ip6_tables module not installed - can't filter IPv6 addresses"); ++ block_ipv6 = false; ++ } else { ++ if (global_data->vrrp_iptables_inchain[0] && ++ !ip6tables_is_chain(h6, global_data->vrrp_iptables_inchain)) { ++ log_message(LOG_INFO, "ip6tables chain %s doesn't exist", global_data->vrrp_iptables_inchain); ++ block_ipv6 = false; ++ } ++ if (global_data->vrrp_iptables_outchain[0] && ++ !ip6tables_is_chain(h6, global_data->vrrp_iptables_outchain)) { ++ log_message(LOG_INFO, "ip6tables chain %s doesn't exist", global_data->vrrp_iptables_outchain); ++ block_ipv6 = false; ++ } ++ ++ ip6tables_close(h6, false); ++ } + } + } +- else +- use_ip6tables = false; + } + + static int iptables_entry(struct ipt_handle* h, const char* chain_name, unsigned int rulenum, char* target_name, const ip_address_t* src_ip_address, const ip_address_t* dst_ip_address, const char* in_iface, const char* out_iface, uint16_t protocol, uint8_t type, int cmd, bool force) +@@ -303,11 +318,8 @@ handle_iptable_rule_to_vip_lib(ip_address_t *ipaddress, int cmd, struct ipt_hand + char *ifname = NULL; + + /* If iptables for the address family isn't in use, skip */ +- if ((ipaddress->ifa.ifa_family == AF_INET && !use_ip4tables) || +- (ipaddress->ifa.ifa_family == AF_INET6 && !use_ip6tables)) +- return; +- +- if (global_data->vrrp_iptables_inchain[0] == '\0') ++ if ((ipaddress->ifa.ifa_family == AF_INET && !block_ipv4) || ++ (ipaddress->ifa.ifa_family == AF_INET6 && !block_ipv6)) + return; + + #ifdef _HAVE_LIBIPSET_ +@@ -343,33 +355,28 @@ handle_iptable_rule_to_vip_lib(ip_address_t *ipaddress, int cmd, struct ipt_hand + IPPROTO_NONE, 0, cmd, force); + } + ++#ifdef _HAVE_LIBIPSET_ + static void +-iptables_remove_structure( +-#ifndef _HAVE_LIBIPSET_ +- __attribute__((unused)) +-#endif +- bool ignore_errors) ++iptables_remove_structure(bool ignore_errors) + { +-#ifdef _HAVE_LIBIPSET_ + if (global_data->using_ipsets) { + add_del_rules(IPADDRESS_DEL, ignore_errors); + add_del_sets(IPADDRESS_DEL, false); + } +-#endif + } ++#endif + + void +-iptables_startup(bool reload) ++iptables_startup( ++#ifndef _HAVE_LIBIPSET_ ++ __attribute__((unused)) ++#endif ++ bool reload) + { +- if (!reload) { +- check_chains_exist(); + #ifdef _HAVE_LIBIPSET_ +- if (!use_ip4tables && !use_ip6tables) +- global_data->using_ipsets = false; +-#endif +- } ++ if (!block_ipv4 && !block_ipv6) ++ global_data->using_ipsets = false; + +-#ifdef _HAVE_LIBIPSET_ + if (global_data->using_ipsets) { + add_del_sets(IPADDRESS_ADD, reload); + add_del_rules(IPADDRESS_ADD, false); +@@ -380,50 +387,94 @@ iptables_startup(bool reload) + void + iptables_cleanup(void) + { ++#ifdef _HAVE_LIBIPSET_ + iptables_remove_structure(true); ++#endif ++} ++ ++/* return true if a given file exists within procfs */ ++/* Taken from iptables code */ ++static ++bool proc_file_exists(const char *filename) ++{ ++ struct stat s; ++ struct statfs f; ++ ++ if (lstat(filename, &s)) ++ return false; ++ if (!S_ISREG(s.st_mode)) ++ return false; ++ if (statfs(filename, &f)) ++ return false; ++ if (f.f_type != PROC_SUPER_MAGIC) ++ return false; ++ ++ return true; + } + +-bool ++void + iptables_init_lib(void) + { +-#ifdef _LIBXTABLES_DYNAMIC_ +- xtables_load(); ++ if (block_ipv4) { ++ if (!proc_file_exists("/proc/net/ip_tables_names") && ++ !load_xtables_module("ip_tables", "iptables")) ++#ifdef _LIBIPTC_DYNAMIC_ ++ using_libip4tc = false; ++ else ++ using_libip4tc = true; ++#else ++ { ++ block_ipv4 = false; ++ log_message(LOG_INFO, "Unable to load module ip_tables"); ++ } + #endif ++ } + ++ if (block_ipv6) { ++ if (!proc_file_exists("/proc/net/ip6_tables_names") && ++ !load_xtables_module("ip6_tables", "ip6tables")) + #ifdef _LIBIPTC_DYNAMIC_ +- if (!iptables_lib_init()) { +- using_libiptc = false; +-#ifdef _LIBXTABLES_DYNAMIC_ +- xtables_unload(); ++ using_libip6tc = false; ++ else ++ using_libip6tc = true; ++#else ++ { ++ block_ipv6 = false; ++ log_message(LOG_INFO, "Unable to load module ip_tables"); ++ } + #endif +- return false; + } +-#endif + +- if (!load_xtables_module("ip_tables", "iptables")) { + #ifdef _LIBIPTC_DYNAMIC_ +- using_libiptc = false; +-#endif ++ if ((!block_ipv4 && !block_ipv6) || ++ (!using_libip4tc && !using_libip6tc) || ++ !iptables_lib_init()) { + #ifdef _LIBXTABLES_DYNAMIC_ + xtables_unload(); + #endif +- return false; ++ ++#ifdef _HAVE_LIBIPSET_ ++ global_data->using_ipsets = false; ++#endif ++ ++ return; + } ++#endif + + #ifdef _HAVE_LIBIPSET_ +- if (!ipset_init()) ++ if (global_data->using_ipsets && !ipset_init()) + global_data->using_ipsets = false; + #endif + + #ifdef _LIBXTABLES_DYNAMIC_ + xtables_unload(); + #endif +- +- return true; + } + + void + iptables_fini(void) + { ++#ifdef _HAVE_LIBIPSET_ + iptables_remove_structure(false); ++#endif + } +diff --git a/keepalived/vrrp/vrrp_iptables_calls.c b/keepalived/vrrp/vrrp_iptables_calls.c +index fb5c98df..ff6b7e6e 100644 +--- a/keepalived/vrrp/vrrp_iptables_calls.c ++++ b/keepalived/vrrp/vrrp_iptables_calls.c +@@ -37,22 +37,18 @@ + #include + #include + #include +-#ifdef _HAVE_LIBIPSET_ + #include + #if defined XT_SET_H_ADD_IP_SET_H_GUARD + #define _IP_SET_H + #elif defined XT_SET_H_ADD_UAPI_IP_SET_H_GUARD + #define _UAPI_IP_SET_H + #endif ++#ifdef _HAVE_LIBIPSET_ + #include + #endif + #include + #include + #include +-#ifdef _LIBXTABLES_DYNAMIC_ +-#include +-#include +-#endif + + #include "vrrp_iptables_calls.h" + #include "memory.h" +@@ -63,6 +59,7 @@ + #ifdef _LIBIPTC_DYNAMIC_ + #include "global_data.h" + #endif ++#include "vrrp_iptables.h" + + /* We sometimes get a resource_busy on iptc_commit. This appears to happen + * when someone else is also updating it. +@@ -406,6 +403,7 @@ int ip6tables_process_entry( struct ip6tc_handle* handle, const char* chain_name + + #ifdef _HAVE_LIBIPTC_ + #ifdef _LIBXTABLES_DYNAMIC_ ++static + bool xtables_load(void) + { + if (libxtables_handle) +@@ -418,7 +416,7 @@ bool xtables_load(void) + } + + if (!(xtables_insmod_addr = dlsym(libxtables_handle, "xtables_insmod"))) { +- log_message(LOG_INFO, "Failed to dynamic link xtables_insmod"); ++ log_message(LOG_INFO, "Failed to dynamic link xtables_insmod - %s", dlerror()); + dlclose(libxtables_handle); + libxtables_handle = NULL; + +@@ -438,25 +436,18 @@ void xtables_unload(void) + } + #endif + +-bool load_xtables_module(const char *module, ++bool ++load_xtables_module(const char *module, + #ifndef _LIBXTABLES_DYNAMIC_ +- __attribute__((unused)) ++ __attribute__((unused)) + #endif +- const char *function) ++ const char *function) + { + struct sigaction act, old_act; + bool res = true; +-#ifdef _LIBXTABLES_DYNAMIC_ +- struct stat stat_buf; +- char module_path[32] = "/sys/module/"; +- +- if (!libxtables_handle) { +- /* See if the module is loaded anyway */ +- strcat(module_path, module); +- if (!stat(module_path, &stat_buf) && +- (stat_buf.st_mode & S_IFDIR)) +- return true; + ++#ifdef _LIBXTABLES_DYNAMIC_ ++ if (!libxtables_handle && !xtables_load()) { + log_message(LOG_INFO, "Module %s cannot be loaded; not using %s", module, function); + return false; + } +@@ -882,47 +873,50 @@ int ip6tables_add_rules(struct ip6tc_handle* handle, const char* chain_name, uns + #ifdef _LIBIPTC_DYNAMIC_ + bool iptables_lib_init(void) + { +- if (libip4tc_handle) +- return true; +- +- /* Attempt to open the ip4tc library */ +- if (!(libip4tc_handle = dlopen("libip4tc.so", RTLD_NOW)) && +- !(libip4tc_handle = dlopen(IP4TC_LIB_NAME, RTLD_NOW))) { +- log_message(LOG_INFO, "Unable to load ip4tc library - %s", dlerror()); +- return false; +- } +- +- if (!(iptc_init_addr = dlsym(libip4tc_handle, "iptc_init")) || +- !(iptc_free_addr = dlsym(libip4tc_handle, "iptc_free")) || +- !(iptc_is_chain_addr = dlsym(libip4tc_handle,"iptc_is_chain")) || +- !(iptc_insert_entry_addr = dlsym(libip4tc_handle,"iptc_insert_entry")) || +- !(iptc_append_entry_addr = dlsym(libip4tc_handle,"iptc_append_entry")) || +- !(iptc_delete_entry_addr = dlsym(libip4tc_handle,"iptc_delete_entry")) || +- !(iptc_commit_addr = dlsym(libip4tc_handle,"iptc_commit")) || +- !(iptc_strerror_addr = dlsym(libip4tc_handle,"iptc_strerror"))) +- log_message(LOG_INFO, "Failed to dynamic link an iptc function"); +- +- /* Attempt to open the ip6tc library */ +- if (!(libip6tc_handle = dlopen("libip6tc.so", RTLD_NOW)) && +- !(libip6tc_handle = dlopen(IP6TC_LIB_NAME, RTLD_NOW))) { +- log_message(LOG_INFO, "Unable to load ip6tc library - %s", dlerror()); +- +- if (global_data->block_ipv4) ++ if (!libip4tc_handle && block_ipv4) { ++ /* Attempt to open the ip4tc library */ ++ if (!(libip4tc_handle = dlopen("libip4tc.so", RTLD_NOW)) && ++ !(libip4tc_handle = dlopen(IP4TC_LIB_NAME, RTLD_NOW))) { ++ log_message(LOG_INFO, "Unable to load ip4tc library - %s", dlerror()); ++ using_libip4tc = false; ++ } ++ else if (!(iptc_init_addr = dlsym(libip4tc_handle, "iptc_init")) || ++ !(iptc_free_addr = dlsym(libip4tc_handle, "iptc_free")) || ++ !(iptc_is_chain_addr = dlsym(libip4tc_handle,"iptc_is_chain")) || ++ !(iptc_insert_entry_addr = dlsym(libip4tc_handle,"iptc_insert_entry")) || ++ !(iptc_append_entry_addr = dlsym(libip4tc_handle,"iptc_append_entry")) || ++ !(iptc_delete_entry_addr = dlsym(libip4tc_handle,"iptc_delete_entry")) || ++ !(iptc_commit_addr = dlsym(libip4tc_handle,"iptc_commit")) || ++ !(iptc_strerror_addr = dlsym(libip4tc_handle,"iptc_strerror"))) { ++ log_message(LOG_INFO, "Failed to dynamic link an iptc function - %s", dlerror()); ++ using_libip4tc = false; + dlclose(libip4tc_handle); +- +- return false; ++ libip4tc_handle = NULL; ++ } + } + +- if (!(ip6tc_init_addr = dlsym(libip6tc_handle, "ip6tc_init")) || +- !(ip6tc_free_addr = dlsym(libip6tc_handle, "ip6tc_free")) || +- !(ip6tc_is_chain_addr = dlsym(libip6tc_handle,"ip6tc_is_chain")) || +- !(ip6tc_insert_entry_addr = dlsym(libip6tc_handle,"ip6tc_insert_entry")) || +- !(ip6tc_append_entry_addr = dlsym(libip6tc_handle,"ip6tc_append_entry")) || +- !(ip6tc_delete_entry_addr = dlsym(libip6tc_handle,"ip6tc_delete_entry")) || +- !(ip6tc_commit_addr = dlsym(libip6tc_handle,"ip6tc_commit")) || +- !(ip6tc_strerror_addr = dlsym(libip6tc_handle,"ip6tc_strerror"))) +- log_message(LOG_INFO, "Failed to dynamic link an ip6tc function"); ++ if (!libip6tc_handle && block_ipv6) { ++ /* Attempt to open the ip6tc library */ ++ if (!(libip6tc_handle = dlopen("libip6tc.so", RTLD_NOW)) && ++ !(libip6tc_handle = dlopen(IP6TC_LIB_NAME, RTLD_NOW))) { ++ log_message(LOG_INFO, "Unable to load ip6tc library - %s", dlerror()); ++ using_libip6tc = false; ++ } ++ else if (!(ip6tc_init_addr = dlsym(libip6tc_handle, "ip6tc_init")) || ++ !(ip6tc_free_addr = dlsym(libip6tc_handle, "ip6tc_free")) || ++ !(ip6tc_is_chain_addr = dlsym(libip6tc_handle,"ip6tc_is_chain")) || ++ !(ip6tc_insert_entry_addr = dlsym(libip6tc_handle,"ip6tc_insert_entry")) || ++ !(ip6tc_append_entry_addr = dlsym(libip6tc_handle,"ip6tc_append_entry")) || ++ !(ip6tc_delete_entry_addr = dlsym(libip6tc_handle,"ip6tc_delete_entry")) || ++ !(ip6tc_commit_addr = dlsym(libip6tc_handle,"ip6tc_commit")) || ++ !(ip6tc_strerror_addr = dlsym(libip6tc_handle,"ip6tc_strerror"))) { ++ log_message(LOG_INFO, "Failed to dynamic link an ip6tc function - %s", dlerror()); ++ using_libip6tc = false; ++ dlclose(libip6tc_handle); ++ libip6tc_handle = NULL; ++ } ++ } + +- return true; ++ return libip4tc_handle || libip6tc_handle; + } + #endif +-- +2.13.5 + diff --git a/SOURCES/bz1508435-no-segfault-ip_vs-load.patch b/SOURCES/bz1508435-no-segfault-ip_vs-load.patch new file mode 100644 index 0000000..b894b25 --- /dev/null +++ b/SOURCES/bz1508435-no-segfault-ip_vs-load.patch @@ -0,0 +1,168 @@ +From 19d0482859d9068b893104b9f8beeaba709aec2d Mon Sep 17 00:00:00 2001 +From: Quentin Armitage +Date: Mon, 27 Mar 2017 11:28:02 +0100 +Subject: [PATCH 2/3] Don't segfault if unable to load ip_vs module + +In a docker container it isn't possible to load a kernel module. The +check code was detecting that it couldn't load the module, but the +checker process, when cleaning up prior to exiting, was assuming that +certain pointers had been initialised which hadn't been when an error +was detected so early in the initialisation. + +This commit adds testing for uninitialised pointers during the exit +sequence. + +Signed-off-by: Quentin Armitage +--- + keepalived/check/check_api.c | 3 +++ + keepalived/check/check_daemon.c | 8 +++++--- + keepalived/check/check_data.c | 7 ++++++- + keepalived/check/ipvswrapper.c | 9 ++++++--- + keepalived/check/ipwrapper.c | 6 ++++-- + keepalived/check/libipvs.c | 5 ++++- + keepalived/core/keepalived_netlink.c | 7 ++++--- + 7 files changed, 32 insertions(+), 13 deletions(-) + +diff --git a/keepalived/check/check_api.c b/keepalived/check/check_api.c +index 51d8cc71..b7081fd0 100644 +--- a/keepalived/check/check_api.c ++++ b/keepalived/check/check_api.c +@@ -224,6 +224,9 @@ init_checkers_queue(void) + void + free_checkers_queue(void) + { ++ if (!checkers_queue) ++ return; ++ + free_list(&checkers_queue); + ncheckers = 0; + } +diff --git a/keepalived/check/check_daemon.c b/keepalived/check/check_daemon.c +index a87fd4cd..a3ff8cab 100644 +--- a/keepalived/check/check_daemon.c ++++ b/keepalived/check/check_daemon.c +@@ -74,7 +74,7 @@ stop_check(int status) + clear_services(); + ipvs_stop(); + #ifdef _WITH_SNMP_CHECKER_ +- if (global_data->enable_snmp_checker) ++ if (global_data && global_data->enable_snmp_checker) + check_snmp_agent_close(); + #endif + +@@ -82,8 +82,10 @@ stop_check(int status) + pidfile_rm(checkers_pidfile); + + /* Clean data */ +- free_global_data(global_data); +- free_check_data(check_data); ++ if (global_data) ++ free_global_data(global_data); ++ if (check_data) ++ free_check_data(check_data); + free_parent_mallocs_exit(); + + /* +diff --git a/keepalived/check/check_data.c b/keepalived/check/check_data.c +index 0c3f1940..6ce87229 100644 +--- a/keepalived/check/check_data.c ++++ b/keepalived/check/check_data.c +@@ -51,7 +51,12 @@ alloc_ssl(void) + void + free_ssl(void) + { +- ssl_data_t *ssl = check_data->ssl; ++ ssl_data_t *ssl; ++ ++ if (!check_data) ++ return; ++ ++ ssl = check_data->ssl; + + if (!ssl) + return; +diff --git a/keepalived/check/ipvswrapper.c b/keepalived/check/ipvswrapper.c +index cd7f169e..20757c94 100644 +--- a/keepalived/check/ipvswrapper.c ++++ b/keepalived/check/ipvswrapper.c +@@ -165,9 +165,12 @@ void + ipvs_stop(void) + { + /* Clean up the room */ +- FREE(srule); +- FREE(drule); +- FREE(daemonrule); ++ if (srule) ++ FREE(srule); ++ if (drule) ++ FREE(drule); ++ if (daemonrule) ++ FREE(daemonrule); + ipvs_close(); + } + +diff --git a/keepalived/check/ipwrapper.c b/keepalived/check/ipwrapper.c +index 0c2fc46b..ecf12713 100644 +--- a/keepalived/check/ipwrapper.c ++++ b/keepalived/check/ipwrapper.c +@@ -138,10 +138,12 @@ void + clear_services(void) + { + element e; +- list l = check_data->vs; + virtual_server_t *vs; + +- for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { ++ if (!check_data || !check_data->vs) ++ return; ++ ++ for (e = LIST_HEAD(check_data->vs); e; ELEMENT_NEXT(e)) { + vs = ELEMENT_DATA(e); + clear_service_vs(vs); + } +diff --git a/keepalived/check/libipvs.c b/keepalived/check/libipvs.c +index 2250e2b4..8722f7a6 100644 +--- a/keepalived/check/libipvs.c ++++ b/keepalived/check/libipvs.c +@@ -1157,7 +1157,10 @@ void ipvs_close(void) + if (try_nl) + return; + #endif +- close(sockfd); ++ if (sockfd != -1) { ++ close(sockfd); ++ sockfd = -1; ++ } + } + + const char *ipvs_strerror(int err) +diff --git a/keepalived/core/keepalived_netlink.c b/keepalived/core/keepalived_netlink.c +index 41e63b85..0f465cf5 100644 +--- a/keepalived/core/keepalived_netlink.c ++++ b/keepalived/core/keepalived_netlink.c +@@ -251,9 +251,12 @@ netlink_socket(nl_handle_t *nl, int flags, int group, ...) + } + + /* Close a netlink socket */ +-static int ++static void + netlink_close(nl_handle_t *nl) + { ++ if (!nl) ++ return; ++ + /* First of all release pending thread */ + thread_cancel(nl->thread); + +@@ -269,8 +272,6 @@ netlink_close(nl_handle_t *nl) + #endif + close(nl->fd); + #endif +- +- return 0; + } + + #ifdef _WITH_VRRP_ +-- +2.13.5 + diff --git a/SOURCES/bz1508435-remove-ipset-handling.patch b/SOURCES/bz1508435-remove-ipset-handling.patch new file mode 100644 index 0000000..2cc4f94 --- /dev/null +++ b/SOURCES/bz1508435-remove-ipset-handling.patch @@ -0,0 +1,30 @@ +From dcb5af32ea618c42e353f399962bdd6dc1c8a7d9 Mon Sep 17 00:00:00 2001 +From: Quentin Armitage +Date: Mon, 27 Mar 2017 20:44:50 +0100 +Subject: [PATCH 3/3] Don't attempt to remove ipsets if ipset handling not + initialised + +Signed-off-by: Quentin Armitage +--- + keepalived/vrrp/vrrp_ipset.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/keepalived/vrrp/vrrp_ipset.c b/keepalived/vrrp/vrrp_ipset.c +index 1c3e4990..b40bc836 100644 +--- a/keepalived/vrrp/vrrp_ipset.c ++++ b/keepalived/vrrp/vrrp_ipset.c +@@ -270,6 +270,11 @@ bool remove_ipsets(void) + if (!global_data->using_ipsets) + return true; + ++#ifdef _LIBIPSET_DYNAMIC_ ++ if (!libipset_handle) ++ return true; ++#endif ++ + session = ipset_session_init(printf); + if (!session) { + log_message(LOG_INFO, "Cannot initialize ipset session."); +-- +2.13.5 + diff --git a/SPECS/keepalived.spec b/SPECS/keepalived.spec index 55c7834..4e0a046 100644 --- a/SPECS/keepalived.spec +++ b/SPECS/keepalived.spec @@ -9,7 +9,7 @@ Name: keepalived Summary: Load balancer and high availability service Version: 1.3.5 -Release: 1%{?dist} +Release: 6%{?dist} License: GPLv2+ URL: http://www.keepalived.org/ Group: System Environment/Daemons @@ -18,13 +18,21 @@ Source0: http://www.keepalived.org/software/keepalived-%{version}.tar.gz Source1: keepalived.service Patch0: bz1419049-fix-unused-variables.patch - +Patch1: bz1477572-fix-man-page-vrrp_ipsets.patch +Patch2: bz1477563-fix-keepalived_script-user.patch +Patch3: bz1508435-load-ip-tables-handling.patch +Patch4: bz1508435-no-segfault-ip_vs-load.patch +Patch5: bz1508435-remove-ipset-handling.patch +Patch6: bz1477587-exclude-mismatch-vips.patch + +Requires: ipset-libs Requires(post): systemd Requires(preun): systemd Requires(postun): systemd BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) %if %{with snmp} +BuildRequires: net-snmp BuildRequires: net-snmp-devel %endif BuildRequires: systemd-units @@ -47,6 +55,12 @@ Keepalived also implements the Virtual Router Redundancy Protocol %prep %setup -q %patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 %build %configure \ @@ -103,6 +117,21 @@ Keepalived also implements the Virtual Router Redundancy Protocol %{_mandir}/man8/keepalived.8* %changelog +* Wed Jan 31 2018 Ryan O'Hara - 1.3.5-6 +- Add net-snmp as BuildRequires (#1536252) + +* Mon Dec 11 2017 Ryan O'Hara - 1.3.5-5 +- Exclude VIPs of non-matching address family (#1477587) + +* Thu Nov 16 2017 Ryan O'Hara - 1.3.5-4 +- Fix bugs related to failures when load modules and/or segfaults (#1508435) + +* Wed Aug 02 2017 Ryan O'Hara - 1.3.5-3 +- Don't complain about keepalived_script user if not needed (#1477563) + +* Wed Aug 02 2017 Ryan O'Hara - 1.3.5-2 +- Fix ipset-libs dependency and vrrp_ipset in man page (#1477572) + * Wed Mar 22 2017 Ryan O'Hara - 1.3.5-1 - Rebase to upstream version 1.3.5 (#1419049)