diff --git a/.gitignore b/.gitignore
index 39a9ee1..11ee4b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/ipset-6.38.tar.bz2
+SOURCES/ipset-7.1.tar.bz2
diff --git a/.ipset.metadata b/.ipset.metadata
index 9e70132..86e3c22 100644
--- a/.ipset.metadata
+++ b/.ipset.metadata
@@ -1 +1 @@
-7e5a25c449067e95c2e3a2c60768a1e301f12458 SOURCES/ipset-6.38.tar.bz2
+ed687efcc7f3a1117cc01d6a29639f9d604f2a04 SOURCES/ipset-7.1.tar.bz2
diff --git a/SOURCES/ipset.start-stop b/SOURCES/ipset.start-stop
index 61178c1..0c493dc 100644
--- a/SOURCES/ipset.start-stop
+++ b/SOURCES/ipset.start-stop
@@ -17,9 +17,22 @@ CLEAN_FILES=""
 
 trap "rm -rf \${CLEAN_FILES}" EXIT
 
-[ -x ${IPSET_BIN} ] || { echo "ipset: Cannot execute ${IPSET_BIN}" >&2; exit 5; }
+info() {
+	echo "ipset: ${*}" >&2
+}
+
+warn() {
+	echo "<4>ipset: ${*}" >&2
+}
+
+err() {
+	echo "<3>ipset: ${*}" >&2
+}
+
+[ -x ${IPSET_BIN} ] || { err "Cannot execute ${IPSET_BIN}"; exit 1; }
 
 # Source ipset configuration
+# shellcheck source=ipset-config
 [ -f ${IPSET_CONFIG} ] && . ${IPSET_CONFIG}
 
 set -f
