linux/tools/testing/selftests/net/forwarding/lib.sh
Amit Cohen 8f72a9cf36 selftests: forwarding: lib.sh: Add wait for dev with timeout
Add a function that waits for device with maximum number of iterations.
It enables to limit the waiting and prevent infinite loop.

This will be used by the subsequent patch which will set two ports to
different speeds in order to make sure they cannot negotiate a link.

Waiting for all the setup is limited with 10 minutes for each device.

Signed-off-by: Amit Cohen <amitc@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-18 17:11:54 -08:00

1068 lines
20 KiB
Bash

#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
##############################################################################
# Defines
# Can be overridden by the configuration file.
PING=${PING:=ping}
PING6=${PING6:=ping6}
MZ=${MZ:=mausezahn}
ARPING=${ARPING:=arping}
TEAMD=${TEAMD:=teamd}
WAIT_TIME=${WAIT_TIME:=5}
PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no}
NETIF_TYPE=${NETIF_TYPE:=veth}
NETIF_CREATE=${NETIF_CREATE:=yes}
MCD=${MCD:=smcrouted}
MC_CLI=${MC_CLI:=smcroutectl}
PING_TIMEOUT=${PING_TIMEOUT:=5}
WAIT_TIMEOUT=${WAIT_TIMEOUT:=20}
INTERFACE_TIMEOUT=${INTERFACE_TIMEOUT:=600}
relative_path="${BASH_SOURCE%/*}"
if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then
relative_path="."
fi
if [[ -f $relative_path/forwarding.config ]]; then
source "$relative_path/forwarding.config"
fi
##############################################################################
# Sanity checks
check_tc_version()
{
tc -j &> /dev/null
if [[ $? -ne 0 ]]; then
echo "SKIP: iproute2 too old; tc is missing JSON support"
exit 1
fi
}
check_tc_shblock_support()
{
tc filter help 2>&1 | grep block &> /dev/null
if [[ $? -ne 0 ]]; then
echo "SKIP: iproute2 too old; tc is missing shared block support"
exit 1
fi
}
check_tc_chain_support()
{
tc help 2>&1|grep chain &> /dev/null
if [[ $? -ne 0 ]]; then
echo "SKIP: iproute2 too old; tc is missing chain support"
exit 1
fi
}
if [[ "$(id -u)" -ne 0 ]]; then
echo "SKIP: need root privileges"
exit 0
fi
if [[ "$CHECK_TC" = "yes" ]]; then
check_tc_version
fi
require_command()
{
local cmd=$1; shift
if [[ ! -x "$(command -v "$cmd")" ]]; then
echo "SKIP: $cmd not installed"
exit 1
fi
}
require_command jq
require_command $MZ
if [[ ! -v NUM_NETIFS ]]; then
echo "SKIP: importer does not define \"NUM_NETIFS\""
exit 1
fi
##############################################################################
# Command line options handling
count=0
while [[ $# -gt 0 ]]; do
if [[ "$count" -eq "0" ]]; then
unset NETIFS
declare -A NETIFS
fi
count=$((count + 1))
NETIFS[p$count]="$1"
shift
done
##############################################################################
# Network interfaces configuration
create_netif_veth()
{
local i
for ((i = 1; i <= NUM_NETIFS; ++i)); do
local j=$((i+1))
ip link show dev ${NETIFS[p$i]} &> /dev/null
if [[ $? -ne 0 ]]; then
ip link add ${NETIFS[p$i]} type veth \
peer name ${NETIFS[p$j]}
if [[ $? -ne 0 ]]; then
echo "Failed to create netif"
exit 1
fi
fi
i=$j
done
}
create_netif()
{
case "$NETIF_TYPE" in
veth) create_netif_veth
;;
*) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
exit 1
;;
esac
}
if [[ "$NETIF_CREATE" = "yes" ]]; then
create_netif
fi
for ((i = 1; i <= NUM_NETIFS; ++i)); do
ip link show dev ${NETIFS[p$i]} &> /dev/null
if [[ $? -ne 0 ]]; then
echo "SKIP: could not find all required interfaces"
exit 1
fi
done
##############################################################################
# Helpers
# Exit status to return at the end. Set in case one of the tests fails.
EXIT_STATUS=0
# Per-test return value. Clear at the beginning of each test.
RET=0
check_err()
{
local err=$1
local msg=$2
if [[ $RET -eq 0 && $err -ne 0 ]]; then
RET=$err
retmsg=$msg
fi
}
check_fail()
{
local err=$1
local msg=$2
if [[ $RET -eq 0 && $err -eq 0 ]]; then
RET=1
retmsg=$msg
fi
}
check_err_fail()
{
local should_fail=$1; shift
local err=$1; shift
local what=$1; shift
if ((should_fail)); then
check_fail $err "$what succeeded, but should have failed"
else
check_err $err "$what failed"
fi
}
log_test()
{
local test_name=$1
local opt_str=$2
if [[ $# -eq 2 ]]; then
opt_str="($opt_str)"
fi
if [[ $RET -ne 0 ]]; then
EXIT_STATUS=1
printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str"
if [[ ! -z "$retmsg" ]]; then
printf "\t%s\n" "$retmsg"
fi
if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
echo "Hit enter to continue, 'q' to quit"
read a
[ "$a" = "q" ] && exit 1
fi
return 1
fi
printf "TEST: %-60s [ OK ]\n" "$test_name $opt_str"
return 0
}
log_info()
{
local msg=$1
echo "INFO: $msg"
}
setup_wait_dev()
{
local dev=$1; shift
local wait_time=${1:-$WAIT_TIME}; shift
setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time
if (($?)); then
check_err 1
log_test setup_wait_dev ": Interface $dev does not come up."
exit 1
fi
}
setup_wait_dev_with_timeout()
{
local dev=$1; shift
local max_iterations=${1:-$WAIT_TIMEOUT}; shift
local wait_time=${1:-$WAIT_TIME}; shift
local i
for ((i = 1; i <= $max_iterations; ++i)); do
ip link show dev $dev up \
| grep 'state UP' &> /dev/null
if [[ $? -ne 0 ]]; then
sleep 1
else
sleep $wait_time
return 0
fi
done
return 1
}
setup_wait()
{
local num_netifs=${1:-$NUM_NETIFS}
local i
for ((i = 1; i <= num_netifs; ++i)); do
setup_wait_dev ${NETIFS[p$i]} 0
done
# Make sure links are ready.
sleep $WAIT_TIME
}
cmd_jq()
{
local cmd=$1
local jq_exp=$2
local jq_opts=$3
local ret
local output
output="$($cmd)"
# it the command fails, return error right away
ret=$?
if [[ $ret -ne 0 ]]; then
return $ret
fi
output=$(echo $output | jq -r $jq_opts "$jq_exp")
ret=$?
if [[ $ret -ne 0 ]]; then
return $ret
fi
echo $output
# return success only in case of non-empty output
[ ! -z "$output" ]
}
lldpad_app_wait_set()
{
local dev=$1; shift
while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do
echo "$dev: waiting for lldpad to push pending APP updates"
sleep 5
done
}
lldpad_app_wait_del()
{
# Give lldpad a chance to push down the changes. If the device is downed
# too soon, the updates will be left pending. However, they will have
# been struck off the lldpad's DB already, so we won't be able to tell
# they are pending. Then on next test iteration this would cause
# weirdness as newly-added APP rules conflict with the old ones,
# sometimes getting stuck in an "unknown" state.
sleep 5
}
pre_cleanup()
{
if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
echo "Pausing before cleanup, hit any key to continue"
read
fi
}
vrf_prepare()
{
ip -4 rule add pref 32765 table local
ip -4 rule del pref 0
ip -6 rule add pref 32765 table local
ip -6 rule del pref 0
}
vrf_cleanup()
{
ip -6 rule add pref 0 table local
ip -6 rule del pref 32765
ip -4 rule add pref 0 table local
ip -4 rule del pref 32765
}
__last_tb_id=0
declare -A __TB_IDS
__vrf_td_id_assign()
{
local vrf_name=$1
__last_tb_id=$((__last_tb_id + 1))
__TB_IDS[$vrf_name]=$__last_tb_id
return $__last_tb_id
}
__vrf_td_id_lookup()
{
local vrf_name=$1
return ${__TB_IDS[$vrf_name]}
}
vrf_create()
{
local vrf_name=$1
local tb_id
__vrf_td_id_assign $vrf_name
tb_id=$?
ip link add dev $vrf_name type vrf table $tb_id
ip -4 route add table $tb_id unreachable default metric 4278198272
ip -6 route add table $tb_id unreachable default metric 4278198272
}
vrf_destroy()
{
local vrf_name=$1
local tb_id
__vrf_td_id_lookup $vrf_name
tb_id=$?
ip -6 route del table $tb_id unreachable default metric 4278198272
ip -4 route del table $tb_id unreachable default metric 4278198272
ip link del dev $vrf_name
}
__addr_add_del()
{
local if_name=$1
local add_del=$2
local array
shift
shift
array=("${@}")
for addrstr in "${array[@]}"; do
ip address $add_del $addrstr dev $if_name
done
}
__simple_if_init()
{
local if_name=$1; shift
local vrf_name=$1; shift
local addrs=("${@}")
ip link set dev $if_name master $vrf_name
ip link set dev $if_name up
__addr_add_del $if_name add "${addrs[@]}"
}
__simple_if_fini()
{
local if_name=$1; shift
local addrs=("${@}")
__addr_add_del $if_name del "${addrs[@]}"
ip link set dev $if_name down
ip link set dev $if_name nomaster
}
simple_if_init()
{
local if_name=$1
local vrf_name
local array
shift
vrf_name=v$if_name
array=("${@}")
vrf_create $vrf_name
ip link set dev $vrf_name up
__simple_if_init $if_name $vrf_name "${array[@]}"
}
simple_if_fini()
{
local if_name=$1
local vrf_name
local array
shift
vrf_name=v$if_name
array=("${@}")
__simple_if_fini $if_name "${array[@]}"
vrf_destroy $vrf_name
}
tunnel_create()
{
local name=$1; shift
local type=$1; shift
local local=$1; shift
local remote=$1; shift
ip link add name $name type $type \
local $local remote $remote "$@"
ip link set dev $name up
}
tunnel_destroy()
{
local name=$1; shift
ip link del dev $name
}
vlan_create()
{
local if_name=$1; shift
local vid=$1; shift
local vrf=$1; shift
local ips=("${@}")
local name=$if_name.$vid
ip link add name $name link $if_name type vlan id $vid
if [ "$vrf" != "" ]; then
ip link set dev $name master $vrf
fi
ip link set dev $name up
__addr_add_del $name add "${ips[@]}"
}
vlan_destroy()
{
local if_name=$1; shift
local vid=$1; shift
local name=$if_name.$vid
ip link del dev $name
}
team_create()
{
local if_name=$1; shift
local mode=$1; shift
require_command $TEAMD
$TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
for slave in "$@"; do
ip link set dev $slave down
ip link set dev $slave master $if_name
ip link set dev $slave up
done
ip link set dev $if_name up
}
team_destroy()
{
local if_name=$1; shift
$TEAMD -t $if_name -k
}
master_name_get()
{
local if_name=$1
ip -j link show dev $if_name | jq -r '.[]["master"]'
}
link_stats_get()
{
local if_name=$1; shift
local dir=$1; shift
local stat=$1; shift
ip -j -s link show dev $if_name \
| jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
}
link_stats_tx_packets_get()
{
link_stats_get $1 tx packets
}
link_stats_rx_errors_get()
{
link_stats_get $1 rx errors
}
tc_rule_stats_get()
{
local dev=$1; shift
local pref=$1; shift
local dir=$1; shift
tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \
| jq '.[1].options.actions[].stats.packets'
}
ethtool_stats_get()
{
local dev=$1; shift
local stat=$1; shift
ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
}
mac_get()
{
local if_name=$1
ip -j link show dev $if_name | jq -r '.[]["address"]'
}
bridge_ageing_time_get()
{
local bridge=$1
local ageing_time
# Need to divide by 100 to convert to seconds.
ageing_time=$(ip -j -d link show dev $bridge \
| jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
echo $((ageing_time / 100))
}
declare -A SYSCTL_ORIG
sysctl_set()
{
local key=$1; shift
local value=$1; shift
SYSCTL_ORIG[$key]=$(sysctl -n $key)
sysctl -qw $key=$value
}
sysctl_restore()
{
local key=$1; shift
sysctl -qw $key=${SYSCTL_ORIG["$key"]}
}
forwarding_enable()
{
sysctl_set net.ipv4.conf.all.forwarding 1
sysctl_set net.ipv6.conf.all.forwarding 1
}
forwarding_restore()
{
sysctl_restore net.ipv6.conf.all.forwarding
sysctl_restore net.ipv4.conf.all.forwarding
}
declare -A MTU_ORIG
mtu_set()
{
local dev=$1; shift
local mtu=$1; shift
MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
ip link set dev $dev mtu $mtu
}
mtu_restore()
{
local dev=$1; shift
ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
}
tc_offload_check()
{
local num_netifs=${1:-$NUM_NETIFS}
for ((i = 1; i <= num_netifs; ++i)); do
ethtool -k ${NETIFS[p$i]} \
| grep "hw-tc-offload: on" &> /dev/null
if [[ $? -ne 0 ]]; then
return 1
fi
done
return 0
}
trap_install()
{
local dev=$1; shift
local direction=$1; shift
# Some devices may not support or need in-hardware trapping of traffic
# (e.g. the veth pairs that this library creates for non-existent
# loopbacks). Use continue instead, so that there is a filter in there
# (some tests check counters), and so that other filters are still
# processed.
tc filter add dev $dev $direction pref 1 \
flower skip_sw action trap 2>/dev/null \
|| tc filter add dev $dev $direction pref 1 \
flower action continue
}
trap_uninstall()
{
local dev=$1; shift
local direction=$1; shift
tc filter del dev $dev $direction pref 1 flower
}
slow_path_trap_install()
{
# For slow-path testing, we need to install a trap to get to
# slow path the packets that would otherwise be switched in HW.
if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
trap_install "$@"
fi
}
slow_path_trap_uninstall()
{
if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
trap_uninstall "$@"
fi
}
__icmp_capture_add_del()
{
local add_del=$1; shift
local pref=$1; shift
local vsuf=$1; shift
local tundev=$1; shift
local filter=$1; shift
tc filter $add_del dev "$tundev" ingress \
proto ip$vsuf pref $pref \
flower ip_proto icmp$vsuf $filter \
action pass
}
icmp_capture_install()
{
__icmp_capture_add_del add 100 "" "$@"
}
icmp_capture_uninstall()
{
__icmp_capture_add_del del 100 "" "$@"
}
icmp6_capture_install()
{
__icmp_capture_add_del add 100 v6 "$@"
}
icmp6_capture_uninstall()
{
__icmp_capture_add_del del 100 v6 "$@"
}
__vlan_capture_add_del()
{
local add_del=$1; shift
local pref=$1; shift
local dev=$1; shift
local filter=$1; shift
tc filter $add_del dev "$dev" ingress \
proto 802.1q pref $pref \
flower $filter \
action pass
}
vlan_capture_install()
{
__vlan_capture_add_del add 100 "$@"
}
vlan_capture_uninstall()
{
__vlan_capture_add_del del 100 "$@"
}
__dscp_capture_add_del()
{
local add_del=$1; shift
local dev=$1; shift
local base=$1; shift
local dscp;
for prio in {0..7}; do
dscp=$((base + prio))
__icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
"skip_hw ip_tos $((dscp << 2))"
done
}
dscp_capture_install()
{
local dev=$1; shift
local base=$1; shift
__dscp_capture_add_del add $dev $base
}
dscp_capture_uninstall()
{
local dev=$1; shift
local base=$1; shift
__dscp_capture_add_del del $dev $base
}
dscp_fetch_stats()
{
local dev=$1; shift
local base=$1; shift
for prio in {0..7}; do
local dscp=$((base + prio))
local t=$(tc_rule_stats_get $dev $((dscp + 100)))
echo "[$dscp]=$t "
done
}
matchall_sink_create()
{
local dev=$1; shift
tc qdisc add dev $dev clsact
tc filter add dev $dev ingress \
pref 10000 \
matchall \
action drop
}
tests_run()
{
local current_test
for current_test in ${TESTS:-$ALL_TESTS}; do
$current_test
done
}
multipath_eval()
{
local desc="$1"
local weight_rp12=$2
local weight_rp13=$3
local packets_rp12=$4
local packets_rp13=$5
local weights_ratio packets_ratio diff
RET=0
if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
| bc -l)
else
weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
| bc -l)
fi
if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
check_err 1 "Packet difference is 0"
log_test "Multipath"
log_info "Expected ratio $weights_ratio"
return
fi
if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
| bc -l)
else
packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
| bc -l)
fi
diff=$(echo $weights_ratio - $packets_ratio | bc -l)
diff=${diff#-}
test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
check_err $? "Too large discrepancy between expected and measured ratios"
log_test "$desc"
log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
}
in_ns()
{
local name=$1; shift
ip netns exec $name bash <<-EOF
NUM_NETIFS=0
source lib.sh
$(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
EOF
}
##############################################################################
# Tests
ping_do()
{
local if_name=$1
local dip=$2
local args=$3
local vrf_name
vrf_name=$(master_name_get $if_name)
ip vrf exec $vrf_name \
$PING $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null
}
ping_test()
{
RET=0
ping_do $1 $2
check_err $?
log_test "ping$3"
}
ping6_do()
{
local if_name=$1
local dip=$2
local args=$3
local vrf_name
vrf_name=$(master_name_get $if_name)
ip vrf exec $vrf_name \
$PING6 $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null
}
ping6_test()
{
RET=0
ping6_do $1 $2
check_err $?
log_test "ping6$3"
}
learning_test()
{
local bridge=$1
local br_port1=$2 # Connected to `host1_if`.
local host1_if=$3
local host2_if=$4
local mac=de:ad:be:ef:13:37
local ageing_time
RET=0
bridge -j fdb show br $bridge brport $br_port1 \
| jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
check_fail $? "Found FDB record when should not"
# Disable unknown unicast flooding on `br_port1` to make sure
# packets are only forwarded through the port after a matching
# FDB entry was installed.
bridge link set dev $br_port1 flood off
tc qdisc add dev $host1_if ingress
tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
flower dst_mac $mac action drop
$MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
sleep 1
tc -j -s filter show dev $host1_if ingress \
| jq -e ".[] | select(.options.handle == 101) \
| select(.options.actions[0].stats.packets == 1)" &> /dev/null
check_fail $? "Packet reached second host when should not"
$MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
sleep 1
bridge -j fdb show br $bridge brport $br_port1 \
| jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
check_err $? "Did not find FDB record when should"
$MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
sleep 1
tc -j -s filter show dev $host1_if ingress \
| jq -e ".[] | select(.options.handle == 101) \
| select(.options.actions[0].stats.packets == 1)" &> /dev/null
check_err $? "Packet did not reach second host when should"
# Wait for 10 seconds after the ageing time to make sure FDB
# record was aged-out.
ageing_time=$(bridge_ageing_time_get $bridge)
sleep $((ageing_time + 10))
bridge -j fdb show br $bridge brport $br_port1 \
| jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
check_fail $? "Found FDB record when should not"
bridge link set dev $br_port1 learning off
$MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
sleep 1
bridge -j fdb show br $bridge brport $br_port1 \
| jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
check_fail $? "Found FDB record when should not"
bridge link set dev $br_port1 learning on
tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
tc qdisc del dev $host1_if ingress
bridge link set dev $br_port1 flood on
log_test "FDB learning"
}
flood_test_do()
{
local should_flood=$1
local mac=$2
local ip=$3
local host1_if=$4
local host2_if=$5
local err=0
# Add an ACL on `host2_if` which will tell us whether the packet
# was flooded to it or not.
tc qdisc add dev $host2_if ingress
tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
flower dst_mac $mac action drop
$MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
sleep 1
tc -j -s filter show dev $host2_if ingress \
| jq -e ".[] | select(.options.handle == 101) \
| select(.options.actions[0].stats.packets == 1)" &> /dev/null
if [[ $? -ne 0 && $should_flood == "true" || \
$? -eq 0 && $should_flood == "false" ]]; then
err=1
fi
tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
tc qdisc del dev $host2_if ingress
return $err
}
flood_unicast_test()
{
local br_port=$1
local host1_if=$2
local host2_if=$3
local mac=de:ad:be:ef:13:37
local ip=192.0.2.100
RET=0
bridge link set dev $br_port flood off
flood_test_do false $mac $ip $host1_if $host2_if
check_err $? "Packet flooded when should not"
bridge link set dev $br_port flood on
flood_test_do true $mac $ip $host1_if $host2_if
check_err $? "Packet was not flooded when should"
log_test "Unknown unicast flood"
}
flood_multicast_test()
{
local br_port=$1
local host1_if=$2
local host2_if=$3
local mac=01:00:5e:00:00:01
local ip=239.0.0.1
RET=0
bridge link set dev $br_port mcast_flood off
flood_test_do false $mac $ip $host1_if $host2_if
check_err $? "Packet flooded when should not"
bridge link set dev $br_port mcast_flood on
flood_test_do true $mac $ip $host1_if $host2_if
check_err $? "Packet was not flooded when should"
log_test "Unregistered multicast flood"
}
flood_test()
{
# `br_port` is connected to `host2_if`
local br_port=$1
local host1_if=$2
local host2_if=$3
flood_unicast_test $br_port $host1_if $host2_if
flood_multicast_test $br_port $host1_if $host2_if
}