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 - 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 - 6.38-3 - Fix loading of sets with dependencies on other sets (RHBZ#1646666)