diff --git a/RHEL-79823-portblock-fix-version-detection.patch b/RHEL-79823-portblock-fix-version-detection.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c70ecea3e6397fc5b5d8b0e3e8c58c977a64658c
--- /dev/null
+++ b/RHEL-79823-portblock-fix-version-detection.patch
@@ -0,0 +1,448 @@
+--- a/heartbeat/portblock 2025-02-20 14:54:18.047134471 +0100
++++ b/heartbeat/portblock 2025-02-20 14:09:44.546869740 +0100
+@@ -25,6 +25,7 @@
+ # Defaults
+ OCF_RESKEY_protocol_default=""
+ OCF_RESKEY_portno_default=""
++OCF_RESKEY_direction_default="in"
+ OCF_RESKEY_action_default=""
+ OCF_RESKEY_ip_default="0.0.0.0/0"
+ OCF_RESKEY_reset_local_on_unblock_stop_default="false"
+@@ -33,6 +34,7 @@
+
+ : ${OCF_RESKEY_protocol=${OCF_RESKEY_protocol_default}}
+ : ${OCF_RESKEY_portno=${OCF_RESKEY_portno_default}}
++: ${OCF_RESKEY_direction=${OCF_RESKEY_direction_default}}
+ : ${OCF_RESKEY_action=${OCF_RESKEY_action_default}}
+ : ${OCF_RESKEY_ip=${OCF_RESKEY_ip_default}}
+ : ${OCF_RESKEY_reset_local_on_unblock_stop=${OCF_RESKEY_reset_local_on_unblock_stop_default}}
+@@ -217,6 +219,18 @@
+ Connection state file synchronization script
+
+
++
++
++
++Whether to block incoming or outgoing traffic. Can be either "in",
++"out", or "both".
++If "in" is used, the incoming ports are blocked on the INPUT chain.
++If "out" is used, the outgoing ports are blocked on the OUTPUT chain.
++If "both" is used, both the incoming and outgoing ports are blocked.
++
++Whether to block incoming or outgoing traffic, or both
++
++
+
+
+
+@@ -240,36 +254,73 @@
+ # and disable us -- but we're still in some sense active...
+ #
+
+-#active_grep_pat {udp|tcp} portno,portno
++#active_grep_pat {udp|tcp} portno,portno ip {d|s}
++# d = look for destination ports
++# s = look for source ports
+ active_grep_pat()
+ {
+ w="[ ][ ]*"
+ any="0\\.0\\.0\\.0/0"
+- echo "^DROP${w}${1}${w}--${w}${any}${w}${3}${w}multiport${w}dports${w}${2}\>"
++ src=$any dst=$3
++ if [ "$4" = "s" ]; then
++ local src=$3
++ local dst=$any
++ fi
++ # iptables 1.8.9 briefly broke the output format, returning the
++ # numeric protocol value instead of a string. Support both variants.
++ if [ "$1" = "tcp" ]; then
++ local prot="(tcp|6)"
++ else
++ local prot="(udp|17)"
++ fi
++ echo "^DROP${w}${prot}${w}--${w}${src}${w}${dst}${w}multiport${w}${4}ports${w}${2}$"
+ }
+
+-#chain_isactive {udp|tcp} portno,portno ip
++#chain_isactive {udp|tcp} portno,portno ip chain
+ chain_isactive()
+ {
+- PAT=`active_grep_pat "$1" "$2" "$3"`
+- $IPTABLES $wait -n -L INPUT | grep "$PAT" >/dev/null
++ [ "$4" = "OUTPUT" ] && ds="s" || ds="d"
++ PAT=$(active_grep_pat "$1" "$2" "$3" "$ds")
++ $IPTABLES $wait -n -L "$4" | grep -qE "$PAT"
++}
++
++# netstat -tn and ss -Htn, split on whitespace and colon,
++# look very similar:
++# tcp 0 0 10.43.55.1 675 10.43.9.8 2049 ESTABLISHED
++# ESTAB 0 0 10.43.55.1 675 10.43.9.8 2049
++# so we can write one awk script for both
++get_established_tcp_connections()
++{
++ local columns
++ if [ -z "$1" ] ; then
++ columns='$4,$5, $6,$7'
++ else
++ # swap local and remote for "tickle_local"
++ columns='$6,$7, $4,$5'
++ fi
++ $ss_or_netstat | awk -F '[:[:space:]]+' '
++ ( $8 == "ESTABLISHED" || $1 == "ESTAB" ) && $4 == "'$OCF_RESKEY_ip'" \
++ {printf "%s:%s\t%s:%s\n", '"$columns"'}'
+ }
+
+ save_tcp_connections()
+ {
+ [ -z "$OCF_RESKEY_tickle_dir" ] && return
+ statefile=$OCF_RESKEY_tickle_dir/$OCF_RESKEY_ip
++ # If we have _no_ sync script, we probably have a shared
++ # (or replicated) directory, and need to fsync, or we might
++ # end up with the just truncated file after failover, exactly
++ # when we need it.
++ #
++ # If we _do_ have a sync script, it is not that important whether
++ # the local state file is fsync'ed or not, the sync script is
++ # responsible to "atomically" communicate the state to the peer(s).
+ if [ -z "$OCF_RESKEY_sync_script" ]; then
+- netstat -tn |awk -F '[:[:space:]]+' '
+- $8 == "ESTABLISHED" && $4 == "'$OCF_RESKEY_ip'" \
+- {printf "%s:%s\t%s:%s\n", $4,$5, $6,$7}' |
+- dd of="$statefile".new conv=fsync status=none &&
+- mv "$statefile".new "$statefile"
++ get_established_tcp_connections |
++ dd of="$statefile".new conv=fsync status=none &&
++ mv "$statefile".new "$statefile"
+ else
+- netstat -tn |awk -F '[:[:space:]]+' '
+- $8 == "ESTABLISHED" && $4 == "'$OCF_RESKEY_ip'" \
+- {printf "%s:%s\t%s:%s\n", $4,$5, $6,$7}' \
+- > $statefile
++ get_established_tcp_connections > $statefile
+ $OCF_RESKEY_sync_script $statefile > /dev/null 2>&1 &
+ fi
+ }
+@@ -277,7 +328,6 @@
+ tickle_remote()
+ {
+ [ -z "$OCF_RESKEY_tickle_dir" ] && return
+- echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
+ f=$OCF_RESKEY_tickle_dir/$OCF_RESKEY_ip
+ [ -r $f ] || return
+ $TICKLETCP -n 3 < $f
+@@ -289,11 +339,6 @@
+ f=$OCF_RESKEY_tickle_dir/$OCF_RESKEY_ip
+ [ -r $f ] || return
+
+- checkcmd="netstat -tn"
+- if ! have_binary "netstat"; then
+- checkcmd="ss -Htn"
+- fi
+-
+ # swap "local" and "remote" address,
+ # so we tickle ourselves.
+ # We set up a REJECT with tcp-reset before we do so, so we get rid of
+@@ -302,122 +347,152 @@
+ # the way if we switch-over and then switch-back in quick succession.
+ local i
+ awk '{ print $2, $1; }' $f | $TICKLETCP
+- $checkcmd | grep -Fw $OCF_RESKEY_ip || return
++ $ss_or_netstat | grep -Fw $OCF_RESKEY_ip || return
+ for i in 0.1 0.5 1 2 4 ; do
+ sleep $i
+- awk '{ print $2, $1; }' $f | $TICKLETCP
+- $checkcmd | grep -Fw $OCF_RESKEY_ip || break
++ # now kill what is currently in the list,
++ # not what was recorded during last monitor
++ get_established_tcp_connections swap | $TICKLETCP
++ $ss_or_netstat | grep -Fw $OCF_RESKEY_ip || break
+ done
+ }
+
+ SayActive()
+ {
+- echo "$CMD DROP rule for INPUT chain [$*] is running (OK)"
++ ocf_log debug "$CMD DROP rule [$*] is running (OK)"
+ }
+
+ SayConsideredActive()
+ {
+- echo "$CMD DROP rule for INPUT chain [$*] considered to be running (OK)"
++ ocf_log debug "$CMD DROP rule [$*] considered to be running (OK)"
+ }
+
+ SayInactive()
+ {
+- echo "$CMD DROP rule for INPUT chain [$*] is inactive"
++ ocf_log debug "$CMD DROP rule [$*] is inactive"
+ }
+
+-#IptablesStatus {udp|tcp} portno,portno ip {block|unblock}
++#IptablesStatus {udp|tcp} portno,portno ip {in|out|both} {block|unblock}
+ IptablesStatus() {
+- local rc
+- rc=$OCF_ERR_GENERIC
+- activewords="$CMD $1 $2 is running (OK)"
+- if chain_isactive "$1" "$2" "$3"; then
+- case $4 in
+- block)
+- SayActive $*
+- rc=$OCF_SUCCESS
+- ;;
+- *)
+- SayInactive $*
+- rc=$OCF_NOT_RUNNING
+- ;;
+- esac
+- else
+- case $4 in
+- block)
+- if ha_pseudo_resource "${OCF_RESOURCE_INSTANCE}" status; then
+- SayConsideredActive $*
+- rc=$OCF_SUCCESS
+- else
+- SayInactive $*
+- rc=$OCF_NOT_RUNNING
+- fi
+- ;;
+-
+- *)
+- if ha_pseudo_resource "${OCF_RESOURCE_INSTANCE}" status; then
+- SayActive $*
+- #This is only run on real monitor events.
+- save_tcp_connections
+- rc=$OCF_SUCCESS
+- else
+- SayInactive $*
+- rc=$OCF_NOT_RUNNING
+- fi
+- ;;
+- esac
+- fi
+-
+- return $rc
++ local rc
++ rc=$OCF_ERR_GENERIC
++ is_active=0
++ if [ "$4" = "in" ] || [ "$4" = "both" ]; then
++ chain_isactive "$1" "$2" "$3" INPUT
++ is_active=$?
++ fi
++ if [ "$4" = "out" ] || [ "$4" = "both" ]; then
++ chain_isactive "$1" "$2" "$3" OUTPUT
++ r=$?
++ [ $r -gt $is_active ] && is_active=$r
++ fi
++ if [ $is_active -eq 0 ]; then
++ case $5 in
++ block)
++ SayActive $*
++ rc=$OCF_SUCCESS
++ ;;
++ *)
++ SayInactive $*
++ rc=$OCF_NOT_RUNNING
++ ;;
++ esac
++ else
++ case $5 in
++ block)
++ if ha_pseudo_resource "${OCF_RESOURCE_INSTANCE}" status; then
++ SayConsideredActive $*
++ rc=$OCF_SUCCESS
++ else
++ SayInactive $*
++ rc=$OCF_NOT_RUNNING
++ fi
++ ;;
++ *)
++ if ha_pseudo_resource "${OCF_RESOURCE_INSTANCE}" status; then
++ SayActive $*
++ #This is only run on real monitor events.
++ save_tcp_connections
++ rc=$OCF_SUCCESS
++ else
++ SayInactive $*
++ rc=$OCF_NOT_RUNNING
++ fi
++ ;;
++ esac
++ fi
++ return $rc
+ }
+
+-#IptablesBLOCK {udp|tcp} portno,portno ip
+-IptablesBLOCK()
++#DoIptables {-I|-D} {udp|tcp} portno,portno ip chain
++DoIptables()
+ {
+- local rc=0
+- local try_reset=false
+- if [ "$1/$4/$__OCF_ACTION" = tcp/unblock/stop ] &&
+- ocf_is_true $reset_local_on_unblock_stop
+- then
+- try_reset=true
+- fi
+- if
+- chain_isactive "$1" "$2" "$3"
+- then
+- : OK -- chain already active
++ op=$1 proto=$2 ports=$3 ip=$4 chain=$5
++ active=0; chain_isactive "$proto" "$ports" "$ip" "$chain" && active=1
++ want_active=0; [ "$op" = "-I" ] && want_active=1
++ ocf_log debug "active: $active want_active: $want_active"
++ if [ $active -eq $want_active ] ; then
++ : Chain already in desired state
+ else
+- if $try_reset ; then
+- $IPTABLES $wait -I OUTPUT -p "$1" -s "$3" -m multiport --sports "$2" -j REJECT --reject-with tcp-reset
+- tickle_local
++ [ "$chain" = "OUTPUT" ] && ds="s" || ds="d"
++ $IPTABLES $wait "$op" "$chain" -p "$proto" -${ds} "$ip" -m multiport --${ds}ports "$ports" -j DROP
++ fi
++}
++
++#IptablesBLOCK {udp|tcp} portno,portno ip {in|out|both} {block|unblock}
++IptablesBLOCK()
++{
++ local rc_in=0
++ local rc_out=0
++ if [ "$4" = "in" ] || [ "$4" = "both" ]; then
++ local try_reset=false
++ if [ "$1/$5/$__OCF_ACTION" = tcp/unblock/stop ] &&
++ ocf_is_true $reset_local_on_unblock_stop
++ then
++ try_reset=true
+ fi
+- $IPTABLES $wait -I INPUT -p "$1" -d "$3" -m multiport --dports "$2" -j DROP
+- rc=$?
+- if $try_reset ; then
+- $IPTABLES $wait -D OUTPUT -p "$1" -s "$3" -m multiport --sports "$2" -j REJECT --reject-with tcp-reset
++ if
++ chain_isactive "$1" "$2" "$3" INPUT
++ then
++ : OK -- chain already active
++ else
++ if $try_reset ; then
++ $IPTABLES $wait -I OUTPUT -p "$1" -s "$3" -m multiport --sports "$2" -j REJECT --reject-with tcp-reset
++ tickle_local
++ fi
++ $IPTABLES $wait -I INPUT -p "$1" -d "$3" -m multiport --dports "$2" -j DROP
++ rc_in=$?
++ if $try_reset ; then
++ $IPTABLES $wait -D OUTPUT -p "$1" -s "$3" -m multiport --sports "$2" -j REJECT --reject-with tcp-reset
++ fi
+ fi
+ fi
++ if [ "$4" = "out" ] || [ "$4" = "both" ]; then
++ DoIptables -I "$1" "$2" "$3" OUTPUT
++ rc_out=$?
++ fi
+
+- return $rc
++ [ $rc_in -gt $rc_out ] && return $rc_in || return $rc_out
+ }
+
+-#IptablesUNBLOCK {udp|tcp} portno,portno ip
++#IptablesUNBLOCK {udp|tcp} portno,portno ip {in|out|both}
+ IptablesUNBLOCK()
+ {
+- if
+- chain_isactive "$1" "$2" "$3"
+- then
+- $IPTABLES $wait -D INPUT -p "$1" -d "$3" -m multiport --dports "$2" -j DROP
+- else
+- : Chain Not active
++ if [ "$4" = "in" ] || [ "$4" = "both" ]; then
++ DoIptables -D "$1" "$2" "$3" INPUT
++ fi
++ if [ "$4" = "out" ] || [ "$4" = "both" ]; then
++ DoIptables -D "$1" "$2" "$3" OUTPUT
+ fi
+
+ return $?
+ }
+
+-#IptablesStart {udp|tcp} portno,portno ip {block|unblock}
++#IptablesStart {udp|tcp} portno,portno ip {in|out|both} {block|unblock}
+ IptablesStart()
+ {
+ ha_pseudo_resource "${OCF_RESOURCE_INSTANCE}" start
+- case $4 in
++ case $5 in
+ block) IptablesBLOCK "$@";;
+ unblock)
+ IptablesUNBLOCK "$@"
+@@ -432,11 +507,11 @@
+ return $?
+ }
+
+-#IptablesStop {udp|tcp} portno,portno ip {block|unblock}
++#IptablesStop {udp|tcp} portno,portno ip {in|out|both} {block|unblock}
+ IptablesStop()
+ {
+ ha_pseudo_resource "${OCF_RESOURCE_INSTANCE}" stop
+- case $4 in
++ case $5 in
+ block) IptablesUNBLOCK "$@";;
+ unblock)
+ save_tcp_connections
+@@ -454,7 +529,7 @@
+ CheckPort() {
+ # Examples of valid port: "1080", "1", "0080"
+ # Examples of invalid port: "1080bad", "0", "0000", ""
+- echo $1 |egrep -qx '[0-9]+(:[0-9]+)?(,[0-9]+(:[0-9]+)?)*'
++ echo $1 | $EGREP -qx '[0-9]+(:[0-9]+)?(,[0-9]+(:[0-9]+)?)*'
+ }
+
+ IptablesValidateAll()
+@@ -543,7 +618,7 @@
+ fi
+
+ # iptables v1.4.20+ is required to use -w (wait)
+-version=$(iptables -V | awk -F ' v' '{print $NF}')
++version=$(iptables -V | grep -oE '[0-9]+[\.0-9]+')
+ ocf_version_cmp "$version" "1.4.19.1"
+ if [ "$?" -eq "2" ]; then
+ wait="-w"
+@@ -553,21 +628,36 @@
+
+ protocol=$OCF_RESKEY_protocol
+ portno=$OCF_RESKEY_portno
++direction=$OCF_RESKEY_direction
+ action=$OCF_RESKEY_action
+ ip=$OCF_RESKEY_ip
+ reset_local_on_unblock_stop=$OCF_RESKEY_reset_local_on_unblock_stop
+
++
++# If "tickle" is enabled, we need to record the list of currently established
++# connections during monitor. Use ss where available, and netstat otherwise.
++if [ -n "$OCF_RESKEY_tickle_dir" ] ; then
++ if have_binary ss ; then
++ ss_or_netstat="ss -Htn"
++ elif have_binary netstat ; then
++ ss_or_netstat="netstat -tn"
++ else
++ ocf_log err "Neither ss nor netstat found, but needed to record estblished connections."
++ exit $OCF_ERR_INSTALLED
++ fi
++fi
++
+ case $1 in
+ start)
+- IptablesStart $protocol $portno $ip $action
++ IptablesStart $protocol $portno $ip $direction $action
+ ;;
+
+ stop)
+- IptablesStop $protocol $portno $ip $action
++ IptablesStop $protocol $portno $ip $direction $action
+ ;;
+
+ status|monitor)
+- IptablesStatus $protocol $portno $ip $action
++ IptablesStatus $protocol $portno $ip $direction $action
+ ;;
+
+ validate-all)
diff --git a/resource-agents.spec b/resource-agents.spec
index f9fe6fe462868829751113abc06946b20d4fe7f9..8e9a7e43146717c09e92bbacfeb7b071aca4ca94 100644
--- a/resource-agents.spec
+++ b/resource-agents.spec
@@ -73,7 +73,7 @@
Name: resource-agents
Summary: Open Source HA Reusable Cluster Resource Scripts
Version: 4.9.0
-Release: 54%{?rcver:%{rcver}}%{?numcomm:.%{numcomm}}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}}%{?dist}.8
+Release: 54%{?rcver:%{rcver}}%{?numcomm:.%{numcomm}}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}}%{?dist}.10
License: GPLv2+ and LGPLv2+
URL: https://github.com/ClusterLabs/resource-agents
%if 0%{?fedora} || 0%{?centos_version} || 0%{?rhel}
@@ -167,6 +167,7 @@ Patch70: RHEL-69297-1-Filesystem-dont-kill-unrelated-processes.patch
Patch71: RHEL-69297-2-Filesystem-update-bsd-logic.patch
Patch72: RHEL-72956-1-openstack-cinder-volume-wait-for-volume-to-be-available.patch
Patch73: RHEL-72956-2-openstack-cinder-volume-fix-detach-not-working-during-start-action.patch
+Patch74: RHEL-79823-portblock-fix-version-detection.patch
# bundle patches
Patch1000: 7-gcp-bundled.patch
@@ -424,6 +425,7 @@ exit 1
%patch -p1 -P 71
%patch -p1 -P 72
%patch -p1 -P 73
+%patch -p1 -P 74
chmod 755 heartbeat/nova-compute-wait
chmod 755 heartbeat/NovaEvacuate
@@ -1013,6 +1015,11 @@ ccs_update_schema > /dev/null 2>&1 ||:
%{_usr}/lib/ocf/lib/heartbeat/OCF_*.pm
%changelog
+* Thu Feb 20 2025 Oyvind Albrigtsen - 4.9.0-54.10
+- portblock: fix iptables version detection
+
+ Resolves: RHEL-79823
+
* Fri Jan 10 2025 Oyvind Albrigtsen - 4.9.0-54.8
- openstack-cinder-volume: wait for volume to be available