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 <quentin@armitage.org.uk>
+Date: Tue, 28 Mar 2017 09:11:44 +0100
+Subject: [PATCH] Make dynamic flag bool
+
+Signed-off-by: Quentin Armitage <quentin@armitage.org.uk>
+
+Fix releasing malloc'd memory for saved core pattern
+
+Signed-off-by: Quentin Armitage <quentin@armitage.org.uk>
+
+Fix detecting default script uid/gid
+
+Signed-off-by: Quentin Armitage <quentin@armitage.org.uk>
+
+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 <quentin@armitage.org.uk>
+---
+ 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 <STRING>      {	# VS group declaration
+         weight <INTEGER>		# weight to use (default: 1)
+         inhibit_on_failure		# Set weight to 0 on healthchecker
+ 					#  failure
+-        notify_up <STRING>|<QUOTED-STRING> # Script to launch when
+-					   #  healthchecker consider service
+-					   #  as up.
+-        notify_down <STRING>|<QUOTED-STRING> # Script to launch when
+-					     #  healthchecker consider service
+-					     #  as down.
++        notify_up <STRING>|<QUOTED-STRING> [username [groupname]]
++					# Script to launch when
++					#  healthchecker consider service
++					#  as up.
++        notify_down <STRING>|<QUOTED-STRING> [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 <STRING>      {	# VS group declaration
+     real_server <IP ADDRESS> <PORT> {	# Idem
+         weight <INTEGER>		# Idem
+         inhibit_on_failure		# Idem
+-        notify_up <STRING>|<QUOTED-STRING> # Idem
+-        notify_down <STRING>|<QUOTED-STRING> # Idem
++        notify_up <STRING>|<QUOTED-STRING> [username [groupname]] # Idem
++        notify_down <STRING>|<QUOTED-STRING> [username [groupname]] # Idem
+ 
+         TCP_CHECK {			# TCP healthchecker
+             connect_ip <IP ADDRESS> # IP address to connect
+@@ -620,8 +622,8 @@ virtual_server group <STRING>      {	# VS group declaration
+     real_server <IP ADDRESS> <PORT> {	# Idem
+         weight <INTEGER>		# Idem
+         inhibit_on_failure		# Idem
+-        notify_up <STRING>|<QUOTED-STRING> # Idem
+-        notify_down <STRING>|<QUOTED-STRING> # Idem
++        notify_up <STRING>|<QUOTED-STRING> [username [groupname]] # Idem
++        notify_down <STRING>|<QUOTED-STRING> [username [groupname]] # Idem
+ 
+         SMTP_CHECK {                   # SMTP healthchecker
+             connect_ip <IP ADDRESS>     # Optional IP address to connect to
+@@ -658,8 +660,8 @@ virtual_server group <STRING>      {	# VS group declaration
+     real_server <IP ADDRESS> <PORT> {	# Idem
+         weight <INTEGER>		# Idem
+         inhibit_on_failure		# Idem
+-        notify_up <STRING>|<QUOTED-STRING> # Idem
+-        notify_down <STRING>|<QUOTED-STRING> # Idem
++        notify_up <STRING>|<QUOTED-STRING> [username [groupname]] # Idem
++        notify_down <STRING>|<QUOTED-STRING> [username [groupname]] # Idem
+ 
+         DNS_CHECK {                     # DNS healthchecker
+             connect_ip <IP ADDRESS>     # Optional IP address to connect to
+@@ -677,8 +679,8 @@ virtual_server group <STRING>      {	# VS group declaration
+     real_server <IP ADDRESS> <PORT> {	# Idem
+         weight <INTEGER>		# Idem
+         inhibit_on_failure		# Idem
+-        notify_up <STRING>|<QUOTED-STRING> # Idem
+-        notify_down <STRING>|<QUOTED-STRING> # Idem
++        notify_up <STRING>|<QUOTED-STRING> [username [groupname]] # Idem
++        notify_down <STRING>|<QUOTED-STRING> [username [groupname]] # Idem
+ 
+         MISC_CHECK {				# MISC healthchecker
+             misc_path <STRING>|<QUOTED-STRING>	# 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 <STRING>|<QUOTED-STRING>
++           notify_up <STRING>|<QUOTED-STRING> [username [groupname]]
+            # Script to execute when healthchecker
+            # considers service as down.
+-           notify_down <STRING>|<QUOTED-STRING>
++           notify_down <STRING>|<QUOTED-STRING> [username [groupname]]
+ 
+            uthreshold <INTEGER> # maximum number of connections to server
+            lthreshold <INTEGER> # 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 <quentin@armitage.org.uk>
+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 <quentin@armitage.org.uk>
+---
+ 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 <rohara@redhat.com>
+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 <quentin@armitage.org.uk>
+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 <quentin@armitage.org.uk>
+---
+ 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 <libipset/session.h>
+ #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 <netinet/ip6.h>
+ #include <stdint.h>
+ 
++/* 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 <linux/types.h>        /* For __beXX types in userland */
+ #include <linux/netfilter.h>    /* For nf_inet_addr */
+ #include <stdint.h>
++#include <stdio.h>
+ 
+ #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 <xtables.h>
+ #include <libipset/linux_ip_set.h>
+ #endif
++#include <sys/stat.h>
++#include <sys/vfs.h>
++#include <linux/magic.h>
++#include <stdbool.h>
+ 
+ #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 <xtables.h>
+ #include <libiptc/libiptc.h>
+ #include <libiptc/libip6tc.h>
+-#ifdef _HAVE_LIBIPSET_
+ #include <libipset/linux_ip_set.h>
+ #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 <linux/netfilter/xt_set.h>
+ #endif
+ #include <unistd.h>
+ #include <signal.h>
+ #include <stdint.h>
+-#ifdef _LIBXTABLES_DYNAMIC_
+-#include <sys/types.h>
+-#include <sys/stat.h>
+-#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 <quentin@armitage.org.uk>
+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 <quentin@armitage.org.uk>
+---
+ 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 <quentin@armitage.org.uk>
+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 <quentin@armitage.org.uk>
+---
+ 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 <rohara@redhat.com> - 1.3.5-6
+- Add net-snmp as BuildRequires (#1536252)
+
+* Mon Dec 11 2017 Ryan O'Hara <rohara@redhat.com> - 1.3.5-5
+- Exclude VIPs of non-matching address family (#1477587)
+
+* Thu Nov 16 2017 Ryan O'Hara <rohara@redhat.com> - 1.3.5-4
+- Fix bugs related to failures when load modules and/or segfaults (#1508435)
+
+* Wed Aug 02 2017 Ryan O'Hara <rohara@redhat.com> - 1.3.5-3
+- Don't complain about keepalived_script user if not needed (#1477563)
+
+* Wed Aug 02 2017 Ryan O'Hara <rohara@redhat.com> - 1.3.5-2
+- Fix ipset-libs dependency and vrrp_ipset in man page (#1477572)
+
 * Wed Mar 22 2017 Ryan O'Hara <rohara@redhat.com> - 1.3.5-1
 - Rebase to upstream version 1.3.5 (#1419049)