Blob Blame History Raw
#!/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