From d3ba515d99551ee87096931b34a5b8d4222b385d Mon Sep 17 00:00:00 2001 From: Klement Sekera Date: Tue, 14 Feb 2017 03:09:17 +0100 Subject: BFD: respect remote demand mode Change-Id: I5063d31f5305c848043afb32fcacff6e61aed79f Signed-off-by: Klement Sekera --- src/vnet/bfd/bfd_main.c | 55 +++- test/bfd.py | 46 ++- test/test_bfd.py | 837 +++++++++++++++++++++++++++++------------------- 3 files changed, 590 insertions(+), 348 deletions(-) diff --git a/src/vnet/bfd/bfd_main.c b/src/vnet/bfd/bfd_main.c index 7e06962a785..8bb8de3311a 100644 --- a/src/vnet/bfd/bfd_main.c +++ b/src/vnet/bfd/bfd_main.c @@ -372,8 +372,10 @@ bfd_input_format_trace (u8 * s, va_list * args) if (t->len >= sizeof (bfd_pkt_t) && pkt->head.length >= sizeof (bfd_pkt_t)) { - s = format (s, " my discriminator: %u\n", pkt->my_disc); - s = format (s, " your discriminator: %u\n", pkt->your_disc); + s = format (s, " my discriminator: %u\n", + clib_net_to_host_u32 (pkt->my_disc)); + s = format (s, " your discriminator: %u\n", + clib_net_to_host_u32 (pkt->your_disc)); s = format (s, " desired min tx interval: %u\n", clib_net_to_host_u32 (pkt->des_min_tx)); s = format (s, " required min rx interval: %u\n", @@ -381,6 +383,33 @@ bfd_input_format_trace (u8 * s, va_list * args) s = format (s, " required min echo rx interval: %u", clib_net_to_host_u32 (pkt->req_min_echo_rx)); } + if (t->len >= sizeof (bfd_pkt_with_common_auth_t) && + pkt->head.length >= sizeof (bfd_pkt_with_common_auth_t) && + bfd_pkt_get_auth_present (pkt)) + { + const bfd_pkt_with_common_auth_t *with_auth = (void *) pkt; + const bfd_auth_common_t *common = &with_auth->common_auth; + s = format (s, "\n auth len: %u\n", common->len); + s = format (s, " auth type: %u:%s\n", common->type, + bfd_auth_type_str (common->type)); + if (t->len >= sizeof (bfd_pkt_with_sha1_auth_t) && + pkt->head.length >= sizeof (bfd_pkt_with_sha1_auth_t) && + (BFD_AUTH_TYPE_keyed_sha1 == common->type || + BFD_AUTH_TYPE_meticulous_keyed_sha1 == common->type)) + { + const bfd_pkt_with_sha1_auth_t *with_sha1 = (void *) pkt; + const bfd_auth_sha1_t *sha1 = &with_sha1->sha1_auth; + s = format (s, " seq num: %u\n", + clib_net_to_host_u32 (sha1->seq_num)); + s = format (s, " key id: %u\n", sha1->key_id); + s = format (s, " hash: %U", format_hex_bytes, sha1->hash, + sizeof (sha1->hash)); + } + } + else + { + s = format (s, "\n"); + } } return s; @@ -598,17 +627,25 @@ bfd_send_periodic (vlib_main_t * vm, vlib_node_runtime_t * rt, "frame"); return; } - /* FIXME - A system MUST NOT periodically transmit BFD Control packets if Demand - mode is active on the remote system (bfd.RemoteDemandMode is 1, - bfd.SessionState is Up, and bfd.RemoteSessionState is Up) and a Poll - Sequence is not being transmitted. - */ + if (POLL_NOT_NEEDED == bs->poll_state && bs->remote_demand && + BFD_STATE_up == bs->local_state && BFD_STATE_up == bs->remote_state) + { + /* + * A system MUST NOT periodically transmit BFD Control packets if Demand + * mode is active on the remote system (bfd.RemoteDemandMode is 1, + * bfd.SessionState is Up, and bfd.RemoteSessionState is Up) and a Poll + * Sequence is not being transmitted. + */ + BFD_DBG ("bfd.RemoteDemand is non-zero, not sending periodic control " + "frame"); + return; + } /* sometimes the wheel expires an event a bit sooner than requested, account for that here */ if (now + bm->wheel_inaccuracy >= bs->tx_timeout_clocks) { - BFD_DBG ("Send periodic control frame for bs_idx=%lu", bs->bs_idx); + BFD_DBG ("Send periodic control frame for bs_idx=%lu: %U", bs->bs_idx, + format_bfd_session, bs); vlib_buffer_t *b = bfd_create_frame_to_next_node (vm, bs); if (!b) { diff --git a/test/bfd.py b/test/bfd.py index dc6f9674c00..09a7681cea9 100644 --- a/test/bfd.py +++ b/test/bfd.py @@ -1,10 +1,13 @@ +""" BFD protocol implementation """ + from random import randint -from socket import AF_INET, AF_INET6 -from scapy.all import * -from scapy.packet import * -from scapy.fields import * -from framework import * -from vpp_object import * +from socket import AF_INET, AF_INET6, inet_pton +from scapy.all import bind_layers +from scapy.layers.inet import UDP +from scapy.packet import Packet +from scapy.fields import BitField, BitEnumField, XByteField, FlagsField,\ + ConditionalField, StrField +from vpp_object import VppObject from util import NumericConstant @@ -77,28 +80,34 @@ class BFDAuthType(NumericConstant): def bfd_is_auth_used(pkt): + """ is packet authenticated? """ return "A" in pkt.sprintf("%BFD.flags%") def bfd_is_simple_pwd_used(pkt): + """ is simple password authentication used? """ return bfd_is_auth_used(pkt) and pkt.auth_type == BFDAuthType.simple_pwd def bfd_is_sha1_used(pkt): + """ is sha1 authentication used? """ return bfd_is_auth_used(pkt) and pkt.auth_type in \ (BFDAuthType.keyed_sha1, BFDAuthType.meticulous_keyed_sha1) def bfd_is_md5_used(pkt): + """ is md5 authentication used? """ return bfd_is_auth_used(pkt) and pkt.auth_type in \ (BFDAuthType.keyed_md5, BFDAuthType.meticulous_keyed_md5) def bfd_is_md5_or_sha1_used(pkt): + """ is md5 or sha1 used? """ return bfd_is_md5_used(pkt) or bfd_is_sha1_used(pkt) class BFD(Packet): + """ BFD protocol layer for scapy """ udp_dport = 3784 #: BFD destination port per RFC 5881 udp_sport_min = 49152 #: BFD source port min value per RFC 5881 @@ -164,10 +173,12 @@ class VppBFDAuthKey(VppObject): @property def key(self): + """ key data """ return self._key @property def conf_key_id(self): + """ configuration key ID """ return self._conf_key_id def add_vpp_config(self): @@ -206,8 +217,12 @@ class VppBFDUDPSession(VppObject): self._interface = interface self._af = af self._local_addr = local_addr + if local_addr is not None: + self._local_addr_n = inet_pton(af, local_addr) + else: + self._local_addr_n = None self._peer_addr = peer_addr - self._peer_addr_n = socket.inet_pton(af, peer_addr) + self._peer_addr_n = inet_pton(af, peer_addr) self._desired_min_tx = desired_min_tx self._required_min_rx = required_min_rx self._detect_mult = detect_mult @@ -238,7 +253,7 @@ class VppBFDUDPSession(VppObject): elif self.af == AF_INET6: return self._interface.local_ip6 else: - raise Exception("Unexpected af %s' % af" % self.af) + raise Exception("Unexpected af '%s'" % self.af) return self._local_addr @property @@ -250,7 +265,7 @@ class VppBFDUDPSession(VppObject): elif self.af == AF_INET6: return self._interface.local_ip6n else: - raise Exception("Unexpected af %s' % af" % self.af) + raise Exception("Unexpected af '%s'" % self.af) return self._local_addr_n @property @@ -264,6 +279,7 @@ class VppBFDUDPSession(VppObject): return self._peer_addr_n def get_bfd_udp_session_dump_entry(self): + """ get the namedtuple entry from bfd udp session dump """ result = self.test.vapi.bfd_udp_session_dump() for s in result: self.test.logger.debug("session entry: %s" % str(s)) @@ -285,31 +301,36 @@ class VppBFDUDPSession(VppObject): """ BFD session state """ session = self.get_bfd_udp_session_dump_entry() if session is None: - raise Exception("Could not find BFD session in VPP response: %s" % - repr(result)) + raise Exception("Could not find BFD session in VPP response") return session.state @property def desired_min_tx(self): + """ desired minimum tx interval """ return self._desired_min_tx @property def required_min_rx(self): + """ required minimum rx interval """ return self._required_min_rx @property def detect_mult(self): + """ detect multiplier """ return self._detect_mult @property def sha1_key(self): + """ sha1 key """ return self._sha1_key @property def bfd_key_id(self): + """ bfd key id in use """ return self._bfd_key_id def activate_auth(self, key, bfd_key_id=None, delayed=False): + """ activate authentication for this session """ self._bfd_key_id = bfd_key_id if bfd_key_id else randint(0, 255) self._sha1_key = key is_ipv6 = 1 if AF_INET6 == self.af else 0 @@ -324,6 +345,7 @@ class VppBFDUDPSession(VppObject): is_delayed=is_delayed) def deactivate_auth(self, delayed=False): + """ deactivate authentication """ self._bfd_key_id = None self._sha1_key = None is_delayed = 1 if delayed else 0 @@ -338,6 +360,7 @@ class VppBFDUDPSession(VppObject): detect_mult=None, desired_min_tx=None, required_min_rx=None): + """ modify session parameters """ if detect_mult: self._detect_mult = detect_mult if desired_min_tx: @@ -389,6 +412,7 @@ class VppBFDUDPSession(VppObject): return self.object_id() def admin_up(self): + """ set bfd session admin-up """ is_ipv6 = 1 if AF_INET6 == self._af else 0 self.test.vapi.bfd_udp_session_set_flags(1, self._interface.sw_if_index, diff --git a/test/test_bfd.py b/test/test_bfd.py index b4f082a5420..0ba0b46dee8 100644 --- a/test/test_bfd.py +++ b/test/test_bfd.py @@ -1,16 +1,23 @@ #!/usr/bin/env python +""" BFD tests """ from __future__ import division import unittest import hashlib import binascii import time -from random import randint -from bfd import * -from framework import * +from random import randint, shuffle +from socket import AF_INET, AF_INET6 +from scapy.layers.l2 import Ether +from scapy.layers.inet import UDP, IP +from scapy.layers.inet6 import IPv6 +from bfd import VppBFDAuthKey, BFD, BFDAuthType, VppBFDUDPSession, \ + BFDDiagCode, BFDState +from framework import VppTestCase, VppTestRunner +from vpp_pg_interface import CaptureTimeoutError from util import ppp -us_in_sec = 1000000 +USEC_IN_SEC = 1000000 class AuthKeyFactory(object): @@ -20,11 +27,12 @@ class AuthKeyFactory(object): self._conf_key_ids = {} def create_random_key(self, test, auth_type=BFDAuthType.keyed_sha1): + """ create a random key with unique conf key id """ conf_key_id = randint(0, 0xFFFFFFFF) while conf_key_id in self._conf_key_ids: conf_key_id = randint(0, 0xFFFFFFFF) self._conf_key_ids[conf_key_id] = 1 - key = str(bytearray([randint(0, 255) for j in range(randint(1, 20))])) + key = str(bytearray([randint(0, 255) for _ in range(randint(1, 20))])) return VppBFDAuthKey(test=test, auth_type=auth_type, conf_key_id=conf_key_id, key=key) @@ -32,6 +40,9 @@ class AuthKeyFactory(object): class BFDAPITestCase(VppTestCase): """Bidirectional Forwarding Detection (BFD) - API""" + pg0 = None + pg1 = None + @classmethod def setUpClass(cls): super(BFDAPITestCase, cls).setUpClass() @@ -55,10 +66,10 @@ class BFDAPITestCase(VppTestCase): """ create a BFD session """ session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) session.add_vpp_config() - self.logger.debug("Session state is %s" % str(session.state)) + self.logger.debug("Session state is %s", session.state) session.remove_vpp_config() session.add_vpp_config() - self.logger.debug("Session state is %s" % str(session.state)) + self.logger.debug("Session state is %s", session.state) session.remove_vpp_config() def test_double_add(self): @@ -76,10 +87,10 @@ class BFDAPITestCase(VppTestCase): session = VppBFDUDPSession( self, self.pg0, self.pg0.remote_ip6, af=AF_INET6) session.add_vpp_config() - self.logger.debug("Session state is %s" % str(session.state)) + self.logger.debug("Session state is %s", session.state) session.remove_vpp_config() session.add_vpp_config() - self.logger.debug("Session state is %s" % str(session.state)) + self.logger.debug("Session state is %s", session.state) session.remove_vpp_config() def test_mod_bfd(self): @@ -89,25 +100,25 @@ class BFDAPITestCase(VppTestCase): required_min_rx=10000, detect_mult=1) session.add_vpp_config() - e = session.get_bfd_udp_session_dump_entry() + s = session.get_bfd_udp_session_dump_entry() self.assert_equal(session.desired_min_tx, - e.desired_min_tx, + s.desired_min_tx, "desired min transmit interval") self.assert_equal(session.required_min_rx, - e.required_min_rx, + s.required_min_rx, "required min receive interval") - self.assert_equal(session.detect_mult, e.detect_mult, "detect mult") + self.assert_equal(session.detect_mult, s.detect_mult, "detect mult") session.modify_parameters(desired_min_tx=session.desired_min_tx * 2, required_min_rx=session.required_min_rx * 2, detect_mult=session.detect_mult * 2) - e = session.get_bfd_udp_session_dump_entry() + s = session.get_bfd_udp_session_dump_entry() self.assert_equal(session.desired_min_tx, - e.desired_min_tx, + s.desired_min_tx, "desired min transmit interval") self.assert_equal(session.required_min_rx, - e.required_min_rx, + s.required_min_rx, "required min receive interval") - self.assert_equal(session.detect_mult, e.detect_mult, "detect mult") + self.assert_equal(session.detect_mult, s.detect_mult, "detect mult") def test_add_sha1_keys(self): """ add SHA1 keys """ @@ -122,7 +133,7 @@ class BFDAPITestCase(VppTestCase): self.assertTrue(key.query_vpp_config()) # remove randomly indexes = range(key_count) - random.shuffle(indexes) + shuffle(indexes) removed = [] for i in indexes: key = keys[i] @@ -154,10 +165,10 @@ class BFDAPITestCase(VppTestCase): session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, sha1_key=key) session.add_vpp_config() - self.logger.debug("Session state is %s" % str(session.state)) + self.logger.debug("Session state is %s", session.state) session.remove_vpp_config() session.add_vpp_config() - self.logger.debug("Session state is %s" % str(session.state)) + self.logger.debug("Session state is %s", session.state) session.remove_vpp_config() def test_double_add_sha1(self): @@ -170,7 +181,7 @@ class BFDAPITestCase(VppTestCase): with self.assertRaises(Exception): session.add_vpp_config() - def test_add_authenticated_with_nonexistent_key(self): + def test_add_auth_nonexistent_key(self): """ create BFD session using non-existent SHA1 (negative case) """ session = VppBFDUDPSession( self, self.pg0, self.pg0.remote_ip4, @@ -239,32 +250,94 @@ class BFDTestSession(object): """ BFD session as seen from test framework side """ def __init__(self, test, interface, af, detect_mult=3, sha1_key=None, - bfd_key_id=None, our_seq_number=0xFFFFFFFF - 4): + bfd_key_id=None, our_seq_number=None): self.test = test self.af = af self.sha1_key = sha1_key self.bfd_key_id = bfd_key_id self.interface = interface - self.udp_sport = 50000 - self.our_seq_number = our_seq_number + self.udp_sport = randint(49152, 65535) + if our_seq_number is None: + self.our_seq_number = randint(0, 40000000) + else: + self.our_seq_number = our_seq_number self.vpp_seq_number = None - self.bfd_values = { - 'my_discriminator': 0, - 'desired_min_tx_interval': 100000, - 'detect_mult': detect_mult, - 'diag': BFDDiagCode.no_diagnostic, - } + self.my_discriminator = 0 + self.desired_min_tx = 100000 + self.required_min_rx = 100000 + self.detect_mult = detect_mult + self.diag = BFDDiagCode.no_diagnostic + self.your_discriminator = None + self.state = BFDState.down + self.auth_type = BFDAuthType.no_auth def inc_seq_num(self): + """ increment sequence number, wrapping if needed """ if self.our_seq_number == 0xFFFFFFFF: self.our_seq_number = 0 else: self.our_seq_number += 1 - def update(self, **kwargs): - self.bfd_values.update(kwargs) + def update(self, my_discriminator=None, your_discriminator=None, + desired_min_tx=None, required_min_rx=None, detect_mult=None, + diag=None, state=None, auth_type=None): + """ update BFD parameters associated with session """ + if my_discriminator: + self.my_discriminator = my_discriminator + if your_discriminator: + self.your_discriminator = your_discriminator + if required_min_rx: + self.required_min_rx = required_min_rx + if desired_min_tx: + self.desired_min_tx = desired_min_tx + if detect_mult: + self.detect_mult = detect_mult + if diag: + self.diag = diag + if state: + self.state = state + if auth_type: + self.auth_type = auth_type + + def fill_packet_fields(self, packet): + """ set packet fields with known values in packet """ + bfd = packet[BFD] + if self.my_discriminator: + self.test.logger.debug("BFD: setting packet.my_discriminator=%s", + self.my_discriminator) + bfd.my_discriminator = self.my_discriminator + if self.your_discriminator: + self.test.logger.debug("BFD: setting packet.your_discriminator=%s", + self.your_discriminator) + bfd.your_discriminator = self.your_discriminator + if self.required_min_rx: + self.test.logger.debug( + "BFD: setting packet.required_min_rx_interval=%s", + self.required_min_rx) + bfd.required_min_rx_interval = self.required_min_rx + if self.desired_min_tx: + self.test.logger.debug( + "BFD: setting packet.desired_min_tx_interval=%s", + self.desired_min_tx) + bfd.desired_min_tx_interval = self.desired_min_tx + if self.detect_mult: + self.test.logger.debug( + "BFD: setting packet.detect_mult=%s", self.detect_mult) + bfd.detect_mult = self.detect_mult + if self.diag: + self.test.logger.debug("BFD: setting packet.diag=%s", self.diag) + bfd.diag = self.diag + if self.state: + self.test.logger.debug("BFD: setting packet.state=%s", self.state) + bfd.state = self.state + if self.auth_type: + # this is used by a negative test-case + self.test.logger.debug("BFD: setting packet.auth_type=%s", + self.auth_type) + bfd.auth_type = self.auth_type def create_packet(self): + """ create a BFD packet, reflecting the current state of session """ if self.sha1_key: bfd = BFD(flags="A") bfd.auth_type = self.sha1_key.auth_type @@ -291,9 +364,7 @@ class BFDTestSession(object): UDP(sport=self.udp_sport, dport=BFD.udp_dport) / bfd) self.test.logger.debug("BFD: Creating packet") - for name, value in self.bfd_values.iteritems(): - self.test.logger.debug("BFD: setting packet.%s=%s", name, value) - packet[BFD].setfieldval(name, value) + self.fill_packet_fields(packet) if self.sha1_key: hash_material = str(packet[BFD])[:32] + self.sha1_key.key + \ "\0" * (20 - len(self.sha1_key.key)) @@ -302,11 +373,14 @@ class BFDTestSession(object): packet[BFD].auth_key_hash = hashlib.sha1(hash_material).digest() return packet - def send_packet(self, packet=None): + def send_packet(self, packet=None, interface=None): + """ send packet on interface, creating the packet if needed """ if packet is None: packet = self.create_packet() + if interface is None: + interface = self.test.pg0 self.test.logger.debug(ppp("Sending packet:", packet)) - self.test.pg0.add_stream([packet]) + interface.add_stream(packet) self.test.pg_start() def verify_sha1_auth(self, packet): @@ -359,119 +433,146 @@ class BFDTestSession(object): bfd = packet[BFD] self.test.assert_equal(bfd.version, 1, "BFD version") self.test.assert_equal(bfd.your_discriminator, - self.bfd_values['my_discriminator'], + self.my_discriminator, "BFD - your discriminator") if self.sha1_key: self.verify_sha1_auth(packet) -class BFDCommonCode: - """Common code used by both IPv4 and IPv6 Test Cases""" - - def tearDown(self): - self.vapi.collect_events() # clear the event queue - if not self.vpp_dead: - self.vapi.want_bfd_events(enable_disable=0) - - def bfd_session_up(self): - """ Bring BFD session up """ - self.logger.info("BFD: Waiting for slow hello") - p, timeout = self.wait_for_bfd_packet(2) - self.logger.info("BFD: Sending Init") - self.test_session.update(my_discriminator=randint(0, 40000000), - your_discriminator=p[BFD].my_discriminator, - state=BFDState.init, - required_min_rx_interval=100000) - self.test_session.send_packet() - self.logger.info("BFD: Waiting for event") - e = self.vapi.wait_for_event(1, "bfd_udp_session_details") - self.verify_event(e, expected_state=BFDState.up) - self.logger.info("BFD: Session is Up") - self.test_session.update(state=BFDState.up) - self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) - - def bfd_session_down(self): - """ Bring BFD session down """ - self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) - self.test_session.update(state=BFDState.down) - self.test_session.send_packet() - self.logger.info("BFD: Waiting for event") - e = self.vapi.wait_for_event(1, "bfd_udp_session_details") - self.verify_event(e, expected_state=BFDState.down) - self.logger.info("BFD: Session is Down") - self.assert_equal(self.vpp_session.state, BFDState.down, BFDState) - - def verify_ip(self, packet): - """ Verify correctness of IP layer. """ - if self.vpp_session.af == AF_INET6: - ip = packet[IPv6] - local_ip = self.pg0.local_ip6 - remote_ip = self.pg0.remote_ip6 - self.assert_equal(ip.hlim, 255, "IPv6 hop limit") - else: - ip = packet[IP] - local_ip = self.pg0.local_ip4 - remote_ip = self.pg0.remote_ip4 - self.assert_equal(ip.ttl, 255, "IPv4 TTL") - self.assert_equal(ip.src, local_ip, "IP source address") - self.assert_equal(ip.dst, remote_ip, "IP destination address") - - def verify_udp(self, packet): - """ Verify correctness of UDP layer. """ - udp = packet[UDP] - self.assert_equal(udp.dport, BFD.udp_dport, "UDP destination port") - self.assert_in_range(udp.sport, BFD.udp_sport_min, BFD.udp_sport_max, - "UDP source port") - - def verify_event(self, event, expected_state): - """ Verify correctness of event values. """ - e = event - self.logger.debug("BFD: Event: %s" % repr(e)) - self.assert_equal(e.sw_if_index, - self.vpp_session.interface.sw_if_index, - "BFD interface index") - is_ipv6 = 0 - if self.vpp_session.af == AF_INET6: - is_ipv6 = 1 - self.assert_equal(e.is_ipv6, is_ipv6, "is_ipv6") - if self.vpp_session.af == AF_INET: - self.assert_equal(e.local_addr[:4], self.vpp_session.local_addr_n, - "Local IPv4 address") - self.assert_equal(e.peer_addr[:4], self.vpp_session.peer_addr_n, - "Peer IPv4 address") +def bfd_session_up(test): + """ Bring BFD session up """ + test.logger.info("BFD: Waiting for slow hello") + p = wait_for_bfd_packet(test, 2) + old_offset = None + if hasattr(test, 'vpp_clock_offset'): + old_offset = test.vpp_clock_offset + test.vpp_clock_offset = time.time() - p.time + test.logger.debug("BFD: Calculated vpp clock offset: %s", + test.vpp_clock_offset) + if old_offset: + test.assertAlmostEqual( + old_offset, test.vpp_clock_offset, delta=0.1, + msg="vpp clock offset not stable (new: %s, old: %s)" % + (test.vpp_clock_offset, old_offset)) + test.logger.info("BFD: Sending Init") + test.test_session.update(my_discriminator=randint(0, 40000000), + your_discriminator=p[BFD].my_discriminator, + state=BFDState.init) + test.test_session.send_packet() + test.logger.info("BFD: Waiting for event") + e = test.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(test, e, expected_state=BFDState.up) + test.logger.info("BFD: Session is Up") + test.test_session.update(state=BFDState.up) + test.test_session.send_packet() + test.assert_equal(test.vpp_session.state, BFDState.up, BFDState) + + +def bfd_session_down(test): + """ Bring BFD session down """ + test.assert_equal(test.vpp_session.state, BFDState.up, BFDState) + test.test_session.update(state=BFDState.down) + test.test_session.send_packet() + test.logger.info("BFD: Waiting for event") + e = test.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(test, e, expected_state=BFDState.down) + test.logger.info("BFD: Session is Down") + test.assert_equal(test.vpp_session.state, BFDState.down, BFDState) + + +def verify_ip(test, packet): + """ Verify correctness of IP layer. """ + if test.vpp_session.af == AF_INET6: + ip = packet[IPv6] + local_ip = test.pg0.local_ip6 + remote_ip = test.pg0.remote_ip6 + test.assert_equal(ip.hlim, 255, "IPv6 hop limit") + else: + ip = packet[IP] + local_ip = test.pg0.local_ip4 + remote_ip = test.pg0.remote_ip4 + test.assert_equal(ip.ttl, 255, "IPv4 TTL") + test.assert_equal(ip.src, local_ip, "IP source address") + test.assert_equal(ip.dst, remote_ip, "IP destination address") + + +def verify_udp(test, packet): + """ Verify correctness of UDP layer. """ + udp = packet[UDP] + test.assert_equal(udp.dport, BFD.udp_dport, "UDP destination port") + test.assert_in_range(udp.sport, BFD.udp_sport_min, BFD.udp_sport_max, + "UDP source port") + + +def verify_event(test, event, expected_state): + """ Verify correctness of event values. """ + e = event + test.logger.debug("BFD: Event: %s" % repr(e)) + test.assert_equal(e.sw_if_index, + test.vpp_session.interface.sw_if_index, + "BFD interface index") + is_ipv6 = 0 + if test.vpp_session.af == AF_INET6: + is_ipv6 = 1 + test.assert_equal(e.is_ipv6, is_ipv6, "is_ipv6") + if test.vpp_session.af == AF_INET: + test.assert_equal(e.local_addr[:4], test.vpp_session.local_addr_n, + "Local IPv4 address") + test.assert_equal(e.peer_addr[:4], test.vpp_session.peer_addr_n, + "Peer IPv4 address") + else: + test.assert_equal(e.local_addr, test.vpp_session.local_addr_n, + "Local IPv6 address") + test.assert_equal(e.peer_addr, test.vpp_session.peer_addr_n, + "Peer IPv6 address") + test.assert_equal(e.state, expected_state, BFDState) + + +def wait_for_bfd_packet(test, timeout=1, pcap_time_min=None): + """ wait for BFD packet and verify its correctness + + :param timeout: how long to wait + :param pcap_time_min: ignore packets with pcap timestamp lower than this + + :returns: tuple (packet, time spent waiting for packet) + """ + test.logger.info("BFD: Waiting for BFD packet") + deadline = time.time() + timeout + counter = 0 + while True: + counter += 1 + # sanity check + test.assert_in_range(counter, 0, 100, "number of packets ignored") + time_left = deadline - time.time() + if time_left < 0: + raise CaptureTimeoutError("Packet did not arrive within timeout") + p = test.pg0.wait_for_packet(timeout=time_left) + test.logger.debug(ppp("BFD: Got packet:", p)) + if pcap_time_min is not None and p.time < pcap_time_min: + test.logger.debug(ppp("BFD: ignoring packet (pcap time %s < " + "pcap time min %s):" % + (p.time, pcap_time_min), p)) else: - self.assert_equal(e.local_addr, self.vpp_session.local_addr_n, - "Local IPv6 address") - self.assert_equal(e.peer_addr, self.vpp_session.peer_addr_n, - "Peer IPv6 address") - self.assert_equal(e.state, expected_state, BFDState) - - def wait_for_bfd_packet(self, timeout=1): - """ wait for BFD packet - - :param timeout: how long to wait max - - :returns: tuple (packet, time spent waiting for packet) - """ - self.logger.info("BFD: Waiting for BFD packet") - before = time.time() - p = self.pg0.wait_for_packet(timeout=timeout) - after = time.time() - self.logger.debug(ppp("BFD: Got packet:", p)) - bfd = p[BFD] - if bfd is None: - raise Exception(ppp("Unexpected or invalid BFD packet:", p)) - if bfd.payload: - raise Exception(ppp("Unexpected payload in BFD packet:", bfd)) - self.verify_ip(p) - self.verify_udp(p) - self.test_session.verify_bfd(p) - return p, after - before - - -class BFD4TestCase(VppTestCase, BFDCommonCode): + break + bfd = p[BFD] + if bfd is None: + raise Exception(ppp("Unexpected or invalid BFD packet:", p)) + if bfd.payload: + raise Exception(ppp("Unexpected payload in BFD packet:", bfd)) + verify_ip(test, p) + verify_udp(test, p) + test.test_session.verify_bfd(p) + return p + + +class BFD4TestCase(VppTestCase): """Bidirectional Forwarding Detection (BFD)""" + pg0 = None + vpp_clock_offset = None + vpp_session = None + test_session = None + @classmethod def setUpClass(cls): super(BFD4TestCase, cls).setUpClass() @@ -490,7 +591,7 @@ class BFD4TestCase(VppTestCase, BFDCommonCode): super(BFD4TestCase, self).setUp() self.factory = AuthKeyFactory() self.vapi.want_bfd_events() - self.pg_enable_capture([self.pg0]) + self.pg0.enable_capture() try: self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) @@ -502,182 +603,191 @@ class BFD4TestCase(VppTestCase, BFDCommonCode): raise def tearDown(self): - BFDCommonCode.tearDown(self) - VppTestCase.tearDown(self) + if not self.vpp_dead: + self.vapi.want_bfd_events(enable_disable=0) + self.vapi.collect_events() # clear the event queue + super(BFD4TestCase, self).tearDown() def test_session_up(self): """ bring BFD session up """ - self.bfd_session_up() + bfd_session_up(self) def test_session_down(self): """ bring BFD session down """ - self.bfd_session_up() - self.bfd_session_down() + bfd_session_up(self) + bfd_session_down(self) def test_hold_up(self): """ hold BFD session up """ - self.bfd_session_up() - for i in range(5): - self.wait_for_bfd_packet() + bfd_session_up(self) + for dummy in range(self.test_session.detect_mult * 2): + wait_for_bfd_packet(self) self.test_session.send_packet() self.assert_equal(len(self.vapi.collect_events()), 0, "number of bfd events") def test_slow_timer(self): """ verify slow periodic control frames while session down """ - expected_packets = 3 - self.logger.info("BFD: Waiting for %d BFD packets" % expected_packets) - self.wait_for_bfd_packet(2) - for i in range(expected_packets): - before = time.time() - self.wait_for_bfd_packet(2) - after = time.time() + packet_count = 3 + self.logger.info("BFD: Waiting for %d BFD packets", packet_count) + prev_packet = wait_for_bfd_packet(self, 2) + for dummy in range(packet_count): + next_packet = wait_for_bfd_packet(self, 2) + time_diff = next_packet.time - prev_packet.time # spec says the range should be <0.75, 1>, allow extra 0.05 margin # to work around timing issues self.assert_in_range( - after - before, 0.70, 1.05, "time between slow packets") - before = after + time_diff, 0.70, 1.05, "time between slow packets") + prev_packet = next_packet def test_zero_remote_min_rx(self): - """ no packets when zero BFD RemoteMinRxInterval """ - self.pg_enable_capture([self.pg0]) - p, timeout = self.wait_for_bfd_packet(2) - self.test_session.update(my_discriminator=randint(0, 40000000), - your_discriminator=p[BFD].my_discriminator, - state=BFDState.init, - required_min_rx_interval=0) + """ no packets when zero remote required min rx interval """ + bfd_session_up(self) + self.test_session.update(required_min_rx=0) self.test_session.send_packet() - e = self.vapi.wait_for_event(1, "bfd_udp_session_details") - self.verify_event(e, expected_state=BFDState.up) - cap = 2 * self.vpp_session.desired_min_tx *\ - self.vpp_session.detect_mult - now = time.time() + self.test_session.detect_mult + time_mark = time.time() count = 0 # busy wait here, trying to collect a packet or event, vpp is not # allowed to send packets and the session will timeout first - so the # Up->Down event must arrive before any packets do - while time.time() < now + cap / us_in_sec: + while time.time() < time_mark + cap / USEC_IN_SEC: try: - p, ttp = self.wait_for_bfd_packet(timeout=0) + p = wait_for_bfd_packet( + self, timeout=0, + pcap_time_min=time_mark - self.vpp_clock_offset) self.logger.error(ppp("Received unexpected packet:", p)) count += 1 - except: + except CaptureTimeoutError: pass events = self.vapi.collect_events() if len(events) > 0: - self.verify_event(events[0], BFDState.down) + verify_event(self, events[0], BFDState.down) break self.assert_equal(count, 0, "number of packets received") def test_conn_down(self): """ verify session goes down after inactivity """ - self.bfd_session_up() - self.wait_for_bfd_packet() - self.assert_equal(len(self.vapi.collect_events()), 0, - "number of bfd events") - self.wait_for_bfd_packet() - self.assert_equal(len(self.vapi.collect_events()), 0, - "number of bfd events") + bfd_session_up(self) + for dummy in range(self.test_session.detect_mult): + wait_for_bfd_packet(self) + self.assert_equal(len(self.vapi.collect_events()), 0, + "number of bfd events") e = self.vapi.wait_for_event(1, "bfd_udp_session_details") - self.verify_event(e, expected_state=BFDState.down) + verify_event(self, e, expected_state=BFDState.down) def test_large_required_min_rx(self): - """ large remote RequiredMinRxInterval """ - self.bfd_session_up() - self.wait_for_bfd_packet() + """ large remote required min rx interval """ + bfd_session_up(self) + p = wait_for_bfd_packet(self) interval = 3000000 - self.test_session.update(required_min_rx_interval=interval) + self.test_session.update(required_min_rx=interval) self.test_session.send_packet() - now = time.time() + time_mark = time.time() count = 0 # busy wait here, trying to collect a packet or event, vpp is not # allowed to send packets and the session will timeout first - so the # Up->Down event must arrive before any packets do - while time.time() < now + interval / us_in_sec: + while time.time() < time_mark + interval / USEC_IN_SEC: try: - p, ttp = self.wait_for_bfd_packet(timeout=0) + p = wait_for_bfd_packet(self, timeout=0) + # if vpp managed to send a packet before we did the session + # session update, then that's fine, ignore it + if p.time < time_mark - self.vpp_clock_offset: + continue self.logger.error(ppp("Received unexpected packet:", p)) count += 1 - except: + except CaptureTimeoutError: pass events = self.vapi.collect_events() if len(events) > 0: - self.verify_event(events[0], BFDState.down) + verify_event(self, events[0], BFDState.down) break self.assert_equal(count, 0, "number of packets received") - def test_immediate_remote_min_rx_reduce(self): - """ immediately honor remote min rx reduction """ + def test_immediate_remote_min_rx_reduction(self): + """ immediately honor remote required min rx reduction """ self.vpp_session.remove_vpp_config() self.vpp_session = VppBFDUDPSession( self, self.pg0, self.pg0.remote_ip4, desired_min_tx=10000) - self.pg_enable_capture([self.pg0]) + self.pg0.enable_capture() self.vpp_session.add_vpp_config() - self.test_session.update(desired_min_tx_interval=1000000, - required_min_rx_interval=1000000) - self.bfd_session_up() - self.wait_for_bfd_packet() - interval = 100000 - self.test_session.update(required_min_rx_interval=interval) + self.test_session.update(desired_min_tx=1000000, + required_min_rx=1000000) + bfd_session_up(self) + reference_packet = wait_for_bfd_packet(self) + time_mark = time.time() + interval = 300000 + self.test_session.update(required_min_rx=interval) self.test_session.send_packet() - p, ttp = self.wait_for_bfd_packet() - # allow extra 10% to work around timing issues, first packet is special - self.assert_in_range(ttp, 0, 1.10 * interval / us_in_sec, - "time between BFD packets") - p, ttp = self.wait_for_bfd_packet() - self.assert_in_range(ttp, .9 * .75 * interval / us_in_sec, - 1.10 * interval / us_in_sec, - "time between BFD packets") - p, ttp = self.wait_for_bfd_packet() - self.assert_in_range(ttp, .9 * .75 * interval / us_in_sec, - 1.10 * interval / us_in_sec, + extra_time = time.time() - time_mark + p = wait_for_bfd_packet(self) + # first packet is allowed to be late by time we spent doing the update + # calculated in extra_time + self.assert_in_range(p.time - reference_packet.time, + .95 * 0.75 * interval / USEC_IN_SEC, + 1.05 * interval / USEC_IN_SEC + extra_time, "time between BFD packets") + reference_packet = p + for dummy in range(3): + p = wait_for_bfd_packet(self) + diff = p.time - reference_packet.time + self.assert_in_range(diff, .95 * .75 * interval / USEC_IN_SEC, + 1.05 * interval / USEC_IN_SEC, + "time between BFD packets") + reference_packet = p def test_modify_req_min_rx_double(self): """ modify session - double required min rx """ - self.bfd_session_up() - self.wait_for_bfd_packet() - self.test_session.update(desired_min_tx_interval=10000, - required_min_rx_interval=10000) + bfd_session_up(self) + p = wait_for_bfd_packet(self) + self.test_session.update(desired_min_tx=10000, + required_min_rx=10000) self.test_session.send_packet() - # first double required min rx + # double required min rx self.vpp_session.modify_parameters( required_min_rx=2 * self.vpp_session.required_min_rx) - p, ttp = self.wait_for_bfd_packet() + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) # poll bit needs to be set self.assertIn("P", p.sprintf("%BFD.flags%"), "Poll bit not set in BFD packet") # finish poll sequence with final packet final = self.test_session.create_packet() final[BFD].flags = "F" + timeout = self.test_session.detect_mult * \ + max(self.test_session.desired_min_tx, + self.vpp_session.required_min_rx) / USEC_IN_SEC self.test_session.send_packet(final) - # now we can wait 0.9*3*req-min-rx and the session should still be up - self.sleep(0.9 * self.vpp_session.detect_mult * - self.vpp_session.required_min_rx / us_in_sec) - self.assert_equal(len(self.vapi.collect_events()), 0, - "number of bfd events") + time_mark = time.time() + e = self.vapi.wait_for_event(2 * timeout, "bfd_udp_session_details") + verify_event(self, e, expected_state=BFDState.down) + time_to_event = time.time() - time_mark + self.assert_in_range(time_to_event, .9 * timeout, + 1.1 * timeout, "session timeout") def test_modify_req_min_rx_halve(self): """ modify session - halve required min rx """ self.vpp_session.modify_parameters( required_min_rx=2 * self.vpp_session.required_min_rx) - self.bfd_session_up() - self.wait_for_bfd_packet() - self.test_session.update(desired_min_tx_interval=10000, - required_min_rx_interval=10000) + bfd_session_up(self) + p = wait_for_bfd_packet(self) + self.test_session.update(desired_min_tx=10000, + required_min_rx=10000) self.test_session.send_packet() - p, ttp = self.wait_for_bfd_packet() + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) # halve required min rx old_required_min_rx = self.vpp_session.required_min_rx self.vpp_session.modify_parameters( required_min_rx=0.5 * self.vpp_session.required_min_rx) # now we wait 0.8*3*old-req-min-rx and the session should still be up self.sleep(0.8 * self.vpp_session.detect_mult * - old_required_min_rx / us_in_sec) + old_required_min_rx / USEC_IN_SEC) self.assert_equal(len(self.vapi.collect_events()), 0, "number of bfd events") - p, ttp = self.wait_for_bfd_packet() + p = wait_for_bfd_packet(self) # poll bit needs to be set self.assertIn("P", p.sprintf("%BFD.flags%"), "Poll bit not set in BFD packet") @@ -690,12 +800,12 @@ class BFD4TestCase(VppTestCase, BFDCommonCode): e = self.vapi.wait_for_event(1, "bfd_udp_session_details") after = time.time() detection_time = self.vpp_session.detect_mult *\ - self.vpp_session.required_min_rx / us_in_sec + self.vpp_session.required_min_rx / USEC_IN_SEC self.assert_in_range(after - before, 0.9 * detection_time, 1.1 * detection_time, "time before bfd session goes down") - self.verify_event(e, expected_state=BFDState.down) + verify_event(self, e, expected_state=BFDState.down) def test_modify_des_min_tx(self): """ modify desired min tx interval """ @@ -703,9 +813,11 @@ class BFD4TestCase(VppTestCase, BFDCommonCode): def test_modify_detect_mult(self): """ modify detect multiplier """ - self.bfd_session_up() + bfd_session_up(self) + p = wait_for_bfd_packet(self) self.vpp_session.modify_parameters(detect_mult=1) - p, ttp = self.wait_for_bfd_packet() + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) self.assert_equal(self.vpp_session.detect_mult, p[BFD].detect_mult, "detect mult") @@ -713,7 +825,8 @@ class BFD4TestCase(VppTestCase, BFDCommonCode): self.assertNotIn("P", p.sprintf("%BFD.flags%"), "Poll bit not set in BFD packet") self.vpp_session.modify_parameters(detect_mult=10) - p, ttp = self.wait_for_bfd_packet() + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) self.assert_equal(self.vpp_session.detect_mult, p[BFD].detect_mult, "detect mult") @@ -721,10 +834,42 @@ class BFD4TestCase(VppTestCase, BFDCommonCode): self.assertNotIn("P", p.sprintf("%BFD.flags%"), "Poll bit not set in BFD packet") + def test_no_periodic_if_remote_demand(self): + """ no periodic frames outside poll sequence if remote demand set """ + self.test_session.update(detect_mult=10) + bfd_session_up(self) + demand = self.test_session.create_packet() + demand[BFD].flags = "D" + self.test_session.send_packet(demand) + transmit_time = 0.9 \ + * max(self.vpp_session.required_min_rx, + self.test_session.desired_min_tx) \ + / USEC_IN_SEC + count = 0 + for dummy in range(self.test_session.detect_mult): + time.sleep(transmit_time) + self.test_session.send_packet(demand) + try: + p = wait_for_bfd_packet(self, timeout=0) + self.logger.error(ppp("Received unexpected packet:", p)) + count += 1 + except CaptureTimeoutError: + pass + events = self.vapi.collect_events() + for e in events: + self.logger.error("Received unexpected event: %s", e) + self.assert_equal(count, 0, "number of packets received") + self.assert_equal(len(events), 0, "number of events received") + -class BFD6TestCase(VppTestCase, BFDCommonCode): +class BFD6TestCase(VppTestCase): """Bidirectional Forwarding Detection (BFD) (IPv6) """ + pg0 = None + vpp_clock_offset = None + vpp_session = None + test_session = None + @classmethod def setUpClass(cls): super(BFD6TestCase, cls).setUpClass() @@ -743,7 +888,7 @@ class BFD6TestCase(VppTestCase, BFDCommonCode): super(BFD6TestCase, self).setUp() self.factory = AuthKeyFactory() self.vapi.want_bfd_events() - self.pg_enable_capture([self.pg0]) + self.pg0.enable_capture() try: self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip6, @@ -757,27 +902,34 @@ class BFD6TestCase(VppTestCase, BFDCommonCode): raise def tearDown(self): - BFDCommonCode.tearDown(self) - VppTestCase.tearDown(self) + if not self.vpp_dead: + self.vapi.want_bfd_events(enable_disable=0) + self.vapi.collect_events() # clear the event queue + super(BFD6TestCase, self).tearDown() def test_session_up(self): """ bring BFD session up """ - self.bfd_session_up() + bfd_session_up(self) def test_hold_up(self): """ hold BFD session up """ - self.bfd_session_up() - for i in range(5): - self.wait_for_bfd_packet() + bfd_session_up(self) + for dummy in range(self.test_session.detect_mult*2): + wait_for_bfd_packet(self) self.test_session.send_packet() self.assert_equal(len(self.vapi.collect_events()), 0, "number of bfd events") self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) -class BFDSHA1TestCase(VppTestCase, BFDCommonCode): +class BFDSHA1TestCase(VppTestCase): """Bidirectional Forwarding Detection (BFD) (SHA1 auth) """ + pg0 = None + vpp_clock_offset = None + vpp_session = None + test_session = None + @classmethod def setUpClass(cls): super(BFDSHA1TestCase, cls).setUpClass() @@ -795,11 +947,13 @@ class BFDSHA1TestCase(VppTestCase, BFDCommonCode): super(BFDSHA1TestCase, self).setUp() self.factory = AuthKeyFactory() self.vapi.want_bfd_events() - self.pg_enable_capture([self.pg0]) + self.pg0.enable_capture() def tearDown(self): - BFDCommonCode.tearDown(self) - VppTestCase.tearDown(self) + if not self.vpp_dead: + self.vapi.want_bfd_events(enable_disable=0) + self.vapi.collect_events() # clear the event queue + super(BFDSHA1TestCase, self).tearDown() def test_session_up(self): """ bring BFD session up """ @@ -813,7 +967,7 @@ class BFDSHA1TestCase(VppTestCase, BFDCommonCode): self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id) - self.bfd_session_up() + bfd_session_up(self) def test_hold_up(self): """ hold BFD session up """ @@ -827,9 +981,9 @@ class BFDSHA1TestCase(VppTestCase, BFDCommonCode): self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id) - self.bfd_session_up() - for i in range(5): - self.wait_for_bfd_packet() + bfd_session_up(self) + for dummy in range(self.test_session.detect_mult*2): + wait_for_bfd_packet(self) self.test_session.send_packet() self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) @@ -842,12 +996,14 @@ class BFDSHA1TestCase(VppTestCase, BFDCommonCode): self.pg0.remote_ip4, sha1_key=key) self.vpp_session.add_vpp_config() self.vpp_session.admin_up() + # specify sequence number so that it wraps self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, - bfd_key_id=self.vpp_session.bfd_key_id) - self.bfd_session_up() - for i in range(5): - self.wait_for_bfd_packet() + bfd_key_id=self.vpp_session.bfd_key_id, + our_seq_number=0xFFFFFFFF - 4) + bfd_session_up(self) + for dummy in range(30): + wait_for_bfd_packet(self) self.test_session.inc_seq_num() self.test_session.send_packet() self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) @@ -864,24 +1020,22 @@ class BFDSHA1TestCase(VppTestCase, BFDCommonCode): self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id) - self.bfd_session_up() - self.wait_for_bfd_packet() - self.test_session.send_packet() - self.assert_equal(len(self.vapi.collect_events()), 0, - "number of bfd events") - self.wait_for_bfd_packet() - self.test_session.send_packet() - self.assert_equal(len(self.vapi.collect_events()), 0, - "number of bfd events") - self.wait_for_bfd_packet() - self.test_session.send_packet() - self.wait_for_bfd_packet() + bfd_session_up(self) + detection_time = self.vpp_session.detect_mult *\ + self.vpp_session.required_min_rx / USEC_IN_SEC + session_timeout = time.time() + detection_time + while time.time() < session_timeout: + self.assert_equal(len(self.vapi.collect_events()), 0, + "number of bfd events") + wait_for_bfd_packet(self) + self.test_session.send_packet() + wait_for_bfd_packet(self) self.test_session.send_packet() e = self.vapi.collect_events() # session should be down now, because the sequence numbers weren't # updated self.assert_equal(len(e), 1, "number of bfd events") - self.verify_event(e[0], expected_state=BFDState.down) + verify_event(self, e[0], expected_state=BFDState.down) def execute_rogue_session_scenario(self, vpp_bfd_udp_session, legitimate_test_session, @@ -903,14 +1057,22 @@ class BFDSHA1TestCase(VppTestCase, BFDCommonCode): self.vpp_session.admin_up() self.test_session = legitimate_test_session # bring vpp session up - self.bfd_session_up() + bfd_session_up(self) # send packet from rogue session - rogue_test_session.bfd_values = self.test_session.bfd_values.copy() + rogue_test_session.update( + my_discriminator=self.test_session.my_discriminator, + your_discriminator=self.test_session.your_discriminator, + desired_min_tx=self.test_session.desired_min_tx, + required_min_rx=self.test_session.required_min_rx, + detect_mult=self.test_session.detect_mult, + diag=self.test_session.diag, + state=self.test_session.state, + auth_type=self.test_session.auth_type) if rogue_bfd_values: rogue_test_session.update(**rogue_bfd_values) rogue_test_session.update(state=BFDState.down) rogue_test_session.send_packet() - self.wait_for_bfd_packet() + wait_for_bfd_packet(self) self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) def test_mismatch_auth(self): @@ -974,31 +1136,30 @@ class BFDSHA1TestCase(VppTestCase, BFDCommonCode): self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id, our_seq_number=0) - self.bfd_session_up() - # now we need to not respond for 2*detection_time (4 packets) - self.wait_for_bfd_packet() - self.assert_equal(len(self.vapi.collect_events()), 0, - "number of bfd events") - self.wait_for_bfd_packet() - self.assert_equal(len(self.vapi.collect_events()), 0, - "number of bfd events") - e = self.vapi.wait_for_event(1, "bfd_udp_session_details") - self.verify_event(e, expected_state=BFDState.down) + bfd_session_up(self) + # don't send any packets for 2*detection_time + detection_time = self.vpp_session.detect_mult *\ + self.vpp_session.required_min_rx / USEC_IN_SEC + self.sleep(detection_time, "simulating peer restart") + events = self.vapi.collect_events() + self.assert_equal(len(events), 1, "number of bfd events") + verify_event(self, events[0], expected_state=BFDState.down) self.test_session.update(state=BFDState.down) - self.wait_for_bfd_packet() - self.assert_equal(len(self.vapi.collect_events()), 0, - "number of bfd events") - self.wait_for_bfd_packet() - self.assert_equal(len(self.vapi.collect_events()), 0, - "number of bfd events") # reset sequence number self.test_session.our_seq_number = 0 - self.bfd_session_up() + self.test_session.vpp_seq_number = None + # now throw away any pending packets + self.pg0.enable_capture() + bfd_session_up(self) -class BFDAuthOnOffTestCase(VppTestCase, BFDCommonCode): +class BFDAuthOnOffTestCase(VppTestCase): """Bidirectional Forwarding Detection (BFD) (changing auth) """ + pg0 = None + vpp_session = None + test_session = None + @classmethod def setUpClass(cls): super(BFDAuthOnOffTestCase, cls).setUpClass() @@ -1016,10 +1177,13 @@ class BFDAuthOnOffTestCase(VppTestCase, BFDCommonCode): super(BFDAuthOnOffTestCase, self).setUp() self.factory = AuthKeyFactory() self.vapi.want_bfd_events() + self.pg0.enable_capture() def tearDown(self): - BFDCommonCode.tearDown(self) - VppTestCase.tearDown(self) + if not self.vpp_dead: + self.vapi.want_bfd_events(enable_disable=0) + self.vapi.collect_events() # clear the event queue + super(BFDAuthOnOffTestCase, self).tearDown() def test_auth_on_immediate(self): """ turn auth on without disturbing session state (immediate) """ @@ -1030,15 +1194,17 @@ class BFDAuthOnOffTestCase(VppTestCase, BFDCommonCode): self.vpp_session.add_vpp_config() self.vpp_session.admin_up() self.test_session = BFDTestSession(self, self.pg0, AF_INET) - self.bfd_session_up() - for i in range(5): - self.wait_for_bfd_packet() + bfd_session_up(self) + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.vpp_session.activate_auth(key) self.test_session.bfd_key_id = self.vpp_session.bfd_key_id self.test_session.sha1_key = key - for i in range(5): - self.wait_for_bfd_packet() + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) self.assert_equal(len(self.vapi.collect_events()), 0, @@ -1055,15 +1221,20 @@ class BFDAuthOnOffTestCase(VppTestCase, BFDCommonCode): self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id) - self.bfd_session_up() - for i in range(5): - self.wait_for_bfd_packet() + bfd_session_up(self) + # self.vapi.want_bfd_events(enable_disable=0) + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) + self.test_session.inc_seq_num() self.test_session.send_packet() self.vpp_session.deactivate_auth() self.test_session.bfd_key_id = None self.test_session.sha1_key = None - for i in range(5): - self.wait_for_bfd_packet() + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) + self.test_session.inc_seq_num() self.test_session.send_packet() self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) self.assert_equal(len(self.vapi.collect_events()), 0, @@ -1082,15 +1253,17 @@ class BFDAuthOnOffTestCase(VppTestCase, BFDCommonCode): self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key1, bfd_key_id=self.vpp_session.bfd_key_id) - self.bfd_session_up() - for i in range(5): - self.wait_for_bfd_packet() + bfd_session_up(self) + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.vpp_session.activate_auth(key2) self.test_session.bfd_key_id = self.vpp_session.bfd_key_id self.test_session.sha1_key = key2 - for i in range(5): - self.wait_for_bfd_packet() + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) self.assert_equal(len(self.vapi.collect_events()), 0, @@ -1105,19 +1278,21 @@ class BFDAuthOnOffTestCase(VppTestCase, BFDCommonCode): self.vpp_session.add_vpp_config() self.vpp_session.admin_up() self.test_session = BFDTestSession(self, self.pg0, AF_INET) - self.bfd_session_up() - for i in range(5): - self.wait_for_bfd_packet() + bfd_session_up(self) + for dummy in range(self.test_session.detect_mult*2): + wait_for_bfd_packet(self) self.test_session.send_packet() self.vpp_session.activate_auth(key, delayed=True) - for i in range(5): - self.wait_for_bfd_packet() + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.test_session.bfd_key_id = self.vpp_session.bfd_key_id self.test_session.sha1_key = key self.test_session.send_packet() - for i in range(5): - self.wait_for_bfd_packet() + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) self.assert_equal(len(self.vapi.collect_events()), 0, @@ -1134,19 +1309,22 @@ class BFDAuthOnOffTestCase(VppTestCase, BFDCommonCode): self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id) - self.bfd_session_up() - for i in range(5): - self.wait_for_bfd_packet() + bfd_session_up(self) + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.vpp_session.deactivate_auth(delayed=True) - for i in range(5): - self.wait_for_bfd_packet() + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.test_session.bfd_key_id = None self.test_session.sha1_key = None self.test_session.send_packet() - for i in range(5): - self.wait_for_bfd_packet() + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) self.assert_equal(len(self.vapi.collect_events()), 0, @@ -1165,19 +1343,22 @@ class BFDAuthOnOffTestCase(VppTestCase, BFDCommonCode): self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key1, bfd_key_id=self.vpp_session.bfd_key_id) - self.bfd_session_up() - for i in range(5): - self.wait_for_bfd_packet() + bfd_session_up(self) + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.vpp_session.activate_auth(key2, delayed=True) - for i in range(5): - self.wait_for_bfd_packet() + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.test_session.bfd_key_id = self.vpp_session.bfd_key_id self.test_session.sha1_key = key2 self.test_session.send_packet() - for i in range(5): - self.wait_for_bfd_packet() + for dummy in range(self.test_session.detect_mult*2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) self.assert_equal(len(self.vapi.collect_events()), 0, -- cgit 1.2.3-korg