bridge tests: Test STP config BPDU validation
PR: 254924 Reviewed by: donner Differential Revision: https://reviews.freebsd.org/D29783
This commit is contained in:
parent
0e4025bffa
commit
4ae3a97e12
@ -1,6 +1,6 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.include <bsd.own.mk>
|
||||
PACKAGE= tests
|
||||
|
||||
TESTSDIR= ${TESTSBASE}/sys/net
|
||||
BINDIR= ${TESTSDIR}
|
||||
@ -20,6 +20,11 @@ TESTS_SUBDIRS+= routing
|
||||
# locked.
|
||||
TEST_METADATA+= is_exclusive=true
|
||||
|
||||
${PACKAGE}FILES+= \
|
||||
stp.py
|
||||
|
||||
${PACKAGE}FILESMODE_stp.py= 0555
|
||||
|
||||
MAN=
|
||||
PROGS+= randsleep
|
||||
|
||||
|
@ -408,6 +408,50 @@ inherit_mac_cleanup()
|
||||
vnet_cleanup
|
||||
}
|
||||
|
||||
atf_test_case "stp_validation" "cleanup"
|
||||
stp_validation_head()
|
||||
{
|
||||
atf_set descr 'Check STP validation'
|
||||
atf_set require.user root
|
||||
atf_set require.progs scapy
|
||||
}
|
||||
|
||||
stp_validation_body()
|
||||
{
|
||||
vnet_init
|
||||
|
||||
epair_one=$(vnet_mkepair)
|
||||
epair_two=$(vnet_mkepair)
|
||||
bridge=$(vnet_mkbridge)
|
||||
|
||||
ifconfig ${bridge} up
|
||||
ifconfig ${bridge} addm ${epair_one}a addm ${epair_two}a
|
||||
ifconfig ${bridge} stp ${epair_one}a stp ${epair_two}a
|
||||
|
||||
ifconfig ${epair_one}a up
|
||||
ifconfig ${epair_one}b up
|
||||
ifconfig ${epair_two}a up
|
||||
ifconfig ${epair_two}b up
|
||||
|
||||
# Wait until the interfaces are no longer discarding
|
||||
while ifconfig ${bridge} | grep 'state discarding' >/dev/null
|
||||
do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Now inject invalid STP BPDUs on epair_one and see if they're repeated
|
||||
# on epair_two
|
||||
atf_check -s exit:0 \
|
||||
$(atf_get_srcdir)/stp.py \
|
||||
--sendif ${epair_one}b \
|
||||
--recvif ${epair_two}b
|
||||
}
|
||||
|
||||
stp_validation_cleanup()
|
||||
{
|
||||
vnet_cleanup
|
||||
}
|
||||
|
||||
atf_init_test_cases()
|
||||
{
|
||||
atf_add_test_case "bridge_transmit_ipv4_unicast"
|
||||
@ -418,4 +462,5 @@ atf_init_test_cases()
|
||||
atf_add_test_case "inherit_mac"
|
||||
atf_add_test_case "delete_with_members"
|
||||
atf_add_test_case "mac_conflict"
|
||||
atf_add_test_case "stp_validation"
|
||||
}
|
||||
|
112
tests/sys/net/stp.py
Normal file
112
tests/sys/net/stp.py
Normal file
@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
#
|
||||
# Copyright (c) 2021 Kristof Provost <kp@FreeBSD.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
|
||||
import argparse
|
||||
import scapy.all as sp
|
||||
import sys
|
||||
import os
|
||||
curdir = os.path.dirname(os.path.realpath(__file__))
|
||||
netpfil_common = curdir + "/../netpfil/common"
|
||||
sys.path.append(netpfil_common)
|
||||
from sniffer import Sniffer
|
||||
|
||||
def check_stp(args, packet):
|
||||
stp = packet.getlayer(sp.STP)
|
||||
if stp is None:
|
||||
return False
|
||||
|
||||
if stp.rootmac != "00:0c:29:01:01:01":
|
||||
return False
|
||||
|
||||
# Ensure we don't get confused by valid STP packets generated by if_bridge
|
||||
if (stp.maxage >= 6 and stp.maxage <= 40) and \
|
||||
(stp.hellotime >= 1 and stp.hellotime <= 2) and \
|
||||
(stp.fwddelay >= 4 and stp.fwddelay <= 30):
|
||||
return False
|
||||
|
||||
print("This packet should have been dropped")
|
||||
print(packet.show())
|
||||
return True
|
||||
|
||||
def invalid_stp(send_if):
|
||||
llc = sp.Ether(src="00:0c:29:0b:91:0a", dst="01:80:C2:00:00:00") \
|
||||
/ sp.LLC()
|
||||
|
||||
# Bad maxage
|
||||
stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \
|
||||
bridgeid=32768, bridgemac="00:0c:29:01:01:01", \
|
||||
portid=0x8007, maxage=41, hellotime=2, fwddelay=30)
|
||||
sp.sendp(stp, iface=send_if, verbose=False)
|
||||
stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \
|
||||
bridgeid=32768, bridgemac="00:0c:29:01:01:01", \
|
||||
portid=0x8007, maxage=5, hellotime=2, fwddelay=30)
|
||||
sp.sendp(stp, iface=send_if, verbose=False)
|
||||
|
||||
# Bad hellotime
|
||||
stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \
|
||||
bridgeid=32768, bridgemac="00:0c:29:01:01:01", \
|
||||
portid=0x8007, maxage=40, hellotime=3, fwddelay=30)
|
||||
sp.sendp(stp, iface=send_if, verbose=False)
|
||||
stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \
|
||||
bridgeid=32768, bridgemac="00:0c:29:01:01:01", \
|
||||
portid=0x8007, maxage=40, hellotime=1, fwddelay=30)
|
||||
sp.sendp(stp, iface=send_if, verbose=False)
|
||||
|
||||
# Bad fwddelay
|
||||
stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \
|
||||
bridgeid=32768, bridgemac="00:0c:29:01:01:01", \
|
||||
portid=0x8007, maxage=40, hellotime=2, fwddelay=31)
|
||||
sp.sendp(stp, iface=send_if, verbose=False)
|
||||
stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \
|
||||
bridgeid=32768, bridgemac="00:0c:29:01:01:01", \
|
||||
portid=0x8007, maxage=40, hellotime=2, fwddelay=3)
|
||||
sp.sendp(stp, iface=send_if, verbose=False)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser("stp.py",
|
||||
description="STP test tool")
|
||||
parser.add_argument('--sendif', nargs=1,
|
||||
required=True,
|
||||
help='The interface through which the packet(s) will be sent')
|
||||
parser.add_argument('--recvif', nargs=1,
|
||||
help='The interface on which to expect the ICMP echo request')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
sniffer = Sniffer(args, check_stp)
|
||||
|
||||
invalid_stp(args.sendif[0])
|
||||
|
||||
sniffer.join()
|
||||
|
||||
# The 'correct' packet is a corrupt STP packet, so it shouldn't turn up.
|
||||
if sniffer.foundCorrectPacket:
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in New Issue
Block a user