PageRenderTime 90ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/test/test_l2bd.py

https://gitlab.com/x1046882802/flexiroutervpp
Python | 289 lines | 165 code | 33 blank | 91 comment | 27 complexity | aa2084c3a52cad40e82feb6a01b5d03a MD5 | raw file
  1. #!/usr/bin/env python3
  2. import unittest
  3. import random
  4. from scapy.packet import Raw
  5. from scapy.layers.l2 import Ether, Dot1Q
  6. from scapy.layers.inet import IP, UDP
  7. from framework import VppTestCase, VppTestRunner
  8. from util import Host, ppp
  9. from vpp_sub_interface import VppDot1QSubint, VppDot1ADSubint
  10. class TestL2bd(VppTestCase):
  11. """ L2BD Test Case """
  12. @classmethod
  13. def setUpClass(cls):
  14. """
  15. Perform standard class setup (defined by class method setUpClass in
  16. class VppTestCase) before running the test case, set test case related
  17. variables and configure VPP.
  18. :var int bd_id: Bridge domain ID.
  19. :var int mac_entries_count: Number of MAC entries for bridge-domain to
  20. learn.
  21. :var int dot1q_tag: VLAN tag for dot1q sub-interface.
  22. :var int dot1ad_sub_id: SubID of dot1ad sub-interface.
  23. :var int dot1ad_outer_tag: VLAN S-tag for dot1ad sub-interface.
  24. :var int dot1ad_inner_tag: VLAN C-tag for dot1ad sub-interface.
  25. :var int sl_pkts_per_burst: Number of packets in burst for single-loop
  26. test.
  27. :var int dl_pkts_per_burst: Number of packets in burst for dual-loop
  28. test.
  29. """
  30. super(TestL2bd, cls).setUpClass()
  31. # Test variables
  32. cls.bd_id = 1
  33. cls.mac_entries_count = 100
  34. # cls.dot1q_sub_id = 100
  35. cls.dot1q_tag = 100
  36. cls.dot1ad_sub_id = 20
  37. cls.dot1ad_outer_tag = 200
  38. cls.dot1ad_inner_tag = 300
  39. cls.sl_pkts_per_burst = 2
  40. cls.dl_pkts_per_burst = 257
  41. try:
  42. # create 3 pg interfaces
  43. cls.create_pg_interfaces(range(3))
  44. # create 2 sub-interfaces for pg1 and pg2
  45. cls.sub_interfaces = [
  46. VppDot1QSubint(cls, cls.pg1, cls.dot1q_tag),
  47. VppDot1ADSubint(cls, cls.pg2, cls.dot1ad_sub_id,
  48. cls.dot1ad_outer_tag, cls.dot1ad_inner_tag)]
  49. # packet flows mapping pg0 -> pg1, pg2, etc.
  50. cls.flows = dict()
  51. cls.flows[cls.pg0] = [cls.pg1, cls.pg2]
  52. cls.flows[cls.pg1] = [cls.pg0, cls.pg2]
  53. cls.flows[cls.pg2] = [cls.pg0, cls.pg1]
  54. # packet sizes
  55. cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
  56. cls.sub_if_packet_sizes = [64, 512, 1518 + 4, 9018 + 4]
  57. cls.interfaces = list(cls.pg_interfaces)
  58. cls.interfaces.extend(cls.sub_interfaces)
  59. # Create BD with MAC learning enabled and put interfaces and
  60. # sub-interfaces to this BD
  61. for pg_if in cls.pg_interfaces:
  62. sw_if_index = pg_if.sub_if.sw_if_index \
  63. if hasattr(pg_if, 'sub_if') else pg_if.sw_if_index
  64. cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=sw_if_index,
  65. bd_id=cls.bd_id)
  66. # setup all interfaces
  67. for i in cls.interfaces:
  68. i.admin_up()
  69. # mapping between packet-generator index and lists of test hosts
  70. cls.hosts_by_pg_idx = dict()
  71. # create test host entries and inject packets to learn MAC entries
  72. # in the bridge-domain
  73. cls.create_hosts_and_learn(cls.mac_entries_count)
  74. cls.logger.info(cls.vapi.ppcli("show l2fib"))
  75. except Exception:
  76. super(TestL2bd, cls).tearDownClass()
  77. raise
  78. @classmethod
  79. def tearDownClass(cls):
  80. super(TestL2bd, cls).tearDownClass()
  81. def setUp(self):
  82. """
  83. Clear trace and packet infos before running each test.
  84. """
  85. super(TestL2bd, self).setUp()
  86. self.reset_packet_infos()
  87. def tearDown(self):
  88. """
  89. Show various debug prints after each test.
  90. """
  91. super(TestL2bd, self).tearDown()
  92. if not self.vpp_dead:
  93. self.logger.info(self.vapi.ppcli("show l2fib verbose"))
  94. self.logger.info(self.vapi.ppcli("show bridge-domain %s detail" %
  95. self.bd_id))
  96. @classmethod
  97. def create_hosts_and_learn(cls, count):
  98. """
  99. Create required number of host MAC addresses and distribute them among
  100. interfaces. Create host IPv4 address for every host MAC address. Create
  101. L2 MAC packet stream with host MAC addresses per interface to let
  102. the bridge domain learn these MAC addresses.
  103. :param count: Integer number of hosts to create MAC/IPv4 addresses for.
  104. """
  105. n_int = len(cls.pg_interfaces)
  106. macs_per_if = count // n_int
  107. i = -1
  108. for pg_if in cls.pg_interfaces:
  109. i += 1
  110. start_nr = macs_per_if * i
  111. end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
  112. cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
  113. hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
  114. packets = []
  115. for j in range(start_nr, end_nr):
  116. host = Host(
  117. "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
  118. "172.17.1%02x.%u" % (pg_if.sw_if_index, j))
  119. packet = (Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac))
  120. hosts.append(host)
  121. if hasattr(pg_if, 'sub_if'):
  122. packet = pg_if.sub_if.add_dot1_layer(packet)
  123. packets.append(packet)
  124. pg_if.add_stream(packets)
  125. cls.logger.info("Sending broadcast eth frames for MAC learning")
  126. cls.pg_start()
  127. def create_stream(self, src_if, packet_sizes, packets_per_burst):
  128. """
  129. Create input packet stream for defined interface.
  130. :param object src_if: Interface to create packet stream for.
  131. :param list packet_sizes: List of required packet sizes.
  132. :param int packets_per_burst: Number of packets in burst.
  133. :return: Stream of packets.
  134. """
  135. pkts = []
  136. for i in range(0, packets_per_burst):
  137. dst_if = self.flows[src_if][i % 2]
  138. dst_host = random.choice(self.hosts_by_pg_idx[dst_if.sw_if_index])
  139. src_host = random.choice(self.hosts_by_pg_idx[src_if.sw_if_index])
  140. pkt_info = self.create_packet_info(src_if, dst_if)
  141. payload = self.info_to_payload(pkt_info)
  142. p = (Ether(dst=dst_host.mac, src=src_host.mac) /
  143. IP(src=src_host.ip4, dst=dst_host.ip4) /
  144. UDP(sport=1234, dport=1234) /
  145. Raw(payload))
  146. pkt_info.data = p.copy()
  147. if hasattr(src_if, 'sub_if'):
  148. p = src_if.sub_if.add_dot1_layer(p)
  149. size = random.choice(packet_sizes)
  150. self.extend_packet(p, size)
  151. pkts.append(p)
  152. return pkts
  153. def verify_capture(self, pg_if, capture):
  154. """
  155. Verify captured input packet stream for defined interface.
  156. :param object pg_if: Interface to verify captured packet stream for.
  157. :param list capture: Captured packet stream.
  158. """
  159. last_info = dict()
  160. for i in self.pg_interfaces:
  161. last_info[i.sw_if_index] = None
  162. dst_sw_if_index = pg_if.sw_if_index
  163. for packet in capture:
  164. payload_info = self.payload_to_info(packet[Raw])
  165. src_sw_if_index = payload_info.src
  166. src_if = None
  167. for ifc in self.pg_interfaces:
  168. if ifc != pg_if:
  169. if ifc.sw_if_index == src_sw_if_index:
  170. src_if = ifc
  171. break
  172. if hasattr(src_if, 'sub_if'):
  173. # Check VLAN tags and Ethernet header
  174. packet = src_if.sub_if.remove_dot1_layer(packet)
  175. self.assertTrue(Dot1Q not in packet)
  176. try:
  177. ip = packet[IP]
  178. udp = packet[UDP]
  179. packet_index = payload_info.index
  180. self.assertEqual(payload_info.dst, dst_sw_if_index)
  181. self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
  182. (pg_if.name, payload_info.src, packet_index))
  183. next_info = self.get_next_packet_info_for_interface2(
  184. payload_info.src, dst_sw_if_index,
  185. last_info[payload_info.src])
  186. last_info[payload_info.src] = next_info
  187. self.assertTrue(next_info is not None)
  188. self.assertEqual(packet_index, next_info.index)
  189. saved_packet = next_info.data
  190. # Check standard fields
  191. self.assertEqual(ip.src, saved_packet[IP].src)
  192. self.assertEqual(ip.dst, saved_packet[IP].dst)
  193. self.assertEqual(udp.sport, saved_packet[UDP].sport)
  194. self.assertEqual(udp.dport, saved_packet[UDP].dport)
  195. except:
  196. self.logger.error(ppp("Unexpected or invalid packet:", packet))
  197. raise
  198. for i in self.pg_interfaces:
  199. remaining_packet = self.get_next_packet_info_for_interface2(
  200. i, dst_sw_if_index, last_info[i.sw_if_index])
  201. self.assertTrue(
  202. remaining_packet is None,
  203. "Port %u: Packet expected from source %u didn't arrive" %
  204. (dst_sw_if_index, i.sw_if_index))
  205. def run_l2bd_test(self, pkts_per_burst):
  206. """ L2BD MAC learning test """
  207. # Create incoming packet streams for packet-generator interfaces
  208. for i in self.pg_interfaces:
  209. packet_sizes = self.sub_if_packet_sizes if hasattr(i, 'sub_if') \
  210. else self.pg_if_packet_sizes
  211. pkts = self.create_stream(i, packet_sizes, pkts_per_burst)
  212. i.add_stream(pkts)
  213. # Enable packet capture and start packet sending
  214. self.pg_enable_capture(self.pg_interfaces)
  215. self.pg_start()
  216. # Verify outgoing packet streams per packet-generator interface
  217. for i in self.pg_interfaces:
  218. capture = i.get_capture()
  219. self.logger.info("Verifying capture on interface %s" % i.name)
  220. self.verify_capture(i, capture)
  221. def test_l2bd_sl(self):
  222. """ L2BD MAC learning single-loop test
  223. Test scenario:
  224. 1.config
  225. MAC learning enabled
  226. learn 100 MAC entries
  227. 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of
  228. dot1ad in the first version)
  229. 2.sending l2 eth pkts between 3 interface
  230. 64B, 512B, 1518B, 9200B (ether_size)
  231. burst of 2 pkts per interface
  232. """
  233. self.run_l2bd_test(self.sl_pkts_per_burst)
  234. def test_l2bd_dl(self):
  235. """ L2BD MAC learning dual-loop test
  236. Test scenario:
  237. 1.config
  238. MAC learning enabled
  239. learn 100 MAC entries
  240. 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of
  241. dot1ad in the first version)
  242. 2.sending l2 eth pkts between 3 interface
  243. 64B, 512B, 1518B, 9200B (ether_size)
  244. burst of 257 pkts per interface
  245. """
  246. self.run_l2bd_test(self.dl_pkts_per_burst)
  247. if __name__ == '__main__':
  248. unittest.main(testRunner=VppTestRunner)