BFD: basic asynchronous session up/down
This is a work-in-progress basic BFD session handling. Only asynchronous mode is supported at the moment. Setting the session flags doesn't work. Change-Id: Idba27f721b5c35be5a66a6d202a63d23ff7ecf6f Signed-off-by: Klement Sekera <ksekera@cisco.com>
This commit is contained in:

committed by
Dave Barach

parent
d171d48edc
commit
0e3c0de1ed
2
Makefile
2
Makefile
@ -214,6 +214,8 @@ build-vpp-api: $(BR)/.bootstrap.ok
|
||||
|
||||
VPP_PYTHON_PREFIX=$(BR)/python
|
||||
|
||||
|
||||
#$(if $(filter-out $(3),retest),make -C $(BR) PLATFORM=$(1) TAG=$(2) vpp-install ,)
|
||||
define test
|
||||
$(if $(filter-out $(3),retest),make -C $(BR) PLATFORM=$(1) TAG=$(2) vpp-api-install plugins-install vpp-install,)
|
||||
make -C test \
|
||||
|
216
test/bfd.py
Normal file
216
test/bfd.py
Normal file
@ -0,0 +1,216 @@
|
||||
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 util import NumericConstant
|
||||
|
||||
|
||||
class BFDDiagCode(NumericConstant):
|
||||
""" BFD Diagnostic Code """
|
||||
no_diagnostic = 0
|
||||
control_detection_time_expired = 1
|
||||
echo_function_failed = 2
|
||||
neighbor_signaled_session_down = 3
|
||||
forwarding_plane_reset = 4
|
||||
path_down = 5
|
||||
concatenated_path_down = 6
|
||||
administratively_down = 7
|
||||
reverse_concatenated_path_down = 8
|
||||
|
||||
desc_dict = {
|
||||
no_diagnostic: "No diagnostic",
|
||||
control_detection_time_expired: "Control Detection Time Expired",
|
||||
echo_function_failed: "Echo Function Failed",
|
||||
neighbor_signaled_session_down: "Neighbor Signaled Session Down",
|
||||
forwarding_plane_reset: "Forwarding Plane Reset",
|
||||
path_down: "Path Down",
|
||||
concatenated_path_down: "Concatenated Path Down",
|
||||
administratively_down: "Administratively Down",
|
||||
reverse_concatenated_path_down: "Reverse Concatenated Path Down",
|
||||
}
|
||||
|
||||
def __init__(self, value):
|
||||
NumericConstant.__init__(self, value)
|
||||
|
||||
|
||||
class BFDState(NumericConstant):
|
||||
""" BFD State """
|
||||
admin_down = 0
|
||||
down = 1
|
||||
init = 2
|
||||
up = 3
|
||||
|
||||
desc_dict = {
|
||||
admin_down: "AdminDown",
|
||||
down: "Down",
|
||||
init: "Init",
|
||||
up: "Up",
|
||||
}
|
||||
|
||||
def __init__(self, value):
|
||||
NumericConstant.__init__(self, value)
|
||||
|
||||
|
||||
class BFD(Packet):
|
||||
|
||||
udp_dport = 3784 #: BFD destination port per RFC 5881
|
||||
udp_sport_min = 49152 #: BFD source port min value per RFC 5881
|
||||
udp_sport_max = 65535 #: BFD source port max value per RFC 5881
|
||||
|
||||
name = "BFD"
|
||||
|
||||
fields_desc = [
|
||||
BitField("version", 1, 3),
|
||||
BitEnumField("diag", 0, 5, BFDDiagCode.desc_dict),
|
||||
BitEnumField("state", 0, 2, BFDState.desc_dict),
|
||||
FlagsField("flags", 0, 6, ['P', 'F', 'C', 'A', 'D', 'M']),
|
||||
XByteField("detect_mult", 0),
|
||||
XByteField("length", 24),
|
||||
BitField("my_discriminator", 0, 32),
|
||||
BitField("your_discriminator", 0, 32),
|
||||
BitField("desired_min_tx_interval", 0, 32),
|
||||
BitField("required_min_rx_interval", 0, 32),
|
||||
BitField("required_min_echo_rx_interval", 0, 32)]
|
||||
|
||||
def mysummary(self):
|
||||
return self.sprintf("BFD(my_disc=%BFD.my_discriminator%,"
|
||||
"your_disc=%BFD.your_discriminator%)")
|
||||
|
||||
# glue the BFD packet class to scapy parser
|
||||
bind_layers(UDP, BFD, dport=BFD.udp_dport)
|
||||
|
||||
|
||||
class VppBFDUDPSession(VppObject):
|
||||
""" Represents BFD UDP session in VPP """
|
||||
|
||||
@property
|
||||
def test(self):
|
||||
""" Test which created this session """
|
||||
return self._test
|
||||
|
||||
@property
|
||||
def interface(self):
|
||||
""" Interface on which this session lives """
|
||||
return self._interface
|
||||
|
||||
@property
|
||||
def af(self):
|
||||
""" Address family - AF_INET or AF_INET6 """
|
||||
return self._af
|
||||
|
||||
@property
|
||||
def bs_index(self):
|
||||
""" BFD session index from VPP """
|
||||
if self._bs_index is not None:
|
||||
return self._bs_index
|
||||
raise NotConfiguredException("not configured")
|
||||
|
||||
@property
|
||||
def local_addr(self):
|
||||
""" BFD session local address (VPP address) """
|
||||
if self._local_addr is None:
|
||||
return self._interface.local_ip4
|
||||
return self._local_addr
|
||||
|
||||
@property
|
||||
def local_addr_n(self):
|
||||
""" BFD session local address (VPP address) - raw, suitable for API """
|
||||
if self._local_addr is None:
|
||||
return self._interface.local_ip4n
|
||||
return self._local_addr_n
|
||||
|
||||
@property
|
||||
def peer_addr(self):
|
||||
""" BFD session peer address """
|
||||
return self._peer_addr
|
||||
|
||||
@property
|
||||
def peer_addr_n(self):
|
||||
""" BFD session peer address - raw, suitable for API """
|
||||
return self._peer_addr_n
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" BFD session state """
|
||||
result = self.test.vapi.bfd_udp_session_dump()
|
||||
session = None
|
||||
for s in result:
|
||||
if s.sw_if_index == self.interface.sw_if_index:
|
||||
if self.af == AF_INET \
|
||||
and s.is_ipv6 == 0 \
|
||||
and self.interface.local_ip4n == s.local_addr[:4] \
|
||||
and self.interface.remote_ip4n == s.peer_addr[:4]:
|
||||
session = s
|
||||
break
|
||||
if session is None:
|
||||
raise Exception(
|
||||
"Could not find BFD session in VPP response: %s" % repr(result))
|
||||
return session.state
|
||||
|
||||
@property
|
||||
def desired_min_tx(self):
|
||||
return self._desired_min_tx
|
||||
|
||||
@property
|
||||
def required_min_rx(self):
|
||||
return self._required_min_rx
|
||||
|
||||
@property
|
||||
def detect_mult(self):
|
||||
return self._detect_mult
|
||||
|
||||
def __init__(self, test, interface, peer_addr, local_addr=None, af=AF_INET):
|
||||
self._test = test
|
||||
self._interface = interface
|
||||
self._af = af
|
||||
self._local_addr = local_addr
|
||||
self._peer_addr = peer_addr
|
||||
self._peer_addr_n = socket.inet_pton(af, peer_addr)
|
||||
self._bs_index = None
|
||||
self._desired_min_tx = 200000 # 0.2s
|
||||
self._required_min_rx = 200000 # 0.2s
|
||||
self._detect_mult = 3 # 3 packets need to be missed
|
||||
|
||||
def add_vpp_config(self):
|
||||
is_ipv6 = 1 if AF_INET6 == self.af else 0
|
||||
result = self.test.vapi.bfd_udp_add(
|
||||
self._interface.sw_if_index,
|
||||
self.desired_min_tx,
|
||||
self.required_min_rx,
|
||||
self.detect_mult,
|
||||
self.local_addr_n,
|
||||
self.peer_addr_n,
|
||||
is_ipv6=is_ipv6)
|
||||
self._bs_index = result.bs_index
|
||||
|
||||
def query_vpp_config(self):
|
||||
result = self.test.vapi.bfd_udp_session_dump()
|
||||
session = None
|
||||
for s in result:
|
||||
if s.sw_if_index == self.interface.sw_if_index:
|
||||
if self.af == AF_INET \
|
||||
and s.is_ipv6 == 0 \
|
||||
and self.interface.local_ip4n == s.local_addr[:4] \
|
||||
and self.interface.remote_ip4n == s.peer_addr[:4]:
|
||||
session = s
|
||||
break
|
||||
if session is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
def remove_vpp_config(self):
|
||||
if hasattr(self, '_bs_index'):
|
||||
is_ipv6 = 1 if AF_INET6 == self._af else 0
|
||||
self.test.vapi.bfd_udp_del(
|
||||
self._interface.sw_if_index,
|
||||
self.local_addr_n,
|
||||
self.peer_addr_n,
|
||||
is_ipv6=is_ipv6)
|
||||
|
||||
def object_id(self):
|
||||
return "bfd-udp-%d" % self.bs_index
|
||||
|
||||
def admin_up(self):
|
||||
self.test.vapi.bfd_session_set_flags(self.bs_index, 1)
|
@ -462,6 +462,34 @@ class VppTestCase(unittest.TestCase):
|
||||
if info.dst == dst_index:
|
||||
return info
|
||||
|
||||
def assert_equal(self, real_value, expected_value, name_or_class=None):
|
||||
if name_or_class is None:
|
||||
self.assertEqual(real_value, expected_value, msg)
|
||||
return
|
||||
try:
|
||||
msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
|
||||
msg = msg % (getdoc(name_or_class).strip(),
|
||||
real_value, str(name_or_class(real_value)),
|
||||
expected_value, str(name_or_class(expected_value)))
|
||||
except:
|
||||
msg = "Invalid %s: %s does not match expected value %s" % (
|
||||
name_or_class, real_value, expected_value)
|
||||
|
||||
self.assertEqual(real_value, expected_value, msg)
|
||||
|
||||
def assert_in_range(
|
||||
self,
|
||||
real_value,
|
||||
expected_min,
|
||||
expected_max,
|
||||
name=None):
|
||||
if name is None:
|
||||
msg = None
|
||||
else:
|
||||
msg = "Invalid %s: %s out of range <%s,%s>" % (
|
||||
name, real_value, expected_min, expected_max)
|
||||
self.assertTrue(expected_min <= real_value <= expected_max, msg)
|
||||
|
||||
|
||||
class VppTestResult(unittest.TestResult):
|
||||
"""
|
||||
|
20
test/hook.py
20
test/hook.py
@ -100,9 +100,13 @@ class PollHook(Hook):
|
||||
signaldict = dict(
|
||||
(k, v) for v, k in reversed(sorted(signal.__dict__.items()))
|
||||
if v.startswith('SIG') and not v.startswith('SIG_'))
|
||||
|
||||
if self.testcase.vpp.returncode in signaldict:
|
||||
s = signaldict[abs(self.testcase.vpp.returncode)]
|
||||
else:
|
||||
s = "unknown"
|
||||
msg = "VPP subprocess died unexpectedly with returncode %d [%s]" % (
|
||||
self.testcase.vpp.returncode,
|
||||
signaldict[abs(self.testcase.vpp.returncode)])
|
||||
self.testcase.vpp.returncode, s)
|
||||
self.logger.critical(msg)
|
||||
core_path = self.testcase.tempdir + '/core'
|
||||
if os.path.isfile(core_path):
|
||||
@ -110,27 +114,27 @@ class PollHook(Hook):
|
||||
self.testcase.vpp_dead = True
|
||||
raise VppDiedError(msg)
|
||||
|
||||
def after_api(self, api_name, api_args):
|
||||
def before_api(self, api_name, api_args):
|
||||
"""
|
||||
Check if VPP died after executing an API
|
||||
Check if VPP died before executing an API
|
||||
|
||||
:param api_name: name of the API
|
||||
:param api_args: tuple containing the API arguments
|
||||
:raises VppDiedError: exception if VPP is not running anymore
|
||||
|
||||
"""
|
||||
super(PollHook, self).after_api(api_name, api_args)
|
||||
super(PollHook, self).before_api(api_name, api_args)
|
||||
self.poll_vpp()
|
||||
|
||||
def after_cli(self, cli):
|
||||
def before_cli(self, cli):
|
||||
"""
|
||||
Check if VPP died after executing a CLI
|
||||
Check if VPP died before executing a CLI
|
||||
|
||||
:param cli: CLI string
|
||||
:raises Exception: exception if VPP is not running anymore
|
||||
|
||||
"""
|
||||
super(PollHook, self).after_cli(cli)
|
||||
super(PollHook, self).before_cli(cli)
|
||||
self.poll_vpp()
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from abc import abstractmethod
|
||||
from abc import abstractmethod, ABCMeta
|
||||
|
||||
from scapy.layers.l2 import Ether, Raw
|
||||
from scapy.layers.inet import IP, UDP
|
||||
@ -8,6 +8,7 @@ from scapy.layers.inet import IP, UDP
|
||||
|
||||
class BridgeDomain(object):
|
||||
""" Bridge domain abstraction """
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
@property
|
||||
def frame_pg0_to_pg1(self):
|
||||
|
273
test/test_bfd.py
Normal file
273
test/test_bfd.py
Normal file
@ -0,0 +1,273 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import unittest
|
||||
import time
|
||||
from random import randint
|
||||
from bfd import *
|
||||
from framework import *
|
||||
from util import ppp
|
||||
|
||||
|
||||
class BFDCLITestCase(VppTestCase):
|
||||
"""Bidirectional Forwarding Detection (BFD) - CLI"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(BFDCLITestCase, cls).setUpClass()
|
||||
|
||||
try:
|
||||
cls.create_pg_interfaces([0])
|
||||
cls.pg0.config_ip4()
|
||||
cls.pg0.resolve_arp()
|
||||
|
||||
except Exception:
|
||||
super(BFDCLITestCase, cls).tearDownClass()
|
||||
raise
|
||||
|
||||
def test_add_bfd(self):
|
||||
""" 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))
|
||||
session.remove_vpp_config()
|
||||
session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
|
||||
session.add_vpp_config()
|
||||
self.logger.debug("Session state is %s" % str(session.state))
|
||||
session.remove_vpp_config()
|
||||
|
||||
def test_double_add(self):
|
||||
""" create the same BFD session twice (negative case) """
|
||||
session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
|
||||
session.add_vpp_config()
|
||||
try:
|
||||
session.add_vpp_config()
|
||||
except:
|
||||
session.remove_vpp_config()
|
||||
return
|
||||
session.remove_vpp_config()
|
||||
raise Exception("Expected failure while adding duplicate "
|
||||
"configuration")
|
||||
|
||||
|
||||
def create_packet(interface, ttl=255, src_port=50000, **kwargs):
|
||||
p = (Ether(src=interface.remote_mac, dst=interface.local_mac) /
|
||||
IP(src=interface.remote_ip4, dst=interface.local_ip4, ttl=ttl) /
|
||||
UDP(sport=src_port, dport=BFD.udp_dport) /
|
||||
BFD(*kwargs))
|
||||
return p
|
||||
|
||||
|
||||
def verify_ip(test, packet, local_ip, remote_ip):
|
||||
""" Verify correctness of IP layer. """
|
||||
ip = packet[IP]
|
||||
test.assert_equal(ip.src, local_ip, "IP source address")
|
||||
test.assert_equal(ip.dst, remote_ip, "IP destination address")
|
||||
test.assert_equal(ip.ttl, 255, "IP TTL")
|
||||
|
||||
|
||||
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")
|
||||
|
||||
|
||||
class BFDTestSession(object):
|
||||
|
||||
def __init__(self, test, interface, detect_mult=3):
|
||||
self.test = test
|
||||
self.interface = interface
|
||||
self.bfd_values = {
|
||||
'my_discriminator': 0,
|
||||
'desired_min_tx_interval': 500000,
|
||||
'detect_mult': detect_mult,
|
||||
'diag': BFDDiagCode.no_diagnostic,
|
||||
}
|
||||
|
||||
def update(self, **kwargs):
|
||||
self.bfd_values.update(kwargs)
|
||||
|
||||
def create_packet(self):
|
||||
packet = create_packet(self.interface)
|
||||
for name, value in self.bfd_values.iteritems():
|
||||
packet[BFD].setfieldval(name, value)
|
||||
return packet
|
||||
|
||||
def send_packet(self):
|
||||
p = self.create_packet()
|
||||
self.test.logger.debug(ppp("Sending packet:", p))
|
||||
self.test.pg0.add_stream([p])
|
||||
self.test.pg_start()
|
||||
|
||||
def verify_packet(self, packet):
|
||||
""" Verify correctness of BFD layer. """
|
||||
bfd = packet[BFD]
|
||||
self.test.assert_equal(bfd.version, 1, "BFD version")
|
||||
self.test.assert_equal(bfd.your_discriminator,
|
||||
self.bfd_values['my_discriminator'],
|
||||
"BFD - your discriminator")
|
||||
|
||||
|
||||
class BFDTestCase(VppTestCase):
|
||||
"""Bidirectional Forwarding Detection (BFD)"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(BFDTestCase, cls).setUpClass()
|
||||
try:
|
||||
cls.create_pg_interfaces([0, 1])
|
||||
cls.pg0.config_ip4()
|
||||
cls.pg0.generate_remote_hosts()
|
||||
cls.pg0.configure_ipv4_neighbors()
|
||||
cls.pg0.admin_up()
|
||||
cls.pg0.resolve_arp()
|
||||
|
||||
except Exception:
|
||||
super(BFDTestCase, cls).tearDownClass()
|
||||
raise
|
||||
|
||||
def setUp(self):
|
||||
self.vapi.want_bfd_events()
|
||||
self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4)
|
||||
self.vpp_session.add_vpp_config()
|
||||
self.vpp_session.admin_up()
|
||||
self.test_session = BFDTestSession(self, self.pg0)
|
||||
|
||||
def tearDown(self):
|
||||
self.vapi.want_bfd_events(enable_disable=0)
|
||||
if not self.vpp_dead:
|
||||
self.vpp_session.remove_vpp_config()
|
||||
super(BFDTestCase, self).tearDown()
|
||||
|
||||
def verify_event(self, event, expected_state):
|
||||
""" Verify correctness of event values. """
|
||||
e = event
|
||||
self.logger.debug("Event: %s" % repr(e))
|
||||
self.assert_equal(e.bs_index, self.vpp_session.bs_index,
|
||||
"BFD session index")
|
||||
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")
|
||||
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):
|
||||
p = self.pg0.wait_for_packet(timeout=timeout)
|
||||
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(self, p, self.pg0.local_ip4, self.pg0.remote_ip4)
|
||||
verify_udp(self, p)
|
||||
self.test_session.verify_packet(p)
|
||||
return p
|
||||
|
||||
def test_slow_timer(self):
|
||||
""" Slow timer """
|
||||
|
||||
self.pg_enable_capture([self.pg0])
|
||||
expected_packets = 10
|
||||
self.logger.info("Waiting for %d BFD packets" % expected_packets)
|
||||
self.wait_for_bfd_packet()
|
||||
for i in range(expected_packets):
|
||||
before = time.time()
|
||||
self.wait_for_bfd_packet()
|
||||
after = time.time()
|
||||
self.assert_in_range(
|
||||
after - before, 0.75, 1, "time between slow packets")
|
||||
before = after
|
||||
|
||||
def test_zero_remote_min_rx(self):
|
||||
""" Zero RemoteMinRxInterval """
|
||||
self.pg_enable_capture([self.pg0])
|
||||
p = self.wait_for_bfd_packet()
|
||||
self.test_session.update(my_discriminator=randint(0, 40000000),
|
||||
your_discriminator=p[BFD].my_discriminator,
|
||||
state=BFDState.init,
|
||||
required_min_rx_interval=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)
|
||||
|
||||
try:
|
||||
p = self.pg0.wait_for_packet(timeout=1)
|
||||
except:
|
||||
return
|
||||
raise Exception(ppp("Received unexpected BFD packet:", p))
|
||||
|
||||
def bfd_conn_up(self):
|
||||
self.pg_enable_capture([self.pg0])
|
||||
self.logger.info("Waiting for slow hello")
|
||||
p = self.wait_for_bfd_packet()
|
||||
self.logger.info("Sending Init")
|
||||
self.test_session.update(my_discriminator=randint(0, 40000000),
|
||||
your_discriminator=p[BFD].my_discriminator,
|
||||
state=BFDState.init,
|
||||
required_min_rx_interval=500000)
|
||||
self.test_session.send_packet()
|
||||
self.logger.info("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("Session is Up")
|
||||
self.test_session.update(state=BFDState.up)
|
||||
|
||||
def test_conn_up(self):
|
||||
""" Basic connection up """
|
||||
self.bfd_conn_up()
|
||||
|
||||
def test_hold_up(self):
|
||||
""" Hold BFD up """
|
||||
self.bfd_conn_up()
|
||||
for i in range(5):
|
||||
self.wait_for_bfd_packet()
|
||||
self.test_session.send_packet()
|
||||
|
||||
def test_conn_down(self):
|
||||
""" Session down after inactivity """
|
||||
self.bfd_conn_up()
|
||||
self.wait_for_bfd_packet()
|
||||
self.assert_equal(
|
||||
0, len(self.vapi.collect_events()),
|
||||
"number of bfd events")
|
||||
self.wait_for_bfd_packet()
|
||||
self.assert_equal(
|
||||
0, len(self.vapi.collect_events()),
|
||||
"number of bfd events")
|
||||
e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
|
||||
self.verify_event(e, expected_state=BFDState.down)
|
||||
|
||||
@unittest.skip("this test is not working yet")
|
||||
def test_large_required_min_rx(self):
|
||||
self.bfd_conn_up()
|
||||
interval = 5000000
|
||||
self.test_session.update(required_min_rx_interval=interval)
|
||||
self.test_session.send_packet()
|
||||
now = time.time()
|
||||
count = 1
|
||||
while time.time() < now + interval / 1000000:
|
||||
try:
|
||||
p = self.wait_for_bfd_packet()
|
||||
if count > 1:
|
||||
self.logger.error(ppp("Received unexpected packet:", p))
|
||||
count += 1
|
||||
except:
|
||||
pass
|
||||
self.assert_equal(count, 1, "number of packets received")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(testRunner=VppTestRunner)
|
22
test/util.py
22
test/util.py
@ -1,5 +1,6 @@
|
||||
import socket
|
||||
import sys
|
||||
from abc import abstractmethod, ABCMeta
|
||||
from cStringIO import StringIO
|
||||
|
||||
|
||||
@ -14,6 +15,27 @@ def ppp(headline, packet):
|
||||
return o.getvalue()
|
||||
|
||||
|
||||
class NumericConstant(object):
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
desc_dict = {}
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self, value):
|
||||
self._value = value
|
||||
|
||||
def __int__(self):
|
||||
return self._value
|
||||
|
||||
def __long__(self):
|
||||
return self._value
|
||||
|
||||
def __str__(self):
|
||||
if self._value in self.desc_dict:
|
||||
return self.desc_dict[self._value]
|
||||
return ""
|
||||
|
||||
|
||||
class Host(object):
|
||||
""" Generic test host "connected" to VPPs interface. """
|
||||
|
||||
|
79
test/vpp_object.py
Normal file
79
test/vpp_object.py
Normal file
@ -0,0 +1,79 @@
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
||||
class VppObject(object):
|
||||
""" Abstract vpp object """
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
def __init__(self):
|
||||
VppObjectRegistry().register(self)
|
||||
|
||||
@abstractmethod
|
||||
def add_vpp_config(self):
|
||||
""" Add the configuration for this object to vpp. """
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def query_vpp_config(self):
|
||||
"""Query the vpp configuration.
|
||||
|
||||
:return: True if the object is configured"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def remove_vpp_config(self):
|
||||
""" Remove the configuration for this object from vpp. """
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def object_id(self):
|
||||
""" Return a unique string representing this object. """
|
||||
pass
|
||||
|
||||
|
||||
class VppObjectRegistry(object):
|
||||
""" Class which handles automatic configuration cleanup. """
|
||||
_shared_state = {}
|
||||
|
||||
def __init__(self):
|
||||
self.__dict__ = self._shared_state
|
||||
if not hasattr(self, "_object_registry"):
|
||||
self._object_registry = []
|
||||
if not hasattr(self, "_object_dict"):
|
||||
self._object_dict = dict()
|
||||
|
||||
def register(self, o):
|
||||
""" Register an object in the registry. """
|
||||
if not o.unique_id() in self._object_dict:
|
||||
self._object_registry.append(o)
|
||||
self._object_dict[o.unique_id()] = o
|
||||
else:
|
||||
print "not adding duplicate %s" % o
|
||||
|
||||
def remove_vpp_config(self, logger):
|
||||
"""
|
||||
Remove configuration (if present) from vpp and then remove all objects
|
||||
from the registry.
|
||||
"""
|
||||
if not self._object_registry:
|
||||
logger.info("No objects registered for auto-cleanup.")
|
||||
return
|
||||
logger.info("Removing VPP configuration for registered objects")
|
||||
for o in reversed(self._object_registry):
|
||||
if o.query_vpp_config():
|
||||
logger.info("Removing %s", o)
|
||||
o.remove_vpp_config()
|
||||
else:
|
||||
logger.info("Skipping %s, configuration not present", o)
|
||||
failed = []
|
||||
for o in self._object_registry:
|
||||
if o.query_vpp_config():
|
||||
failed.append(o)
|
||||
self._object_registry = []
|
||||
self._object_dict = dict()
|
||||
if failed:
|
||||
logger.error("Couldn't remove configuration for object(s):")
|
||||
for x in failed:
|
||||
logger.error(repr(x))
|
||||
raise Exception("Couldn't remove configuration for object(s): %s" %
|
||||
(", ".join(str(x) for x in failed)))
|
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,10 @@
|
||||
import os
|
||||
import time
|
||||
from scapy.utils import wrpcap, rdpcap
|
||||
from scapy.utils import wrpcap, rdpcap, PcapReader
|
||||
from vpp_interface import VppInterface
|
||||
|
||||
from scapy.layers.l2 import Ether, ARP
|
||||
from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6ND_NA, \
|
||||
from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6ND_NA,\
|
||||
ICMPv6NDOptSrcLLAddr, ICMPv6NDOptDstLLAddr
|
||||
from util import ppp
|
||||
|
||||
@ -93,6 +93,7 @@ class VppPGInterface(VppInterface):
|
||||
pass
|
||||
# FIXME this should be an API, but no such exists atm
|
||||
self.test.vapi.cli(self.capture_cli)
|
||||
self._pcap_reader = None
|
||||
|
||||
def add_stream(self, pkts):
|
||||
"""
|
||||
@ -132,6 +133,41 @@ class VppPGInterface(VppInterface):
|
||||
return []
|
||||
return output
|
||||
|
||||
def wait_for_packet(self, timeout):
|
||||
"""
|
||||
Wait for next packet captured with a timeout
|
||||
|
||||
:param timeout: How long to wait for the packet
|
||||
|
||||
:returns: Captured packet if no packet arrived within timeout
|
||||
:raises Exception: if no packet arrives within timeout
|
||||
"""
|
||||
limit = time.time() + timeout
|
||||
if self._pcap_reader is None:
|
||||
self.test.logger.debug("Waiting for the capture file to appear")
|
||||
while time.time() < limit:
|
||||
if os.path.isfile(self.out_path):
|
||||
break
|
||||
time.sleep(0) # yield
|
||||
if os.path.isfile(self.out_path):
|
||||
self.test.logger.debug("Capture file appeared after %fs" %
|
||||
(time.time() - (limit - timeout)))
|
||||
self._pcap_reader = PcapReader(self.out_path)
|
||||
else:
|
||||
self.test.logger.debug("Timeout - capture file still nowhere")
|
||||
raise Exception("Packet didn't arrive within timeout")
|
||||
|
||||
self.test.logger.debug("Waiting for packet")
|
||||
while time.time() < limit:
|
||||
p = self._pcap_reader.recv()
|
||||
if p is not None:
|
||||
self.test.logger.debug("Packet received after %fs",
|
||||
(time.time() - (limit - timeout)))
|
||||
return p
|
||||
time.sleep(0) # yield
|
||||
self.test.logger.debug("Timeout - no packets received")
|
||||
raise Exception("Packet didn't arrive within timeout")
|
||||
|
||||
def create_arp_req(self):
|
||||
"""Create ARP request applicable for this interface"""
|
||||
return (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.remote_mac) /
|
||||
|
@ -197,7 +197,8 @@ _(to_netconf_server) \
|
||||
_(from_netconf_server) \
|
||||
_(to_netconf_client) \
|
||||
_(from_netconf_client) \
|
||||
_(oam_events)
|
||||
_(oam_events) \
|
||||
_(bfd_events)
|
||||
|
||||
/* WARNING: replicated in vpp/stats.h */
|
||||
typedef struct
|
||||
|
@ -349,6 +349,22 @@ nobase_include_HEADERS += \
|
||||
vnet/ip/udp.h \
|
||||
vnet/ip/udp_packet.h
|
||||
|
||||
########################################
|
||||
# Bidirectional Forwarding Detection
|
||||
########################################
|
||||
|
||||
nobase_include_HEADERS += \
|
||||
vnet/bfd/bfd_protocol.h \
|
||||
vnet/bfd/bfd_main.h \
|
||||
vnet/bfd/bfd_api.h \
|
||||
vnet/bfd/bfd_udp.h
|
||||
|
||||
libvnet_la_SOURCES += \
|
||||
vnet/bfd/bfd_api.h \
|
||||
vnet/bfd/bfd_udp.c \
|
||||
vnet/bfd/bfd_main.c \
|
||||
vnet/bfd/bfd_protocol.c
|
||||
|
||||
########################################
|
||||
# Layer 3 protocol: IPSec
|
||||
########################################
|
||||
|
@ -90,7 +90,9 @@ _(EXCEEDED_NUMBER_OF_PORTS_CAPACITY, -96, "Operation would exceed capacity of nu
|
||||
_(INVALID_ADDRESS_FAMILY, -97, "Invalid address family") \
|
||||
_(INVALID_SUB_SW_IF_INDEX, -98, "Invalid sub-interface sw_if_index") \
|
||||
_(TABLE_TOO_BIG, -99, "Table too big") \
|
||||
_(CANNOT_ENABLE_DISABLE_FEATURE, -100, "Cannot enable/disable feature")
|
||||
_(CANNOT_ENABLE_DISABLE_FEATURE, -100, "Cannot enable/disable feature") \
|
||||
_(BFD_EEXIST, -101, "Duplicate BFD session") \
|
||||
_(BFD_NOENT, -102, "No such BFD session")
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
46
vnet/vnet/bfd/bfd_api.h
Normal file
46
vnet/vnet/bfd/bfd_api.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2016 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* @file
|
||||
* @brief BFD global declarations
|
||||
*/
|
||||
#ifndef __included_bfd_api_h__
|
||||
#define __included_bfd_api_h__
|
||||
|
||||
#include <vnet/api_errno.h>
|
||||
#include <vnet/vnet.h>
|
||||
#include <vnet/ip/ip6_packet.h>
|
||||
#include <vnet/bfd/bfd_udp.h>
|
||||
|
||||
vnet_api_error_t bfd_udp_add_session (u32 sw_if_index, u32 desired_min_tx_us,
|
||||
u32 required_min_rx_us, u8 detect_mult,
|
||||
const ip46_address_t * local_addr,
|
||||
const ip46_address_t * peer_addr);
|
||||
|
||||
vnet_api_error_t bfd_udp_del_session (u32 sw_if_index,
|
||||
const ip46_address_t * local_addr,
|
||||
const ip46_address_t * peer_addr);
|
||||
|
||||
vnet_api_error_t bfd_session_set_flags (u32 bs_index, u8 admin_up_down);
|
||||
|
||||
#endif /* __included_bfd_api_h__ */
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
80
vnet/vnet/bfd/bfd_debug.h
Normal file
80
vnet/vnet/bfd/bfd_debug.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2016 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* @file
|
||||
* @brief BFD global declarations
|
||||
*/
|
||||
#ifndef __included_bfd_debug_h__
|
||||
#define __included_bfd_debug_h__
|
||||
|
||||
/* controls debug prints */
|
||||
#define BFD_DEBUG (0)
|
||||
|
||||
#if BFD_DEBUG
|
||||
#define BFD_DEBUG_FILE_DEF \
|
||||
static const char *__file = NULL; \
|
||||
if (!__file) \
|
||||
{ \
|
||||
__file = strrchr (__FILE__, '/'); \
|
||||
if (__file) \
|
||||
{ \
|
||||
++__file; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
__file = __FILE__; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define BFD_DBG(fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
BFD_DEBUG_FILE_DEF \
|
||||
u8 *_s = NULL; \
|
||||
vlib_main_t *vm = vlib_get_main (); \
|
||||
_s = format (_s, "%6.02f:DBG:%s:%d:%s():" fmt, vlib_time_now (vm), \
|
||||
__file, __LINE__, __func__, ##__VA_ARGS__); \
|
||||
printf ("%s\n", _s); \
|
||||
vec_free (_s); \
|
||||
} \
|
||||
while (0);
|
||||
|
||||
#define BFD_ERR(fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
BFD_DEBUG_FILE_DEF \
|
||||
u8 *_s = NULL; \
|
||||
vlib_main_t *vm = vlib_get_main (); \
|
||||
_s = format (_s, "%6.02f:ERR:%s:%d:%s():" fmt, vlib_time_now (vm), \
|
||||
__file, __LINE__, __func__, ##__VA_ARGS__); \
|
||||
printf ("%s\n", _s); \
|
||||
vec_free (_s); \
|
||||
} \
|
||||
while (0);
|
||||
|
||||
#else
|
||||
#define BFD_DBG(...)
|
||||
#define BFD_ERR(...)
|
||||
#endif
|
||||
|
||||
#endif /* __included_bfd_debug_h__ */
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
1
vnet/vnet/bfd/bfd_doc.md
Normal file
1
vnet/vnet/bfd/bfd_doc.md
Normal file
@ -0,0 +1 @@
|
||||
TODO
|
928
vnet/vnet/bfd/bfd_main.c
Normal file
928
vnet/vnet/bfd/bfd_main.c
Normal file
File diff suppressed because it is too large
Load Diff
218
vnet/vnet/bfd/bfd_main.h
Normal file
218
vnet/vnet/bfd/bfd_main.h
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2016 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* @file
|
||||
* @brief BFD global declarations
|
||||
*/
|
||||
#ifndef __included_bfd_main_h__
|
||||
#define __included_bfd_main_h__
|
||||
|
||||
#include <vppinfra/timing_wheel.h>
|
||||
#include <vnet/vnet.h>
|
||||
#include <vnet/bfd/bfd_protocol.h>
|
||||
#include <vnet/bfd/bfd_udp.h>
|
||||
|
||||
#define foreach_bfd_transport(F) \
|
||||
F (UDP4, "ip4-rewrite") \
|
||||
F (UDP6, "ip6-rewrite")
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define F(t, n) BFD_TRANSPORT_##t,
|
||||
foreach_bfd_transport (F)
|
||||
#undef F
|
||||
} bfd_transport_t;
|
||||
|
||||
#define foreach_bfd_mode(F) \
|
||||
F (asynchronous) \
|
||||
F (demand)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define F(x) BFD_MODE_##x,
|
||||
foreach_bfd_mode (F)
|
||||
#undef F
|
||||
} bfd_mode_e;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* index in bfd_main.sessions pool */
|
||||
uword bs_idx;
|
||||
|
||||
/* session state */
|
||||
bfd_state_e local_state;
|
||||
|
||||
/* local diagnostics */
|
||||
bfd_diag_code_e local_diag;
|
||||
|
||||
/* remote session state */
|
||||
bfd_state_e remote_state;
|
||||
|
||||
/* local discriminator */
|
||||
u32 local_discr;
|
||||
|
||||
/* remote discriminator */
|
||||
u32 remote_discr;
|
||||
|
||||
/* configured desired min tx interval (microseconds) */
|
||||
u32 config_desired_min_tx_us;
|
||||
|
||||
/* desired min tx interval (microseconds) */
|
||||
u32 desired_min_tx_us;
|
||||
|
||||
/* desired min tx interval (clocks) */
|
||||
u64 desired_min_tx_clocks;
|
||||
|
||||
/* required min rx interval */
|
||||
u32 required_min_rx_us;
|
||||
|
||||
/* remote min rx interval (microseconds) */
|
||||
u32 remote_min_rx_us;
|
||||
|
||||
/* remote min rx interval (clocks) */
|
||||
u64 remote_min_rx_clocks;
|
||||
|
||||
/* remote desired min tx interval */
|
||||
u32 remote_desired_min_tx_us;
|
||||
|
||||
/* 1 if in demand mode, 0 otherwise */
|
||||
u8 local_demand;
|
||||
|
||||
/* 1 if remote system sets demand mode, 0 otherwise */
|
||||
u8 remote_demand;
|
||||
|
||||
u8 local_detect_mult;
|
||||
u8 remote_detect_mult;
|
||||
|
||||
/* set to value of timer in timing wheel, 0 if not set */
|
||||
u64 wheel_time_clocks;
|
||||
|
||||
/* transmit interval */
|
||||
u64 transmit_interval_clocks;
|
||||
|
||||
/* next time at which to transmit a packet */
|
||||
u64 tx_timeout_clocks;
|
||||
|
||||
/* timestamp of last packet received */
|
||||
u64 last_rx_clocks;
|
||||
|
||||
/* detection time */
|
||||
u64 detection_time_clocks;
|
||||
|
||||
/* transport type for this session */
|
||||
bfd_transport_t transport;
|
||||
|
||||
union
|
||||
{
|
||||
bfd_udp_session_t udp;
|
||||
};
|
||||
} bfd_session_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 client_index;
|
||||
u32 client_pid;
|
||||
} event_subscriber_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* pool of bfd sessions context data */
|
||||
bfd_session_t *sessions;
|
||||
|
||||
/* timing wheel for scheduling timeouts */
|
||||
timing_wheel_t wheel;
|
||||
|
||||
/* hashmap - bfd session by discriminator */
|
||||
u32 *session_by_disc;
|
||||
|
||||
/* background process node index */
|
||||
u32 bfd_process_node_index;
|
||||
|
||||
/* convenience variables */
|
||||
vlib_main_t *vlib_main;
|
||||
vnet_main_t *vnet_main;
|
||||
|
||||
/* cpu clocks per second */
|
||||
f64 cpu_cps;
|
||||
|
||||
/* for generating random numbers */
|
||||
u32 random_seed;
|
||||
|
||||
/* pool of event subscribers */
|
||||
//event_subscriber_t *subscribers;
|
||||
|
||||
} bfd_main_t;
|
||||
|
||||
extern bfd_main_t bfd_main;
|
||||
|
||||
/* Packet counters */
|
||||
#define foreach_bfd_error(F) \
|
||||
F (NONE, "good bfd packets (processed)") \
|
||||
F (BAD, "invalid bfd packets") \
|
||||
F (DISABLED, "bfd packets received on disabled interfaces")
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define F(sym, str) BFD_ERROR_##sym,
|
||||
foreach_bfd_error (F)
|
||||
#undef F
|
||||
BFD_N_ERROR,
|
||||
} bfd_error_t;
|
||||
|
||||
/* bfd packet trace capture */
|
||||
typedef struct
|
||||
{
|
||||
u32 len;
|
||||
u8 data[400];
|
||||
} bfd_input_trace_t;
|
||||
|
||||
enum
|
||||
{
|
||||
BFD_EVENT_RESCHEDULE = 1,
|
||||
BFD_EVENT_NEW_SESSION,
|
||||
} bfd_process_event_e;
|
||||
|
||||
bfd_error_t bfd_input (vlib_main_t * vm, vlib_buffer_t * b0, u32 bi0);
|
||||
u8 *bfd_input_format_trace (u8 * s, va_list * args);
|
||||
|
||||
bfd_session_t *bfd_get_session (bfd_main_t * bm, bfd_transport_t t);
|
||||
void bfd_put_session (bfd_main_t * bm, bfd_session_t * bs);
|
||||
bfd_session_t *bfd_find_session_by_idx (bfd_main_t * bm, uword bs_idx);
|
||||
bfd_session_t *bfd_find_session_by_disc (bfd_main_t * bm, u32 disc);
|
||||
void bfd_session_start (bfd_main_t * bm, bfd_session_t * bs);
|
||||
void bfd_consume_pkt (bfd_main_t * bm, const bfd_pkt_t * bfd, u32 bs_idx);
|
||||
int bfd_verify_pkt_common (const bfd_pkt_t * pkt);
|
||||
int bfd_verify_pkt_session (const bfd_pkt_t * pkt, u16 pkt_size,
|
||||
const bfd_session_t * bs);
|
||||
void bfd_event (bfd_main_t * bm, bfd_session_t * bs);
|
||||
void bfd_send_final (vlib_main_t * vm, vlib_buffer_t * b, bfd_session_t * bs);
|
||||
u8 *format_bfd_session (u8 * s, va_list * args);
|
||||
|
||||
|
||||
#define USEC_PER_MS 1000LL
|
||||
#define USEC_PER_SECOND (1000 * USEC_PER_MS)
|
||||
|
||||
/* default, slow transmission interval for BFD packets, per spec at least 1s */
|
||||
#define BFD_DEFAULT_DESIRED_MIN_TX_US USEC_PER_SECOND
|
||||
|
||||
#endif /* __included_bfd_main_h__ */
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
74
vnet/vnet/bfd/bfd_protocol.c
Normal file
74
vnet/vnet/bfd/bfd_protocol.c
Normal file
@ -0,0 +1,74 @@
|
||||
#include <vnet/bfd/bfd_protocol.h>
|
||||
|
||||
u8 bfd_pkt_get_version (const bfd_pkt_t *pkt)
|
||||
{
|
||||
return pkt->head.vers_diag >> 5;
|
||||
}
|
||||
|
||||
void bfd_pkt_set_version (bfd_pkt_t *pkt, int version)
|
||||
{
|
||||
pkt->head.vers_diag =
|
||||
(version << 5) | (pkt->head.vers_diag & ((1 << 5) - 1));
|
||||
}
|
||||
|
||||
u8 bfd_pkt_get_diag_code (const bfd_pkt_t *pkt)
|
||||
{
|
||||
return pkt->head.vers_diag & ((1 << 5) - 1);
|
||||
}
|
||||
|
||||
void bfd_pkt_set_diag_code (bfd_pkt_t *pkt, int value)
|
||||
{
|
||||
pkt->head.vers_diag =
|
||||
(pkt->head.vers_diag & ~((1 << 5) - 1)) | (value & ((1 << 5) - 1));
|
||||
}
|
||||
|
||||
u8 bfd_pkt_get_state (const bfd_pkt_t *pkt)
|
||||
{
|
||||
return pkt->head.sta_flags >> 6;
|
||||
}
|
||||
|
||||
void bfd_pkt_set_state (bfd_pkt_t *pkt, int value)
|
||||
{
|
||||
pkt->head.sta_flags = (value << 6) | (pkt->head.sta_flags & ((1 << 6) - 1));
|
||||
}
|
||||
|
||||
u8 bfd_pkt_get_poll (const bfd_pkt_t *pkt)
|
||||
{
|
||||
return (pkt->head.sta_flags >> 5) & 1;
|
||||
}
|
||||
|
||||
void bfd_pkt_set_final (bfd_pkt_t *pkt) { pkt->head.sta_flags |= 1 << 5; }
|
||||
|
||||
u8 bfd_pkt_get_final (const bfd_pkt_t *pkt)
|
||||
{
|
||||
return (pkt->head.sta_flags >> 4) & 1;
|
||||
}
|
||||
|
||||
void bfd_pkt_set_poll (bfd_pkt_t *pkt);
|
||||
u8 bfd_pkt_get_control_plane_independent (const bfd_pkt_t *pkt)
|
||||
{
|
||||
return (pkt->head.sta_flags >> 3) & 1;
|
||||
}
|
||||
|
||||
void bfd_pkt_set_control_plane_independent (bfd_pkt_t *pkt);
|
||||
|
||||
u8 bfd_pkt_get_auth_present (const bfd_pkt_t *pkt)
|
||||
{
|
||||
return (pkt->head.sta_flags >> 2) & 1;
|
||||
}
|
||||
|
||||
void bfd_pkt_set_auth_present (bfd_pkt_t *pkt);
|
||||
|
||||
u8 bfd_pkt_get_demand (const bfd_pkt_t *pkt)
|
||||
{
|
||||
return (pkt->head.sta_flags >> 1) & 1;
|
||||
}
|
||||
|
||||
void bfd_pkt_set_demand (bfd_pkt_t *pkt) { pkt->head.sta_flags |= 1 << 1; }
|
||||
|
||||
u8 bfd_pkt_get_multipoint (const bfd_pkt_t *pkt)
|
||||
{
|
||||
return pkt->head.sta_flags & 1;
|
||||
}
|
||||
|
||||
void bfd_pkt_set_multipoint (bfd_pkt_t *pkt);
|
154
vnet/vnet/bfd/bfd_protocol.h
Normal file
154
vnet/vnet/bfd/bfd_protocol.h
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2016 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef __included_bfd_protocol_h__
|
||||
#define __included_bfd_protocol_h__
|
||||
/**
|
||||
* @file
|
||||
* @brief BFD protocol declarations
|
||||
*/
|
||||
|
||||
#include <vppinfra/types.h>
|
||||
#include <vppinfra/clib.h>
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
typedef CLIB_PACKED (struct {
|
||||
/*
|
||||
An optional Authentication Section MAY be present:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Auth Type | Auth Len | Authentication Data... |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
u8 type;
|
||||
u8 len;
|
||||
u8 data[0];
|
||||
}) bfd_auth_t;
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
typedef CLIB_PACKED (struct {
|
||||
/*
|
||||
The Mandatory Section of a BFD Control packet has the following
|
||||
format:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|Vers | Diag |Sta|P|F|C|A|D|M| Detect Mult | Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| My Discriminator |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Your Discriminator |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Desired Min TX Interval |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Required Min RX Interval |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Required Min Echo RX Interval |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
struct
|
||||
{
|
||||
u8 vers_diag;
|
||||
u8 sta_flags;
|
||||
u8 detect_mult;
|
||||
u8 length;
|
||||
} head;
|
||||
u32 my_disc;
|
||||
u32 your_disc;
|
||||
u32 des_min_tx;
|
||||
u32 req_min_rx;
|
||||
u32 req_min_echo_rx;
|
||||
}) bfd_pkt_t;
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
typedef CLIB_PACKED (struct {
|
||||
bfd_pkt_t pkt;
|
||||
bfd_auth_t auth;
|
||||
}) bfd_pkt_with_auth_t;
|
||||
/* *INDENT-ON* */
|
||||
|
||||
u8 bfd_pkt_get_version (const bfd_pkt_t * pkt);
|
||||
void bfd_pkt_set_version (bfd_pkt_t * pkt, int version);
|
||||
u8 bfd_pkt_get_diag_code (const bfd_pkt_t * pkt);
|
||||
void bfd_pkt_set_diag_code (bfd_pkt_t * pkt, int value);
|
||||
u8 bfd_pkt_get_state (const bfd_pkt_t * pkt);
|
||||
void bfd_pkt_set_state (bfd_pkt_t * pkt, int value);
|
||||
u8 bfd_pkt_get_poll (const bfd_pkt_t * pkt);
|
||||
void bfd_pkt_set_final (bfd_pkt_t * pkt);
|
||||
u8 bfd_pkt_get_final (const bfd_pkt_t * pkt);
|
||||
void bfd_pkt_set_poll (bfd_pkt_t * pkt);
|
||||
u8 bfd_pkt_get_control_plane_independent (const bfd_pkt_t * pkt);
|
||||
void bfd_pkt_set_control_plane_independent (bfd_pkt_t * pkt);
|
||||
u8 bfd_pkt_get_auth_present (const bfd_pkt_t * pkt);
|
||||
void bfd_pkt_set_auth_present (bfd_pkt_t * pkt);
|
||||
u8 bfd_pkt_get_demand (const bfd_pkt_t * pkt);
|
||||
void bfd_pkt_set_demand (bfd_pkt_t * pkt);
|
||||
u8 bfd_pkt_get_multipoint (const bfd_pkt_t * pkt);
|
||||
void bfd_pkt_set_multipoint (bfd_pkt_t * pkt);
|
||||
|
||||
/* BFD diagnostic codes */
|
||||
#define foreach_bfd_diag_code(F) \
|
||||
F (0, no_diag, "No Diagnostic") \
|
||||
F (1, det_time_exp, "Control Detection Time Expired") \
|
||||
F (2, echo_failed, "Echo Function Failed") \
|
||||
F (3, neighbor_sig_down, "Neighbor Signaled Session Down") \
|
||||
F (4, fwd_plain_reset, "Forwarding Plane Reset") \
|
||||
F (5, path_down, "Path Down") \
|
||||
F (6, concat_path_down, "Concatenated Path Down") \
|
||||
F (7, admin_down, "Administratively Down") \
|
||||
F (8, reverse_concat_path_down, "Reverse Concatenated Path Down")
|
||||
|
||||
#define BFD_DIAG_CODE_NAME(t) BFD_DIAG_CODE_##t
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define F(n, t, s) BFD_DIAG_CODE_NAME (t) = n,
|
||||
foreach_bfd_diag_code (F)
|
||||
#undef F
|
||||
} bfd_diag_code_e;
|
||||
|
||||
const char *bfd_diag_code_string (bfd_diag_code_e diag);
|
||||
|
||||
/* BFD state values */
|
||||
#define foreach_bfd_state(F) \
|
||||
F (0, admin_down, "AdminDown") \
|
||||
F (1, down, "Down") \
|
||||
F (2, init, "Init") \
|
||||
F (3, up, "Up")
|
||||
|
||||
#define BFD_STATE_NAME(t) BFD_STATE_##t
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define F(n, t, s) BFD_STATE_NAME (t) = n,
|
||||
foreach_bfd_state (F)
|
||||
#undef F
|
||||
} bfd_state_e;
|
||||
|
||||
const char *bfd_state_string (bfd_state_e state);
|
||||
|
||||
#endif /* __included_bfd_protocol_h__ */
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
610
vnet/vnet/bfd/bfd_udp.c
Normal file
610
vnet/vnet/bfd/bfd_udp.c
Normal file
File diff suppressed because it is too large
Load Diff
56
vnet/vnet/bfd/bfd_udp.h
Normal file
56
vnet/vnet/bfd/bfd_udp.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* * Copyright (c) 2011-2016 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* @file
|
||||
* @brief BFD global declarations
|
||||
*/
|
||||
|
||||
#ifndef __included_bfd_udp_h__
|
||||
#define __included_bfd_udp_h__
|
||||
|
||||
#include <vppinfra/clib.h>
|
||||
#include <vnet/adj/adj_types.h>
|
||||
#include <vnet/ip/ip6_packet.h>
|
||||
|
||||
#define BFD_UDP_KEY_BODY
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
typedef CLIB_PACKED (struct {
|
||||
|
||||
u32 sw_if_index;
|
||||
ip46_address_t local_addr;
|
||||
ip46_address_t peer_addr;
|
||||
|
||||
}) bfd_udp_key_t;
|
||||
/* *INDENT-ON* */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bfd_udp_key_t key;
|
||||
|
||||
adj_index_t adj_index;
|
||||
} bfd_udp_session_t;
|
||||
|
||||
void bfd_add_udp_transport (vlib_main_t * vm, vlib_buffer_t * b,
|
||||
bfd_udp_session_t * bs);
|
||||
|
||||
#endif /* __included_bfd_udp_h__ */
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
18
vnet/vnet/bfd/dir.dox
Normal file
18
vnet/vnet/bfd/dir.dox
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
@dir vnet/vnet/bfd
|
||||
@brief Bidirectional Forwarding Detection (BFD) implementation
|
||||
*/
|
@ -143,7 +143,11 @@ typedef struct
|
||||
u8 code;
|
||||
u32 data;
|
||||
} icmp;
|
||||
|
||||
/* IP header - saved by ip*_local nodes */
|
||||
void *header;
|
||||
};
|
||||
|
||||
} ip;
|
||||
|
||||
/*
|
||||
|
@ -1472,9 +1472,12 @@ ip4_local (vlib_main_t * vm,
|
||||
ip0 = vlib_buffer_get_current (p0);
|
||||
ip1 = vlib_buffer_get_current (p1);
|
||||
|
||||
fib_index0 = vec_elt (im->fib_index_by_sw_if_index,
|
||||
vnet_buffer(p0)->sw_if_index[VLIB_RX]);
|
||||
fib_index1 = vec_elt (im->fib_index_by_sw_if_index,
|
||||
vnet_buffer (p0)->ip.header = ip0;
|
||||
vnet_buffer (p1)->ip.header = ip1;
|
||||
|
||||
fib_index0 = vec_elt (im->fib_index_by_sw_if_index,
|
||||
vnet_buffer (p0)->sw_if_index[VLIB_RX]);
|
||||
fib_index1 = vec_elt (im->fib_index_by_sw_if_index,
|
||||
vnet_buffer(p1)->sw_if_index[VLIB_RX]);
|
||||
|
||||
mtrie0 = &ip4_fib_get (fib_index0)->mtrie;
|
||||
@ -1679,6 +1682,8 @@ ip4_local (vlib_main_t * vm,
|
||||
|
||||
ip0 = vlib_buffer_get_current (p0);
|
||||
|
||||
vnet_buffer (p0)->ip.header = ip0;
|
||||
|
||||
fib_index0 = vec_elt (im->fib_index_by_sw_if_index,
|
||||
vnet_buffer(p0)->sw_if_index[VLIB_RX]);
|
||||
|
||||
@ -3294,4 +3299,3 @@ VLIB_CLI_COMMAND (set_ip_classify_command, static) = {
|
||||
.function = set_ip_classify_command_fn,
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
|
@ -37,6 +37,7 @@ typedef enum {
|
||||
_ (67, dhcp_to_server) \
|
||||
_ (68, dhcp_to_client) \
|
||||
_ (500, ikev2) \
|
||||
_ (3784, bfd4) \
|
||||
_ (4341, lisp_gpe) \
|
||||
_ (4342, lisp_cp) \
|
||||
_ (4739, ipfix) \
|
||||
@ -49,6 +50,7 @@ _ (6633, vpath_3)
|
||||
#define foreach_udp6_dst_port \
|
||||
_ (547, dhcpv6_to_server) \
|
||||
_ (546, dhcpv6_to_client) \
|
||||
_ (3784, bfd6) \
|
||||
_ (4341, lisp_gpe6) \
|
||||
_ (4342, lisp_cp6) \
|
||||
_ (4790, vxlan6_gpe) \
|
||||
|
@ -117,6 +117,8 @@
|
||||
#include <vpp-api/vpe_msg_enum.h>
|
||||
#include <vnet/span/span.h>
|
||||
|
||||
#include <vnet/bfd/bfd_main.h>
|
||||
#include <vnet/bfd/bfd_api.h>
|
||||
#include <vnet/fib/ip6_fib.h>
|
||||
#include <vnet/fib/ip4_fib.h>
|
||||
#include <vnet/fib/fib_api.h>
|
||||
@ -302,7 +304,12 @@ _(PUNT, punt) \
|
||||
_(FLOW_CLASSIFY_SET_INTERFACE, flow_classify_set_interface) \
|
||||
_(FLOW_CLASSIFY_DUMP, flow_classify_dump) \
|
||||
_(IPSEC_SPD_DUMP, ipsec_spd_dump) \
|
||||
_(FEATURE_ENABLE_DISABLE, feature_enable_disable)
|
||||
_(FEATURE_ENABLE_DISABLE, feature_enable_disable) \
|
||||
_(BFD_UDP_ADD, bfd_udp_add) \
|
||||
_(BFD_UDP_DEL, bfd_udp_del) \
|
||||
_(BFD_UDP_SESSION_DUMP, bfd_udp_session_dump) \
|
||||
_(BFD_SESSION_SET_FLAGS, bfd_session_set_flags) \
|
||||
_(WANT_BFD_EVENTS, want_bfd_events)
|
||||
|
||||
#define QUOTE_(x) #x
|
||||
#define QUOTE(x) QUOTE_(x)
|
||||
@ -343,6 +350,7 @@ vl_api_memclnt_delete_callback (u32 client_index)
|
||||
}
|
||||
|
||||
pub_sub_handler (oam_events, OAM_EVENTS);
|
||||
pub_sub_handler (bfd_events, BFD_EVENTS);
|
||||
|
||||
#define RESOLUTION_EVENT 1
|
||||
#define RESOLUTION_PENDING_EVENT 2
|
||||
@ -6766,6 +6774,163 @@ static void
|
||||
BAD_SW_IF_INDEX_LABEL;
|
||||
|
||||
REPLY_MACRO (VL_API_L2_INTERFACE_PBB_TAG_REWRITE_REPLY);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vl_api_bfd_udp_add_t_handler (vl_api_bfd_udp_add_t * mp)
|
||||
{
|
||||
vl_api_bfd_udp_add_reply_t *rmp;
|
||||
int rv;
|
||||
|
||||
VALIDATE_SW_IF_INDEX (mp);
|
||||
|
||||
ip46_address_t local_addr;
|
||||
memset (&local_addr, 0, sizeof (local_addr));
|
||||
ip46_address_t peer_addr;
|
||||
memset (&peer_addr, 0, sizeof (peer_addr));
|
||||
if (mp->is_ipv6)
|
||||
{
|
||||
clib_memcpy (&local_addr.ip6, mp->local_addr, sizeof (local_addr.ip6));
|
||||
clib_memcpy (&peer_addr.ip6, mp->peer_addr, sizeof (peer_addr.ip6));
|
||||
}
|
||||
else
|
||||
{
|
||||
clib_memcpy (&local_addr.ip4, mp->local_addr, sizeof (local_addr.ip4));
|
||||
clib_memcpy (&peer_addr.ip4, mp->peer_addr, sizeof (peer_addr.ip4));
|
||||
}
|
||||
|
||||
rv = bfd_udp_add_session (clib_net_to_host_u32 (mp->sw_if_index),
|
||||
clib_net_to_host_u32 (mp->desired_min_tx),
|
||||
clib_net_to_host_u32 (mp->required_min_rx),
|
||||
mp->detect_mult, &local_addr, &peer_addr);
|
||||
|
||||
BAD_SW_IF_INDEX_LABEL;
|
||||
REPLY_MACRO (VL_API_BFD_UDP_ADD_REPLY);
|
||||
}
|
||||
|
||||
static void
|
||||
vl_api_bfd_udp_del_t_handler (vl_api_bfd_udp_del_t * mp)
|
||||
{
|
||||
vl_api_bfd_udp_del_reply_t *rmp;
|
||||
int rv;
|
||||
|
||||
VALIDATE_SW_IF_INDEX (mp);
|
||||
|
||||
ip46_address_t local_addr;
|
||||
memset (&local_addr, 0, sizeof (local_addr));
|
||||
ip46_address_t peer_addr;
|
||||
memset (&peer_addr, 0, sizeof (peer_addr));
|
||||
if (mp->is_ipv6)
|
||||
{
|
||||
clib_memcpy (&local_addr.ip6, mp->local_addr, sizeof (local_addr.ip6));
|
||||
clib_memcpy (&peer_addr.ip6, mp->peer_addr, sizeof (peer_addr.ip6));
|
||||
}
|
||||
else
|
||||
{
|
||||
clib_memcpy (&local_addr.ip4, mp->local_addr, sizeof (local_addr.ip4));
|
||||
clib_memcpy (&peer_addr.ip4, mp->peer_addr, sizeof (peer_addr.ip4));
|
||||
}
|
||||
|
||||
rv =
|
||||
bfd_udp_del_session (clib_net_to_host_u32 (mp->sw_if_index), &local_addr,
|
||||
&peer_addr);
|
||||
|
||||
BAD_SW_IF_INDEX_LABEL;
|
||||
REPLY_MACRO (VL_API_BFD_UDP_DEL_REPLY);
|
||||
}
|
||||
|
||||
void
|
||||
send_bfd_udp_session_details (unix_shared_memory_queue_t * q, u32 context,
|
||||
bfd_session_t * bs)
|
||||
{
|
||||
if (bs->transport != BFD_TRANSPORT_UDP4 &&
|
||||
bs->transport != BFD_TRANSPORT_UDP6)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
vl_api_bfd_udp_session_details_t *mp = vl_msg_api_alloc (sizeof (*mp));
|
||||
memset (mp, 0, sizeof (*mp));
|
||||
mp->_vl_msg_id = ntohs (VL_API_BFD_UDP_SESSION_DETAILS);
|
||||
mp->context = context;
|
||||
mp->bs_index = clib_host_to_net_u32 (bs->bs_idx);
|
||||
mp->state = bs->local_state;
|
||||
bfd_udp_session_t *bus = &bs->udp;
|
||||
bfd_udp_key_t *key = &bus->key;
|
||||
mp->sw_if_index = clib_host_to_net_u32 (key->sw_if_index);
|
||||
mp->is_ipv6 = !(ip46_address_is_ip4 (&key->local_addr));
|
||||
if (mp->is_ipv6)
|
||||
{
|
||||
clib_memcpy (mp->local_addr, &key->local_addr,
|
||||
sizeof (key->local_addr));
|
||||
clib_memcpy (mp->peer_addr, &key->peer_addr, sizeof (key->peer_addr));
|
||||
}
|
||||
else
|
||||
{
|
||||
clib_memcpy (mp->local_addr, &key->local_addr.ip4.data,
|
||||
sizeof (&key->local_addr.ip4.data));
|
||||
clib_memcpy (mp->peer_addr, &key->peer_addr.ip4.data,
|
||||
sizeof (&key->peer_addr.ip4.data));
|
||||
}
|
||||
|
||||
vl_msg_api_send_shmem (q, (u8 *) & mp);
|
||||
}
|
||||
|
||||
void
|
||||
bfd_event (bfd_main_t * bm, bfd_session_t * bs)
|
||||
{
|
||||
vpe_api_main_t *vam = &vpe_api_main;
|
||||
vpe_client_registration_t *reg;
|
||||
unix_shared_memory_queue_t *q;
|
||||
/* *INDENT-OFF* */
|
||||
pool_foreach (reg, vam->bfd_events_registrations, ({
|
||||
q = vl_api_client_index_to_input_queue (reg->client_index);
|
||||
if (q)
|
||||
{
|
||||
switch (bs->transport)
|
||||
{
|
||||
case BFD_TRANSPORT_UDP4:
|
||||
/* fallthrough */
|
||||
case BFD_TRANSPORT_UDP6:
|
||||
send_bfd_udp_session_details (q, 0, bs);
|
||||
}
|
||||
}
|
||||
}));
|
||||
/* *INDENT-ON* */
|
||||
}
|
||||
|
||||
static void
|
||||
vl_api_bfd_udp_session_dump_t_handler (vl_api_bfd_udp_session_dump_t * mp)
|
||||
{
|
||||
unix_shared_memory_queue_t *q;
|
||||
|
||||
q = vl_api_client_index_to_input_queue (mp->client_index);
|
||||
|
||||
if (q == 0)
|
||||
return;
|
||||
|
||||
bfd_session_t *bs = NULL;
|
||||
/* *INDENT-OFF* */
|
||||
pool_foreach (bs, bfd_main.sessions, ({
|
||||
if (bs->transport == BFD_TRANSPORT_UDP4 ||
|
||||
bs->transport == BFD_TRANSPORT_UDP6)
|
||||
send_bfd_udp_session_details (q, mp->context, bs);
|
||||
}));
|
||||
/* *INDENT-ON* */
|
||||
}
|
||||
|
||||
static void
|
||||
vl_api_bfd_session_set_flags_t_handler (vl_api_bfd_session_set_flags_t * mp)
|
||||
{
|
||||
vl_api_bfd_session_set_flags_reply_t *rmp;
|
||||
int rv;
|
||||
|
||||
rv =
|
||||
bfd_session_set_flags (clib_net_to_host_u32 (mp->bs_index),
|
||||
mp->admin_up_down);
|
||||
|
||||
REPLY_MACRO (VL_API_BFD_SESSION_SET_FLAGS_REPLY);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -7090,6 +7255,7 @@ vpe_api_init (vlib_main_t * vm)
|
||||
am->to_netconf_client_registration_hash = hash_create (0, sizeof (uword));
|
||||
am->from_netconf_client_registration_hash = hash_create (0, sizeof (uword));
|
||||
am->oam_events_registration_hash = hash_create (0, sizeof (uword));
|
||||
am->bfd_events_registration_hash = hash_create (0, sizeof (uword));
|
||||
|
||||
vl_api_init (vm);
|
||||
vl_set_memory_region_name ("/vpe-api");
|
||||
|
@ -4656,6 +4656,191 @@ define feature_enable_disable_reply
|
||||
i32 retval;
|
||||
};
|
||||
|
||||
/** \brief Configure BFD feature
|
||||
@param client_index - opaque cookie to identify the sender
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param slow_timer - slow timer (seconds)
|
||||
@param min_tx - desired min tx interval
|
||||
@param min_rx - desired min rx interval
|
||||
@param detect_mult - desired detection multiplier
|
||||
*/
|
||||
define bfd_set_config {
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
u32 slow_timer;
|
||||
u32 min_tx;
|
||||
u32 min_rx;
|
||||
u8 detect_mult;
|
||||
};
|
||||
|
||||
/** \brief Configure BFD feature response
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param retval - return code for the request
|
||||
*/
|
||||
define bfd_set_config_reply {
|
||||
u32 context;
|
||||
i32 retval;
|
||||
};
|
||||
|
||||
/** \brief Get BFD configuration
|
||||
*/
|
||||
define bfd_get_config {
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
};
|
||||
|
||||
/** \brief Get BFD configuration response
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param retval - return code for the request
|
||||
@param slow_timer - slow timer (seconds)
|
||||
@param min_tx - desired min tx interval
|
||||
@param min_rx - desired min rx interval
|
||||
@param detect_mult - desired detection multiplier
|
||||
*/
|
||||
define bfd_get_config_reply {
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
u32 slow_timer;
|
||||
u32 min_tx;
|
||||
u32 min_rx;
|
||||
u8 detect_mult;
|
||||
};
|
||||
|
||||
/** \brief Add UDP BFD session on interface
|
||||
@param client_index - opaque cookie to identify the sender
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param sw_if_index - sw index of the interface
|
||||
@param desired_min_tx - desired min transmit interval (microseconds)
|
||||
@param required_min_rx - required min receive interval (microseconds)
|
||||
@param detect_mult - detect multiplier (# of packets missed between connection goes down)
|
||||
@param local_addr - local address
|
||||
@param peer_addr - peer address
|
||||
@param is_ipv6 - local_addr, peer_addr are IPv6 if non-zero, otherwise IPv4
|
||||
*/
|
||||
define bfd_udp_add {
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
u32 sw_if_index;
|
||||
u32 desired_min_tx;
|
||||
u32 required_min_rx;
|
||||
u8 local_addr[16];
|
||||
u8 peer_addr[16];
|
||||
u8 is_ipv6;
|
||||
u8 detect_mult;
|
||||
};
|
||||
|
||||
/** \brief Add UDP BFD session response
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param retval - return code for the request
|
||||
@param bs_index - index of the session created
|
||||
*/
|
||||
define bfd_udp_add_reply {
|
||||
u32 context;
|
||||
i32 retval;
|
||||
u32 bs_index;
|
||||
};
|
||||
|
||||
/** \brief Delete UDP BFD session on interface
|
||||
@param client_index - opaque cookie to identify the sender
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param sw_if_index - sw index of the interface
|
||||
@param local_addr - local address
|
||||
@param peer_addr - peer address
|
||||
@param is_ipv6 - local_addr, peer_addr are IPv6 if non-zero, otherwise IPv4
|
||||
*/
|
||||
define bfd_udp_del {
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
u32 sw_if_index;
|
||||
u8 local_addr[16];
|
||||
u8 peer_addr[16];
|
||||
u8 is_ipv6;
|
||||
};
|
||||
|
||||
/** \brief Delete UDP BFD session response
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param retval - return code for the request
|
||||
*/
|
||||
define bfd_udp_del_reply {
|
||||
u32 context;
|
||||
i32 retval;
|
||||
};
|
||||
|
||||
/** \brief Get all BFD sessions
|
||||
@param client_index - opaque cookie to identify the sender
|
||||
@param context - sender context, to match reply w/ request
|
||||
*/
|
||||
define bfd_udp_session_dump {
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
};
|
||||
|
||||
/** \brief BFD session details structure
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param bs_index - index of the session
|
||||
@param sw_if_index - sw index of the interface
|
||||
@param local_addr - local address
|
||||
@param peer_addr - peer address
|
||||
@param is_ipv6 - local_addr, peer_addr are IPv6 if non-zero, otherwise IPv4
|
||||
@param state - session state
|
||||
*/
|
||||
define bfd_udp_session_details {
|
||||
u32 context;
|
||||
u32 bs_index;
|
||||
u32 sw_if_index;
|
||||
u8 local_addr[16];
|
||||
u8 peer_addr[16];
|
||||
u8 is_ipv6;
|
||||
u8 state;
|
||||
};
|
||||
|
||||
/** \brief Set flags of BFD session
|
||||
@param client_index - opaque cookie to identify the sender
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param bs_index - index of the bfd session to set flags on
|
||||
@param admin_up_down - set the admin state, 1 = up, 0 = down
|
||||
*/
|
||||
define bfd_session_set_flags {
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
u32 bs_index;
|
||||
u8 admin_up_down;
|
||||
};
|
||||
|
||||
/** \brief Reply to bfd_session_set_flags
|
||||
@param context - sender context which was passed in the request
|
||||
@param retval - return code of the set flags request
|
||||
*/
|
||||
define bfd_session_set_flags_reply
|
||||
{
|
||||
u32 context;
|
||||
i32 retval;
|
||||
};
|
||||
|
||||
/** \brief Register for BFD events
|
||||
@param client_index - opaque cookie to identify the sender
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param enable_disable - 1 => register for events, 0 => cancel registration
|
||||
@param pid - sender's pid
|
||||
*/
|
||||
define want_bfd_events
|
||||
{
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
u32 enable_disable;
|
||||
u32 pid;
|
||||
};
|
||||
|
||||
/** \brief Reply for BFD events registration
|
||||
@param context - returned sender context, to match reply w/ request
|
||||
@param retval - return code
|
||||
*/
|
||||
define want_bfd_events_reply
|
||||
{
|
||||
u32 context;
|
||||
i32 retval;
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
|
@ -223,7 +223,7 @@ mhash_init (mhash_t * h, uword n_value_bytes, uword n_key_bytes)
|
||||
}
|
||||
|
||||
static uword
|
||||
mhash_set_tmp_key (mhash_t * h, void *key)
|
||||
mhash_set_tmp_key (mhash_t * h, const void *key)
|
||||
{
|
||||
u8 *key_tmp;
|
||||
int my_cpu = os_get_cpu_number ();
|
||||
@ -251,7 +251,7 @@ mhash_set_tmp_key (mhash_t * h, void *key)
|
||||
}
|
||||
|
||||
hash_pair_t *
|
||||
mhash_get_pair (mhash_t * h, void *key)
|
||||
mhash_get_pair (mhash_t * h, const void *key)
|
||||
{
|
||||
uword ikey;
|
||||
mhash_sanitize_hash_user (h);
|
||||
|
@ -101,13 +101,13 @@ mhash_key_to_mem (mhash_t * h, uword key)
|
||||
return vec_elt_at_index (h->key_vector_or_heap, key);
|
||||
}
|
||||
|
||||
hash_pair_t *mhash_get_pair (mhash_t * h, void *key);
|
||||
hash_pair_t *mhash_get_pair (mhash_t * h, const void *key);
|
||||
uword mhash_set_mem (mhash_t * h, void *key, uword * new_value,
|
||||
uword * old_value);
|
||||
uword mhash_unset (mhash_t * h, void *key, uword * old_value);
|
||||
|
||||
always_inline uword *
|
||||
mhash_get (mhash_t * h, void *key)
|
||||
mhash_get (mhash_t * h, const void *key)
|
||||
{
|
||||
hash_pair_t *p = mhash_get_pair (h, key);
|
||||
return p ? &p->value[0] : 0;
|
||||
|
Reference in New Issue
Block a user