#!/bin/sh
[ -n "$CTDB_BASE" ] || \
- export CTDB_BASE=$(cd -P $(dirname "$0") ; dirname "$PWD")
+ CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD")
+
+. "${CTDB_BASE}/functions"
-. $CTDB_BASE/functions
loadconfig
+# service_name is used by various functions
+# shellcheck disable=SC2034
service_name=per_ip_routing
# Do nothing if unconfigured
die "error: CTDB_PER_IP_ROUTING_CONF=$CTDB_PER_IP_ROUTING_CONF file not found"
fi
+service_state_dir=$(ctdb_setup_service_state_dir) || exit $?
+
######################################################################
ipv4_is_valid_addr()
_count=0
# Get the shell to break up the address into 1 word per octet
+ # Intentional word splitting here
+ # shellcheck disable=SC2086
for _o in $(export IFS="." ; echo $_ip) ; do
# The 2>/dev/null stops output from failures where an "octet"
# is not numeric. The test will still fail.
if ! [ 0 -le $_o -a $_o -le 255 ] 2>/dev/null ; then
return 1
fi
- _count=$(($_count + 1))
+ _count=$((_count + 1))
done
# A valid IPv4 address has 4 octets
# Convert the host address to an unsigned long by splitting out
# the octets and doing the math.
_host_ul=0
+ # Intentional word splitting here
+ # shellcheck disable=SC2086
for _o in $(export IFS="." ; echo $_host) ; do
- _host_ul=$(( ($_host_ul << 8) + $_o)) # work around Emacs color bug
+ _host_ul=$(( (_host_ul << 8) + _o)) # work around Emacs color bug
done
# Calculate the mask and apply it.
- _mask_ul=$(( 0xffffffff << (32 - $_maskbits) ))
- _net_ul=$(( $_host_ul & $_mask_ul ))
+ _mask_ul=$(( 0xffffffff << (32 - _maskbits) ))
+ _net_ul=$(( _host_ul & _mask_ul ))
# Now convert to a network address one byte at a time.
_net=""
for _o in $(seq 1 4) ; do
- _net="$(($_net_ul & 255))${_net:+.}${_net}"
- _net_ul=$(($_net_ul >> 8))
+ _net="$((_net_ul & 255))${_net:+.}${_net}"
+ _net_ul=$((_net_ul >> 8))
done
echo "${_net}/${_maskbits}"
######################################################################
+ensure_rt_tables ()
+{
+ rt_tables="$CTDB_SYS_ETCDIR/iproute2/rt_tables"
+ rt_tables_lock="${service_state_dir}/rt_tables_lock"
+
+ # This file should always exist. Even if this didn't exist on the
+ # system, adding a route will have created it. What if we startup
+ # and immediately shutdown? Let's be sure.
+ if [ ! -f "$rt_tables" ] ; then
+ mkdir -p "${rt_tables%/*}" # dirname
+ touch "$rt_tables"
+ fi
+}
+
# Setup a table id to use for the given IP. We don't need to know it,
# it just needs to exist in /etc/iproute2/rt_tables. Fail if no free
# table id could be found in the configured range.
{
_ip=$1
- _f="$CTDB_ETCDIR/iproute2/rt_tables"
- # This file should always exist, but...
- if [ ! -f "$_f" ] ; then
- mkdir -p $(dirname "$_f")
- touch "$_f"
- fi
+ ensure_rt_tables
# Maintain a table id for each IP address we've ever seen in
# rt_tables. We use a "ctdb." prefix on the label.
# range).
(
# Note that die() just gets us out of the subshell...
- flock --timeout 30 0 || \
- die "ensure_table_id_for_ip: failed to lock file $_f"
+ flock --timeout 30 9 || \
+ die "ensure_table_id_for_ip: failed to lock file $rt_tables"
- _new=$CTDB_PER_IP_ROUTING_TABLE_ID_LOW
+ _new="$CTDB_PER_IP_ROUTING_TABLE_ID_LOW"
while read _t _l ; do
# Skip comments
case "$_t" in
fi
# Potentially update the new table id to be used. The
# redirect stops error spam for a non-numeric value.
- if [ $_new -le $_t -a \
- $_t -le $CTDB_PER_IP_ROUTING_TABLE_ID_HIGH ] 2>/dev/null ; then
- _new=$(($_t + 1))
+ if [ "$_new" -le "$_t" -a \
+ "$_t" -le "$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH" ] 2>/dev/null ; then
+ _new=$((_t + 1))
fi
- done
+ done <"$rt_tables"
# If the new table id is legal then add it to the file and
# print it.
- if [ $_new -le $CTDB_PER_IP_ROUTING_TABLE_ID_HIGH ] ; then
- printf "%d\t%s\n" "$_new" "$_label" >>"$_f"
+ if [ "$_new" -le "$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH" ] ; then
+ printf "%d\t%s\n" "$_new" "$_label" >>"$rt_tables"
return 0
else
return 1
fi
- ) <"$_f"
+ ) 9>"$rt_tables_lock"
}
# Clean up all the table ids that we might own.
clean_up_table_ids ()
{
- _f="$CTDB_ETCDIR/iproute2/rt_tables"
- # Even if this didn't exist on the system, adding a route will
- # have created it. What if we startup and immediately shutdown?
- if [ ! -f "$_f" ] ; then
- mkdir -p $(dirname "$_f")
- touch "$_f"
- fi
+ ensure_rt_tables
(
# Note that die() just gets us out of the subshell...
- flock --timeout 30 0 || \
- die "clean_up_table_ids: failed to lock file $_f"
+ flock --timeout 30 9 || \
+ die "clean_up_table_ids: failed to lock file $rt_tables"
# Delete any items from the file that have a table id in our
# range or a label matching our label. Preserve comments.
- _tmp="${_f}.$$.ctdb"
+ _tmp="${rt_tables}.$$.ctdb"
awk -v min="$CTDB_PER_IP_ROUTING_TABLE_ID_LOW" \
-v max="$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH" \
-v pre="$table_id_prefix" \
- '/^#/ || \
- !(min <= $1 && $1 <= max) && \
- !(index($2, pre) == 1) \
- { print $0 }' "$_f" >"$_tmp"
-
- mv "$_tmp" "$_f"
- # The lock is gone - don't do anything else here
- ) <"$_f"
+ '/^#/ ||
+ !(min <= $1 && $1 <= max) &&
+ !(index($2, pre) == 1)
+ { print $0 }' "$rt_tables" >"$_tmp"
+
+ mv "$_tmp" "$rt_tables"
+ ) 9>"$rt_tables_lock"
}
######################################################################
# that we get the maskbits as item #2 without further parsing.
while IFS="/$IFS" read _i _maskbits _x ; do
if [ "$_ip" = "$_i" ] ; then
- echo -n "$_ip "; ipv4_host_addr_to_net "$_ip" "$_maskbits"
+ printf "%s" "$_ip "; ipv4_host_addr_to_net "$_ip" "$_maskbits"
fi
done <"${CTDB_PUBLIC_ADDRESSES:-/dev/null}"
else
{
_ip="$1"
- [ -n "$(get_config_for_ip $_ip)" ]
+ _conf=$(get_config_for_ip "$_ip")
+ [ -n "$_conf" ]
}
add_routing_for_ip ()
get_config_for_ip "$_ip" |
while read _i _dest _gw ; do
_r="$_dest ${_gw:+via} $_gw dev $_iface table $_table_id"
+ # Intentionally unquoted multi-word value here
+ # shellcheck disable=SC2086
ip route add $_r || \
die "add_routing_for_ip: failed to add route: $_r"
done
# is invalid. Therefore, go to a little bit of trouble to indent
# the failure message so that it is associated with the above
# warning message and doesn't look too nasty.
- ip route flush table $_table_id 2>&1 | sed -e 's@^.@ &@'
+ ip route flush table "$_table_id" 2>&1 | sed -e 's@^.@ &@'
}
######################################################################
# routes.
add_missing_routes ()
{
- ctdb ip -v -Y | {
+ $CTDB ip -v -X | {
read _x # skip header line
# Read the rest of the lines. We're only interested in the
# non-local addresses. For each IP local address we check if
# the relevant routing table is populated and populate it if
# not.
- while IFS=":" read _x _ip _x _iface _x ; do
+ while IFS="|" read _x _ip _x _iface _x ; do
[ -n "$_iface" ] || continue
-
+
_table_id="${table_id_prefix}${_ip}"
- if [ -z "$(ip route show table $_table_id 2>/dev/null)" -o \
+ if [ -z "$(ip route show table "$_table_id" 2>/dev/null)" -o \
"$1" = "force" ] ; then
add_routing_for_ip "$_iface" "$_ip"
fi
remove_bogus_routes ()
{
# Get a IPs current hosted by this node, each anchored with '@'.
- _ips=$(ctdb ip -v -Y | awk -F: 'NR > 1 && $4 != "" {printf "@%s@\n", $2}')
+ _ips=$($CTDB ip -v -X | awk -F'|' 'NR > 1 && $4 != "" {printf "@%s@\n", $2}')
+ # x is intentionally ignored
+ # shellcheck disable=SC2034
ip rule show |
while read _p _x _i _x _t ; do
# Remove trailing colon after priority/preference.
ctdb_check_args "$@"
-ctdb_service_check_reconfigure
-
case "$1" in
- startup)
+startup)
flush_rules_and_routes
# make sure that we only respond to ARP messages from the NIC
}
;;
- shutdown)
+shutdown)
flush_rules_and_routes
clean_up_table_ids
;;
- takeip)
+takeip)
iface=$2
ip=$3
+ # maskbits included here so argument order is obvious
+ # shellcheck disable=SC2034
maskbits=$4
ensure_ipv4_is_valid_addr "$1" "$ip"
# flush our route cache
set_proc sys/net/ipv4/route/flush 1
- ctdb gratiousarp "$ip" "$iface"
+ $CTDB gratarp "$ip" "$iface"
;;
- updateip)
+updateip)
+ # oiface, maskbits included here so argument order is obvious
+ # shellcheck disable=SC2034
oiface=$2
niface=$3
ip=$4
+ # shellcheck disable=SC2034
maskbits=$5
ensure_ipv4_is_valid_addr "$1" "$ip"
# flush our route cache
set_proc sys/net/ipv4/route/flush 1
- ctdb gratiousarp "$ip" "$niface"
+ $CTDB gratarp "$ip" "$niface"
tickle_tcp_connections "$ip"
;;
- releaseip)
+releaseip)
iface=$2
ip=$3
+ # maskbits included here so argument order is obvious
+ # shellcheck disable=SC2034
maskbits=$4
ensure_ipv4_is_valid_addr "$1" "$ip"
del_routing_for_ip "$ip"
;;
- ipreallocated)
+ipreallocated)
add_missing_routes
remove_bogus_routes
;;
- *)
- ctdb_standard_event_handler "$@"
+reconfigure)
+ ctdb_service_reconfigure
;;
esac