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:
Klement Sekera
2016-09-29 14:43:44 +02:00
committed by Dave Barach
parent d171d48edc
commit 0e3c0de1ed
30 changed files with 3504 additions and 201 deletions

View File

@ -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
View 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)

View File

@ -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):
"""

View File

@ -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()

View File

@ -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
View 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)

View File

@ -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
View 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

View File

@ -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) /

View File

@ -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

View File

@ -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
########################################

View File

@ -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
View 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
View 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
View File

@ -0,0 +1 @@
TODO

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
View 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:
*/

View 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);

View 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

File diff suppressed because it is too large Load Diff

56
vnet/vnet/bfd/bfd_udp.h Normal file
View 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
View 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
*/

View File

@ -143,7 +143,11 @@ typedef struct
u8 code;
u32 data;
} icmp;
/* IP header - saved by ip*_local nodes */
void *header;
};
} ip;
/*

View File

@ -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* */

View File

@ -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) \

View File

@ -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");

View File

@ -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")

View File

@ -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);

View File

@ -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;