#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Author: Taehee Yoo <[email protected]>
#
# This script evaluates the AMT driver.
# There are four network-namespaces, LISTENER, SOURCE, GATEWAY, RELAY.
# The role of LISTENER is to listen multicast traffic.
# In order to do that, it send IGMP group join message.
# The role of SOURCE is to send multicast traffic to listener.
# The role of GATEWAY is to work Gateway role of AMT interface.
# The role of RELAY is to work Relay role of AMT interface.
#
#
# +------------------------+
# | LISTENER netns |
# | |
# | +------------------+ |
# | | l_gw | |
# | | 192.168.0.2/24 | |
# | | 2001:db8::2/64 | |
# | +------------------+ |
# | . |
# +------------------------+
# .
# .
# +-----------------------------------------------------+
# | . GATEWAY netns |
# | . |
# |+---------------------------------------------------+|
# || . br0 ||
# || +------------------+ +------------------+ ||
# || | gw_l | | amtg | ||
# || | 192.168.0.1/24 | +--------+---------+ ||
# || | 2001:db8::1/64 | | ||
# || +------------------+ | ||
# |+-------------------------------------|-------------+|
# | | |
# | +--------+---------+ |
# | | gw_relay | |
# | | 10.0.0.1/24 | |
# | +------------------+ |
# | . |
# +-----------------------------------------------------+
# .
# .
# +-----------------------------------------------------+
# | RELAY netns . |
# | +------------------+ |
# | | relay_gw | |
# | | 10.0.0.2/24 | |
# | +--------+---------+ |
# | | |
# | | |
# | +------------------+ +--------+---------+ |
# | | relay_src | | amtr | |
# | | 172.17.0.1/24 | +------------------+ |
# | | 2001:db8:3::1/64 | |
# | +------------------+ |
# | . |
# | . |
# +-----------------------------------------------------+
# .
# .
# +------------------------+
# | . |
# | +------------------+ |
# | | src_relay | |
# | | 172.17.0.2/24 | |
# | | 2001:db8:3::2/64 | |
# | +------------------+ |
# | SOURCE netns |
# +------------------------+
#==============================================================================
readonly LISTENER=$(mktemp -u listener-XXXXXXXX)
readonly GATEWAY=$(mktemp -u gateway-XXXXXXXX)
readonly RELAY=$(mktemp -u relay-XXXXXXXX)
readonly SOURCE=$(mktemp -u source-XXXXXXXX)
readonly SMCROUTEDIR="$(mktemp -d)"
ERR=4
err=0
exit_cleanup()
{
for ns in "$@"; do
ip netns delete "${ns}" 2>/dev/null || true
done
if [ -f "$SMCROUTEDIR/amt.pid" ]; then
smcpid=$(< $SMCROUTEDIR/amt.pid)
kill $smcpid
fi
rm -rf $SMCROUTEDIR
exit $ERR
}
create_namespaces()
{
ip netns add "${LISTENER}" || exit_cleanup
ip netns add "${GATEWAY}" || exit_cleanup "${LISTENER}"
ip netns add "${RELAY}" || exit_cleanup "${LISTENER}" "${GATEWAY}"
ip netns add "${SOURCE}" || exit_cleanup "${LISTENER}" "${GATEWAY}" \
"${RELAY}"
}
# The trap function handler
#
exit_cleanup_all()
{
exit_cleanup "${LISTENER}" "${GATEWAY}" "${RELAY}" "${SOURCE}"
}
setup_interface()
{
for ns in "${LISTENER}" "${GATEWAY}" "${RELAY}" "${SOURCE}"; do
ip -netns "${ns}" link set dev lo up
done;
ip link add l_gw type veth peer name gw_l
ip link add gw_relay type veth peer name relay_gw
ip link add relay_src type veth peer name src_relay
ip link set l_gw netns "${LISTENER}" up
ip link set gw_l netns "${GATEWAY}" up
ip link set gw_relay netns "${GATEWAY}" up
ip link set relay_gw netns "${RELAY}" up
ip link set relay_src netns "${RELAY}" up
ip link set src_relay netns "${SOURCE}" up mtu 1400
ip netns exec "${LISTENER}" ip a a 192.168.0.2/24 dev l_gw
ip netns exec "${LISTENER}" ip r a default via 192.168.0.1 dev l_gw
ip netns exec "${LISTENER}" ip a a 2001:db8::2/64 dev l_gw
ip netns exec "${LISTENER}" ip r a default via 2001:db8::1 dev l_gw
ip netns exec "${LISTENER}" ip a a 239.0.0.1/32 dev l_gw autojoin
ip netns exec "${LISTENER}" ip a a ff0e::5:6/128 dev l_gw autojoin
ip netns exec "${GATEWAY}" ip a a 192.168.0.1/24 dev gw_l
ip netns exec "${GATEWAY}" ip a a 2001:db8::1/64 dev gw_l
ip netns exec "${GATEWAY}" ip a a 10.0.0.1/24 dev gw_relay
ip netns exec "${GATEWAY}" ip link add br0 type bridge
ip netns exec "${GATEWAY}" ip link set br0 up
ip netns exec "${GATEWAY}" ip link set gw_l master br0
ip netns exec "${GATEWAY}" ip link set gw_l up
ip netns exec "${GATEWAY}" ip link add amtg master br0 type amt \
mode gateway local 10.0.0.1 discovery 10.0.0.2 dev gw_relay \
gateway_port 2268 relay_port 2268
ip netns exec "${RELAY}" ip a a 10.0.0.2/24 dev relay_gw
ip netns exec "${RELAY}" ip link add amtr type amt mode relay \
local 10.0.0.2 dev relay_gw relay_port 2268 max_tunnels 4
ip netns exec "${RELAY}" ip a a 172.17.0.1/24 dev relay_src
ip netns exec "${RELAY}" ip a a 2001:db8:3::1/64 dev relay_src
ip netns exec "${SOURCE}" ip a a 172.17.0.2/24 dev src_relay
ip netns exec "${SOURCE}" ip a a 2001:db8:3::2/64 dev src_relay
ip netns exec "${SOURCE}" ip r a default via 172.17.0.1 dev src_relay
ip netns exec "${SOURCE}" ip r a default via 2001:db8:3::1 dev src_relay
ip netns exec "${RELAY}" ip link set amtr up
ip netns exec "${GATEWAY}" ip link set amtg up
}
setup_sysctl()
{
ip netns exec "${RELAY}" sysctl net.ipv4.ip_forward=1 -w -q
}
setup_iptables()
{
ip netns exec "${RELAY}" iptables -t mangle -I PREROUTING \
-d 239.0.0.1 -j TTL --ttl-set 2
ip netns exec "${RELAY}" ip6tables -t mangle -I PREROUTING \
-j HL --hl-set 2
}
setup_mcast_routing()
{
ip netns exec "${RELAY}" smcrouted -P $SMCROUTEDIR/amt.pid
ip netns exec "${RELAY}" smcroutectl a relay_src \
172.17.0.2 239.0.0.1 amtr
ip netns exec "${RELAY}" smcroutectl a relay_src \
2001:db8:3::2 ff0e::5:6 amtr
}
test_remote_ip()
{
REMOTE=$(ip netns exec "${GATEWAY}" \
ip -d -j link show amtg | jq .[0].linkinfo.info_data.remote)
if [ $REMOTE == "\"10.0.0.2\"" ]; then
printf "TEST: %-60s [ OK ]\n" "amt discovery"
else
printf "TEST: %-60s [FAIL]\n" "amt discovery"
ERR=1
fi
}
send_mcast_torture4()
{
ip netns exec "${SOURCE}" bash -c \
'cat /dev/urandom | head -c 1G | nc -w 1 -u 239.0.0.1 4001'
}
send_mcast_torture6()
{
ip netns exec "${SOURCE}" bash -c \
'cat /dev/urandom | head -c 1G | nc -w 1 -u ff0e::5:6 6001'
}
check_features()
{
ip link help 2>&1 | grep -q amt
if [ $? -ne 0 ]; then
echo "Missing amt support in iproute2" >&2
exit_cleanup
fi
}
test_ipv4_forward()
{
RESULT4=$(ip netns exec "${LISTENER}" timeout 15 socat - UDP4-LISTEN:4000,readbytes=128 || true)
if echo "$RESULT4" | grep -q "172.17.0.2"; then
printf "TEST: %-60s [ OK ]\n" "IPv4 amt multicast forwarding"
exit 0
else
printf "TEST: %-60s [FAIL]\n" "IPv4 amt multicast forwarding"
exit 1
fi
}
test_ipv6_forward()
{
RESULT6=$(ip netns exec "${LISTENER}" timeout 15 socat - UDP6-LISTEN:6000,readbytes=128 || true)
if echo "$RESULT6" | grep -q "2001:db8:3::2"; then
printf "TEST: %-60s [ OK ]\n" "IPv6 amt multicast forwarding"
exit 0
else
printf "TEST: %-60s [FAIL]\n" "IPv6 amt multicast forwarding"
exit 1
fi
}
send_mcast4()
{
sleep 2
ip netns exec "${SOURCE}" bash -c \
'printf "%s %128s" 172.17.0.2 | nc -w 1 -u 239.0.0.1 4000' &
}
send_mcast6()
{
sleep 2
ip netns exec "${SOURCE}" bash -c \
'printf "%s %128s" 2001:db8:3::2 | nc -w 1 -u ff0e::5:6 6000' &
}
check_features
create_namespaces
set -e
trap exit_cleanup_all EXIT
setup_interface
setup_sysctl
setup_iptables
setup_mcast_routing
test_remote_ip
test_ipv4_forward &
pid=$!
send_mcast4
wait $pid || err=$?
if [ $err -eq 1 ]; then
ERR=1
fi
test_ipv6_forward &
pid=$!
send_mcast6
wait $pid || err=$?
if [ $err -eq 1 ]; then
ERR=1
fi
send_mcast_torture4
printf "TEST: %-60s [ OK ]\n" "IPv4 amt traffic forwarding torture"
send_mcast_torture6
printf "TEST: %-60s [ OK ]\n" "IPv6 amt traffic forwarding torture"
sleep 5
if [ "${ERR}" -eq 1 ]; then
echo "Some tests failed." >&2
else
ERR=0
fi