#!/bin/bash
# vim: dict+=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# runtest.sh of
# Description: Test that frr correctly changes inbound route filter after a config reload
# Author: Michal Ruprich <mruprich@redhat.com>
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# Copyright (c) 2019 Red Hat, Inc.
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/.
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Include Beaker environment
. /usr/share/beakerlib/beakerlib.sh || exit 1
PACKAGE="frr"
vnSIDE='server'
vnSERVER_IFACE='VNS'
vnCLIENT_IFACE='VNC'
vnSERVER_NAMESPACE='vns'
vnCLIENT_NAMESPACE='vnc'
#Very simple case where we need two routers R1 and R2 to establish neighborship and send some data
SRV_DUMMY1_IF_NAME="dummy1"
SRV_DUMMY1_IF_ADDR="192.168.212.1"
SRV_DUMMY1_IF_NETWORK="192.168.212.0"
SRV_DUMMY1_IF_PREFIX="24"
SRV_DUMMY1_IF_BCAST="192.168.212.255"
SERVER_IF_ADDR="192.168.222.240"
SERVER_IF_PREFIX="31"
SERVER_IF_BCAST="255.255.255.255"
SERVER_FRR_LOG="/var/log/frr/frr-r1.log"
SERVER_CONF_DIR="/etc/frr/vns/"
CLIENT_IF_ADDR="192.168.222.241"
CLIENT_IF_PREFIX="31"
CLIENT_IF_BCAST="255.255.255.255"
CLIENT_FRR_LOG="/var/log/frr/frr-r2.log"
CLIENT_CONF_DIR="/etc/frr/vnc/"
#These functions help with setting up the network namespaces
vnCreateServerClientNetwork()
{
rlRun "ip link add ${vnSERVER_IFACE} type veth peer name ${vnCLIENT_IFACE}" 0 "Creating network ifaces for SERVER: '${vnSERVER_IFACE}' and CLIENT: '${vnCLIENT_IFACE}'."
rlRun "ip netns add ${vnSERVER_NAMESPACE}" 0 "Creating SERVER namespace: '${vnSERVER_NAMESPACE}'."
rlRun "ip netns add ${vnCLIENT_NAMESPACE}" 0 "Creating CLIENT namespace: '${vnCLIENT_NAMESPACE}'."
rlRun "ip link set ${vnSERVER_IFACE} netns ${vnSERVER_NAMESPACE}" 0 "Adding iface: '${vnSERVER_IFACE}' into the namespace: '${vnSERVER_NAMESPACE}'."
rlRun "ip link set ${vnCLIENT_IFACE} netns ${vnCLIENT_NAMESPACE}" 0 "Adding iface: '${vnCLIENT_IFACE}' into the namespace: '${vnCLIENT_NAMESPACE}'."
}
vnRemoveServerClientNetwork()
{
rlRun "ip netns exec ${vnSERVER_NAMESPACE} ip link del ${vnSERVER_IFACE}" 0 "Removing network for SERVER and CLIENT."
rlRun "ip netns del ${vnSERVER_NAMESPACE}" 0 "Removing SERVER namespace: '${vnSERVER_NAMESPACE}'."
rlRun "ip netns del ${vnCLIENT_NAMESPACE}" 0 "Removing CLIENT namespace: '${vnCLIENT_NAMESPACE}'."
}
vnRunServer()
{
local command="$1"
local ret_val="${2:-0}"
local message="${3:-Running command on the SERVER: '${command}'}"
rlRun "ip netns exec ${vnSERVER_NAMESPACE} ${command}" "$ret_val" "$message"
}
vnRunClient()
{
local command="$1"
local ret_val="${2:-0}"
local message="${3:-Running command on the CLIENT: '${command}'}"
rlRun "ip netns exec ${vnCLIENT_NAMESPACE} ${command}" "$ret_val" "$message"
}
vnRun()
{
if [ "$vnSIDE" = 'server' ]; then
vnRunServer "$1" "$2" "$3"
elif [ "$vnSIDE" = 'client' ]; then
vnRunClient "$1" "$2" "$3"
else
rlLogError "'vnSIDE' variable is not set properly."
fi
}
rlJournalStart
rlPhaseStartSetup
rlAssertRpm $PACKAGE
# Need to disable SeLinux, because it does not allow to start service via unit file
# in a network namespace using "ip netns exec". And there are other issues with pid files, log files, etc.
rlRun "setenforce 0" 0 "Disabling SELinux"
rlRun "ORIG_AVC_ERROR=${AVC_ERROR}"
rlRun "AVC_ERROR=+no_avc_check"
# set up network
vnCreateServerClientNetwork
vnRunServer "ip link set ${vnSERVER_IFACE} up" 0 "Setting the SERVER side of veth UP"
vnRunServer "ip link set lo up" 0 "Setting the SERVER side loopback UP"
vnRunClient "ip link set ${vnCLIENT_IFACE} up" 0 "Setting the CLIENT side of veth UP"
vnRunClient "ip link set lo up" 0 "Setting the CLIENT side loopback UP"
vnRunServer "ip addr add ${SERVER_IF_ADDR}/${SERVER_IF_PREFIX} broadcast ${SERVER_IF_BCAST} dev ${vnSERVER_IFACE}" 0 "Configuring IPv4 address on SERVER side of veth"
# client IP is not configured here, as the only way how to trigger warning is to configure the IP while zebra is already running
vnRunServer "ip link add ${SRV_DUMMY1_IF_NAME} type dummy" 0 "Adding dummy interface on the SERVER side"
vnRunServer "ip link set ${SRV_DUMMY1_IF_NAME} up" 0 "Setting the dummy interface UP"
vnRunServer "ip addr add ${SRV_DUMMY1_IF_ADDR}/${SRV_DUMMY1_IF_PREFIX} dev ${SRV_DUMMY1_IF_NAME}" 0 "Configuring IPv4 address on dummy interface of the SERVER side"
rlFileBackup --clean "/etc/frr/"
rlFileBackup --clean "/etc/systemd/system/"
rlFileBackup --clean "/var/log/frr/"
rlFileBackup --clean "/var/log/audit/audit.log"
rlRun "mkdir /etc/frr/{vns,vnc}"
#vtysh.conf and frr.conf are in /etc/frr/vns for the server namespace
rlRun "cp -f vtysh-vns.conf ${SERVER_CONF_DIR}vtysh.conf" 0 "Copying vtysh configuration for the SERVER"
rlRun "cp -f frr-vns.conf ${SERVER_CONF_DIR}frr.conf" 0 "Copying frr configuration for the SERVER"
#vtysh.conf and frr.conf are in /etc/frr/vnc for the client namespace
rlRun "cp -f vtysh-vnc.conf ${CLIENT_CONF_DIR}vtysh.conf" 0 "Copying vtysh configuration for the CLIENT"
rlRun "cp -f frr-vnc.conf ${CLIENT_CONF_DIR}frr.conf" 0 "Copying frr configuration for the CLIENT"
rlRun "cp -f frr-vnc-reload.conf ${CLIENT_CONF_DIR}frr-vnc-reload.conf" 0 "Copying frr configuration for the CLIENT"
#I need separate daemons files as well for watchfrr options
rlRun "cp -f daemons-vns ${SERVER_CONF_DIR}daemons" 0 "Copying daemons file for the SERVER"
rlRun "cp -f daemons-vnc ${CLIENT_CONF_DIR}daemons" 0 "Copying daemons file for the CLIENT"
rlRun "ls -lR /etc/frr/*"
rlRun "cp -f frr-vn{s,c}.service /etc/systemd/system/" 0 "Copying custom unit files to run frr in network namespaces"
# /etc/frr/vns/frr.conf
rlRun "sed -i 's|<VNSIF>|${vnSERVER_IFACE}|g' ${SERVER_CONF_DIR}frr.conf"
rlRun "sed -i 's|<DUMMYIF1>|${SRV_DUMMY1_IF_NAME}|g' ${SERVER_CONF_DIR}frr.conf"
rlRun "sed -i 's|<SERVER_FRR_LOG>|${SERVER_FRR_LOG}|g' ${SERVER_CONF_DIR}frr.conf"
rlRun "sed -i 's|<SERVER_IF_ADDR>|${SERVER_IF_ADDR}|g' ${SERVER_CONF_DIR}frr.conf"
rlRun "sed -i 's|<SRV_DUMMY1_IF_NETWORK>|${SRV_DUMMY1_IF_NETWORK}|g' ${SERVER_CONF_DIR}frr.conf"
rlRun "sed -i 's|<SRV_DUMMY1_IF_PREFIX>|${SRV_DUMMY1_IF_PREFIX}|g' ${SERVER_CONF_DIR}frr.conf"
rlRun "sed -i 's|<CLIENT_IF_ADDR>|${CLIENT_IF_ADDR}|g' ${SERVER_CONF_DIR}frr.conf"
# /etc/frr/vnc/frr.conf
rlRun "sed -i 's|<CLIENT_FRR_LOG>|${CLIENT_FRR_LOG}|g' ${CLIENT_CONF_DIR}frr.conf"
rlRun "sed -i 's|<SERVER_IF_ADDR>|${SERVER_IF_ADDR}|g' ${CLIENT_CONF_DIR}frr.conf"
rlRun "sed -i 's|<CLIENT_IF_ADDR>|${CLIENT_IF_ADDR}|g' ${CLIENT_CONF_DIR}frr.conf"
# /etc/frr/vnc/frr-vnc-reload.conf
rlRun "sed -i 's|<CLIENT_FRR_LOG>|${CLIENT_FRR_LOG}|g' ${CLIENT_CONF_DIR}frr-vnc-reload.conf"
rlRun "sed -i 's|<SERVER_IF_ADDR>|${SERVER_IF_ADDR}|g' ${CLIENT_CONF_DIR}frr-vnc-reload.conf"
rlRun "sed -i 's|<CLIENT_IF_ADDR>|${CLIENT_IF_ADDR}|g' ${CLIENT_CONF_DIR}frr-vnc-reload.conf"
rlRun "systemctl daemon-reload"
rlPhaseEnd
rlPhaseStartTest
rlRun "systemctl start frr-vns.service" 0 "Starting FRR on SERVER side"
rlRun "systemctl start frr-vnc.service" 0 "Starting FRR on CLIENT side"
#vtysh also needs to run for a specific namespace
rlRun "vtysh -N vns -c 'sh run'"
rlRun "vtysh -N vnc -c 'sh run'"
vnRunClient "ip addr add ${CLIENT_IF_ADDR}/${CLIENT_IF_PREFIX} broadcast ${CLIENT_IF_BCAST} dev ${vnCLIENT_IFACE}"
vnRunServer "ping -c 1 ${CLIENT_IF_ADDR}" 0 "Testing that server can ping client IP"
vnRunClient "ping -c 1 ${SERVER_IF_ADDR}" 0 "Testing that client can ping server IP"
# THIS IS THE ONLY BUG THAT CAN BE REPRODUCED, everything else works...
rlAssertNotGrep "warning: interface ${vnCLIENT_IFACE} broadcast addr ${CLIENT_IF_BCAST}/${CLIENT_IF_PREFIX} != calculated ${SERVER_IF_ADDR}, routing protocols may malfunction" "${CLIENT_FRR_LOG}"
rlRun "sleep 30" 0 "Waiting for BGP peers to exchange routes and converge"
#First let's see that the neighborship is established
rlRun "vtysh -N vns -c 'show ip bgp nei' | grep \"BGP neighbor is ${CLIENT_IF_ADDR}\"" 0 "Show BGP neighborship on SERVER"
rlRun "vtysh -N vns -c 'show ip bgp nei' | grep \"BGP state = Established\"" 0 "BGP neighborship on SERVER is Established"
rlRun "vtysh -N vnc -c 'show ip bgp nei' | grep \"BGP neighbor is ${SERVER_IF_ADDR}\"" 0 "Show BGP neighborship on CLIENT"
rlRun "vtysh -N vnc -c 'show ip bgp nei' | grep \"BGP state = Established\"" 0 "GP neighborship on CLIENT is Established"
#At first, the route to the advertised network should be visible ONLY on the SERVER, CLIENT has filter
rlRun -s "vtysh -N vns -c 'show ip route' | grep \"${SRV_DUMMY1_IF_NETWORK}/${SRV_DUMMY1_IF_PREFIX} is directly connected\"" 0 "Show routes on SERVER"
rlRun -s "vtysh -N vnc -c 'show ip route' | grep -v \"${SRV_DUMMY1_IF_NETWORK}/${SRV_DUMMY1_IF_PREFIX}\"" 0 "Show routes on CLIENT"
#Reload CLIENT config with permit clause in the prefix-list
rlRun "/usr/libexec/frr/frr-reload.py -N vnc --reload ${CLIENT_CONF_DIR}frr-vnc-reload.conf"
rlRun "vtysh -N vnc -c 'sh ip route'"
rlRun "sleep 30" 0 "Waiting for the routes to exchange"
rlRun "vtysh -N vnc -c 'sh ip route'"
#Check that the route from SERVER is available
rlRun -s "vtysh -N vnc -c 'show ip route' | grep \"${SRV_DUMMY1_IF_NETWORK}/${SRV_DUMMY1_IF_PREFIX}\"" 0 "Show routes on CLIENT"
rlPhaseEnd
rlPhaseStartCleanup
rlRun "systemctl stop frr-vns.service frr-vnc.service" 0 "Stopping FRR"
vnRunServer "ip link del ${SRV_DUMMY1_IF_NAME} type dummy"
vnRemoveServerClientNetwork
rlFileRestore
rlRun "systemctl daemon-reload"
# restoring SELinux
rlRun "setenforce 1" 0 "re-Enabling SELinux"
rlRun "AVC_ERROR=${ORIG_AVC_ERROR}"
rlPhaseEnd
rlJournalPrintText
rlJournalEnd