@@ -39,13 +52,15 @@ save() {
 	rm -f ${IPSET_DATA_SAVED_FLAG}
 
 	# Save each set in a separate file
-	mkdir -pm 700 ${IPSET_DATA_DIR}
+	mkdir -p ${IPSET_DATA_DIR}
+	chmod 0700 ${IPSET_DATA_DIR}
 	IFS="
 "
 	for set in $(${IPSET_BIN} list -n -t); do
 		# Empty name allowed, use ".set" as suffix. 'ipset save' doesn't
 		# quote set names with spaces: if we have a space in the name,
 		# work around this by quoting it ourselves in the output.
+		# shellcheck disable=SC2003 # No POSIX equivalent to expr index
 		if expr index "${set}" " " >/dev/null; then
 			:> "${IPSET_DATA_DIR}/${set}.set"
 			for line in $(${IPSET_BIN} save "${set}"); do
@@ -57,7 +72,7 @@ save() {
 					line=${line#add *}
 				fi
 				line=${line#${set} *}
-				set="$(echo ${set} | sed 's/"/\\"/'g)"
+				set="$(echo "${set}" | sed 's/"/\\"/g')"
 				if [ $create -eq 1 ]; then
 					echo "create \"${set}\" ${line}" >> "${IPSET_DATA_DIR}/${set}.set"
 				else
@@ -68,7 +83,7 @@ save() {
 			${IPSET_BIN} save "${set}" > "${IPSET_DATA_DIR}/${set}.set" || fail=1
 		fi
 		[ -f "${IPSET_DATA_DIR}/${set}.set" ] && chmod 600 "${IPSET_DATA_DIR}/${set}.set"
-		[ $fail -eq 1 ] && echo "ipset: Cannot save set ${set}" >&2 && unset IFS && return 1
+		[ $fail -eq 1 ] && err "Cannot save set ${set}" && unset IFS && return 1
 	done
 	touch ${IPSET_DATA_SAVED_FLAG} || { unset IFS; return 1; }
 	unset IFS
@@ -80,6 +95,78 @@ save() {
 	return 0
 }
 
+# Generate a grep regexp matching abbreviated command forms. E.g., for create:
+# \(c\|cr\|cre\|crea\|creat\|create\)
+cmd_short_expr() {
+	out=
+	cmd_len=1
+	while [ "${cmd_len}" -le "${#1}" ]; do
+		[ -z "${out}" ] && out='\(' || out="${out}"'\|'
+		# shellcheck disable=SC2003 # No POSIX equivalent to expr substr
+		out="${out}$(expr substr "${1}" 1 "${cmd_len}")"
+		cmd_len=$((cmd_len + 1))
+	done
+	echo "${out}"'\)'
+}
+
+ipset_restore() {
+	file="${1}"
+
+	retfile="$(mktemp -q /tmp/ipset.XXXXXX)"
+	CLEAN_FILES="${CLEAN_FILES} ${retfile}"
+
+	# If restore fails due to invalid lines, remove them and retry
+	while ! restore_err="$( (${IPSET_BIN} -f "${file}" -! restore 2>&1; echo $? >"${retfile}") | head -n1; exit "$(cat "${retfile}")" )"; do
+		warn "${restore_err}"
+		case ${restore_err#*: } in
+		"No command specified"*)
+			line="$(grep -m1 -n "^${restore_err##* }" "${file}")"
+			line="${line%:*}"
+			;;
+		"Missing second mandatory argument to command "*)
+			cmd="${restore_err##* }"
+			cmd_expr="$(cmd_short_expr "${cmd}")"
+			line="$(grep -n '^'"${cmd_expr}" "${file}" | grep -m1 -v '^[0-9]\+\:'"${cmd_expr}"'[[:blank:]]\+[^[:blank:]]\+[[:blank:]]\+[^[:blank:]]\+')"
+			line="${line%:*}"
+			;;
+		"Missing mandatory argument to command "*)
+			cmd="${restore_err##* }"
+			cmd_expr="$(cmd_short_expr "${cmd}")"
+			line="$(grep -n '^'"${cmd_expr}" "${file}" | grep -m1 -v '^[0-9]\+\:'"${cmd_expr}"'[[:blank:]]\+[^[:blank:]]\+')"
+			line="${line%:*}"
+			;;
+		"Command "*"is invalid in restore mode"*)
+			restore_err_cmd="${restore_err#*: }"
+			restore_err_cmd="${restore_err_cmd#*\`}"
+			restore_err_cmd="${restore_err_cmd%%\'*}"
+			cmd="${restore_err_cmd##* }"
+			cmd_expr="$(cmd_short_expr "${cmd}")"
+			line="$(grep -m1 -ne '^'"${cmd_expr}"'[[:blank:]]\+' -e '^'"${restore_err_cmd}"'$' "${file}")"
+			line="${line%:*}"
+			;;
+		"Error in line "*)
+			line="${restore_err%: *}"
+			line="${line##* }"
+			;;
+		*)
+			rm "${retfile}"
+			CLEAN_FILES="${CLEAN_FILES%* ${retfile}}"
+			return 1
+			;;
+		esac
+
+		[ -z "${line}" ] && return 1
+
+		warn "Skipped invalid entry: $(sed "${line}q;d" "${file}")"
+		sed -i -e "${line}d" "${file}"
+
+		[ -s "${file}" ] || return 1
+	done
+
+	rm "${retfile}"
+	CLEAN_FILES="${CLEAN_FILES%* ${retfile}}"
+}
+
 load() {
 	if [ -f ${IPSET_DATA_SAVED_FLAG} ]; then
 		# If we have a cleanly saved directory with all sets, we can
@@ -92,8 +179,8 @@ load() {
 		[ -f ${IPSET_DATA_COMPAT_BACKUP} ] && rm -f ${IPSET_DATA_COMPAT} && mv -Tf ${IPSET_DATA_COMPAT_BACKUP} ${IPSET_DATA_COMPAT}
 	fi
 
-	if [ ! -d ${IPSET_DATA_DIR} -a ! -f ${IPSET_DATA_COMPAT} ]; then
-		echo "ipset: No existing configuration available, none loaded"
+	if [ ! -d ${IPSET_DATA_DIR} ] && [ ! -f ${IPSET_DATA_COMPAT} ]; then
+		info "No existing configuration available, none loaded"
 		touch ${IPSET_RUN}
 		return
 	fi
@@ -104,39 +191,45 @@ load() {
 	chmod 600 "${merged}"
 	set +f
 	if [ -d ${IPSET_DATA_DIR} ]; then
-		# Copy the first lines of each saved set first, as they create
-		# the sets, then the rest: list:set entries depend on other
-		# sets, so make sure they all get created first
-		for f in ${IPSET_DATA_DIR}/*; do
+		# Copy create commands from each saved set first, then the rest:
+		# list:set entries depend on other sets, so make sure they all
+		# get created first
+		for f in "${IPSET_DATA_DIR}"/*; do
 			[ "${f}" = "${IPSET_DATA_DIR}/*" ] && break
-			head -n1 "${f}" >> ${merged}
+			[ -f "${f}" ] || continue
+			grep '^c' "${f}" >> "${merged}"
 		done
-		for f in ${IPSET_DATA_DIR}/*; do
+		for f in "${IPSET_DATA_DIR}"/*; do
 			[ "${f}" = "${IPSET_DATA_DIR}/*" ] && break
-			tail -n +2 "${f}" >> ${merged}
+			[ -f "${f}" ] || continue
+			grep -v '^c' "${f}" >> "${merged}"
 		done
 	fi
 	set -f
-	[ -f ${IPSET_DATA_COMPAT} ] && cat ${IPSET_DATA_COMPAT} >> ${merged}
+	[ -f ${IPSET_DATA_COMPAT} ] && cat ${IPSET_DATA_COMPAT} >> "${merged}"
 
 	# Drop sets that aren't in saved data, mark conflicts with existing sets
 	conflicts=""
 	IFS="
 "
 	for set in $(${IPSET_BIN} list -n -t); do
-		grep -q "^create ${set} " ${merged} && conflicts="${conflicts}|${set}" && continue
-		${IPSET_BIN} destroy "${set}" 2>/dev/null
+		grep -q "^create ${set} " "${merged}" && conflicts="${conflicts}|${set}" && continue
+
 		# We can't destroy the set if it's in use, flush it instead
-		[ $? -ne 0 ] && ${IPSET_BIN} flush "${set}"
+		if ! ${IPSET_BIN} destroy "${set}" 2>/dev/null; then
+			${IPSET_BIN} flush "${set}"
+		fi
 	done
 	unset IFS
 	conflicts="${conflicts#|*}"
 
 	# Common case: if we have no conflicts, just restore in one shot
 	if [ -z "${conflicts}" ]; then
-		${IPSET_BIN} restore -! < ${merged}
-		[ $? -ne 0 ] && echo "ipset: Failed to restore configured sets" >&2
-		rm ${merged}
+		if ! ipset_restore "${merged}"; then
+			err "Failed to restore configured sets"
+			exit 1
+		fi
+		rm "${merged}"
 		CLEAN_FILES="${CLEAN_FILES%* ${merged}}"
 		touch ${IPSET_RUN}
 		return
@@ -149,8 +242,10 @@ load() {
 		IFS="
 "
 		for set in $(${IPSET_BIN} list -n -t); do
-			grep -q "^create $(echo ${salt}${set} | md5sum | head -c31) " ${merged}
-			[ $? -eq 0 ] && unique=0 && break
+			if grep -q "^create $(echo "${salt}${set}" | md5sum | head -c31) " "${merged}"; then
+				unique=0
+				break
+			fi
 		done
 		unset IFS
 		[ ${unique} -eq 1 ] && break
@@ -158,23 +253,30 @@ load() {
 	done
 
 	# Add sets, mangling names for conflicting sets
-	awk '/^(add|create) ('"${conflicts}"')/ { printf "%s ",$1; system("echo '${salt}'" $2 " | md5sum | head -c31"); $1=""; $2=""; print; next} {print}' ${merged} | ${IPSET_BIN} restore -!
+	mangled="$(mktemp -q /tmp/ipset.XXXXXX)"
+	CLEAN_FILES="${CLEAN_FILES} ${mangled}"
+	chmod 600 "${mangled}"
 
-	[ $? -ne 0 ] && echo "ipset: Failed to restore configured sets" >&2
+	awk '/^(add|create) ('"${conflicts}"')/ { printf "%s ",$1; system("echo '${salt}'" $2 " | md5sum | head -c31"); $1=""; $2=""; print; next} {print}' "${merged}" > "${mangled}"
+	if ! ipset_restore "${mangled}"; then
+		err "Failed to restore configured sets"
+		exit 1
+	fi
+
+	rm "${mangled}"
+	CLEAN_FILES="${CLEAN_FILES%* ${mangled}}"
 
 	# Swap and delete old sets
 	IFS='|'
 	for set in ${conflicts}; do
-		mangled="$(echo ${salt}${set} | md5sum | head -c31)"
-		${IPSET_BIN} swap "${set}" "${mangled}" 2>/dev/null
-		if [ $? -ne 0 ]; then
+		mangled="$(echo "${salt}${set}" | md5sum | head -c31)"
+		if ! ${IPSET_BIN} swap "${set}" "${mangled}" 2>/dev/null; then
 			# This fails if set types are different: try to destroy
 			# existing set
-			${IPSET_BIN} destroy "${set}" 2>/dev/null
-			if [ $? -ne 0 ]; then
+			if ! ${IPSET_BIN} destroy "${set}" 2>/dev/null; then
 				# Conflicting set is in use, we can only warn
 				# and flush the existing set
-				echo "ipset: Cannot load set \"${set}\", set with same name and conflicting type in use" >&2
+				err "Cannot load set \"${set}\", set with same name and conflicting type in use"
 				${IPSET_BIN} flush "${set}"
 				${IPSET_BIN} destroy "${mangled}"
 			else
@@ -186,13 +288,13 @@ load() {
 	done
 	unset IFS
 
-	rm ${merged}
+	rm "${merged}"
 	CLEAN_FILES="${CLEAN_FILES%* ${merged}}"
 	touch ${IPSET_RUN}
 }
 
 cleanup() {
-	${IPSET_BIN} flush || echo "ipset: Failed to flush sets" >&2
+	${IPSET_BIN} flush || err "Failed to flush sets"
 
 	# Try to destroy all sets at once. This will fail if some are in use,
 	# destroy all the other ones in that case
@@ -200,24 +302,25 @@ cleanup() {
 	IFS="
 "
 	for set in $(${IPSET_BIN} list -n -t); do
-		${IPSET_BIN} destroy "${set}" 2>/dev/null
+		if ! ${IPSET_BIN} destroy "${set}"; then
+			err "Failed to destroy set ${set}"
+		fi
 	done
 	unset IFS
 }
 
 stop() {
-	[ -f ${IPSET_RUN} ] || { echo "ipset: not running"; return 0; }
-	[ "${IPSET_SAVE_ON_STOP}" = "yes" ] && { save || echo "ipset: Failed to save sets"; }
+	[ -f ${IPSET_RUN} ] || { info "Not running"; return 0; }
+	[ "${IPSET_SAVE_ON_STOP}" = "yes" ] && { save || err "Failed to save sets"; }
 
 	# Nothing to stop if the ip_set module is not loaded
-	lsmod | grep -q "^ip_set " || { echo "ipset: not running"; rm ${IPSET_RUN}; return 0; }
+	lsmod | grep -q "^ip_set " || { info "Not running"; rm ${IPSET_RUN}; return 0; }
 
 	# If the xt_set module is in use, then iptables is using ipset, so
 	# refuse to stop the service
-	mod="$(lsmod | grep ^xt_set)"
-	if [ $? -eq 0 ]; then
-		if [ "$(echo ${mod} | tr -s ' ' | cut -d' ' -f3)" != "0" ]; then
-			echo "ipset: Current iptables configuration requires ipset" >&2 && return 1
+	if mod="$(lsmod | grep ^xt_set)"; then
+		if [ "$(echo "${mod}" | tr -s ' ' | cut -d' ' -f3)" != "0" ]; then
+			err "Current iptables configuration requires ipset" && return 1
 		fi
 	fi
 
@@ -243,7 +346,7 @@ save)
 	save
 	;;
 *)
-	echo "Usage: $0 {start|stop|reload|save}" >&2
+	info "Usage: $0 {start|stop|reload|save}"
 	exit 1
 esac
 
diff --git a/SPECS/ipset.spec b/SPECS/ipset.spec
index 591672c..b1b75df 100644
--- a/SPECS/ipset.spec
+++ b/SPECS/ipset.spec
@@ -2,8 +2,8 @@
 %define legacy_actions %{_libexecdir}/initscripts/legacy-actions
 
 Name:             ipset
-Version:          6.38
-Release:          3%{?dist}
+Version:          7.1
+Release:          1%{?dist}
 Summary:          Manage Linux IP sets
 
 License:          GPLv2
@@ -136,14 +136,14 @@ fi
 %postun service
 %systemd_postun_with_restart %{name}.service
 
-%triggerin service -- ipset-service < 6.38-1%{?dist}
+%triggerin service -- ipset-service < 6.38-1.el7
 # Before 6.38-1, ipset.start-stop keeps a backup of previously saved sets, but
 # doesn't touch the /etc/sysconfig/ipset.d/.saved flag. Remove the backup on
 # upgrade, so that we use the current version of saved sets
 rm -f /etc/sysconfig/ipset.save || :
 exit 0
 
-%triggerun service -- ipset-service < 6.38-1%{?dist}
+%triggerun service -- ipset-service < 6.38-1.el7
 # Up to 6.29-1, ipset.start-stop uses a single data file
 for f in /etc/sysconfig/ipset.d/*; do
     [ "${f}" = "/etc/sysconfig/ipset.d/*" ] && break
@@ -158,7 +158,8 @@ exit 0
 
 %files libs
 %doc COPYING
-%{_libdir}/lib%{name}.so.11*
+%{_libdir}/lib%{name}.so.13*
+%doc %{_mandir}/man3/lib%{name}.3.gz
 
 %files devel
 %{_includedir}/lib%{name}
@@ -176,6 +177,37 @@ exit 0
 
 
 %changelog
+* Sun Feb 24 2019 Stefano Brivio <sbrivio@redhat.com> - 7.1-1
+- Rebase to 7.1 (RHBZ#1649080):
+  - Add compatibility support for strscpy()
+  - Correct the manpage about the sort option
+  - Add missing functions to libipset.map
+  - configure.ac: Fix build regression on RHEL/CentOS/SL (Serhey Popovych)
+  - Implement sorting for hash types in the ipset tool
+  - Fix to list/save into file specified by option (reported by Isaac Good)
+  - Introduction of new commands and protocol version 7, updated kernel include files
+  - Add compatibility support for async in pernet_operations
+  - Use more robust awk patterns to check for backward compatibility
+  - Prepare the ipset tool to handle multiple protocol version
+  - Fix warning message handlin
+  - Correct to test null valued entry in hash:net6,port,net6 test
+  - Library reworked to support embedding ipset completely
+  - Add compatibility to support kvcalloc()
+  - Validate string type attributes in attr2data() (Stefano Brivio)
+  - manpage: Add comment about matching on destination MAC address (Stefano Brivio)
+    (RHBZ#1649079)
+  - Add compatibility to support is_zero_ether_addr()
+  - Fix use-after-free in ipset_parse_name_compat() (Stefano Brivio) (RHBZ#1649073)
+  - Fix leak in build_argv() on line parsing error (Stefano Brivio) (RHBZ#1649073)
+  - Simplify return statement in ipset_mnl_query() (Stefano Brivio) (RHBZ#1649073)
+  - tests/check_klog.sh: Try dmesg too, don't let shell terminate script (Stefano Brivio) 
+- Fixes:
+  - Fix all shellcheck warnings in init script (RHBZ#1649073)
+  - Make error reporting consistent, introduce different severities (RHBZ#1649877)
+  - While restoring, on invalid entries, remove them and retry (RHBZ#1650297)
+  - Fix covscan SC2166 warning in init script (RHBZ#1649073)
+  - Hardcode triggerin, triggerun versions for ipset-service (RHBZ#1646666)
+
 * Tue Nov 06 2018 Stefano Brivio <sbrivio@redhat.com> - 6.38-3
 - Fix loading of sets with dependencies on other sets (RHBZ#1646666)