blob: 1d64937b6ddd5ba4f1748e0b12c6fb1ce7e8f80d [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Mohsin Kazmi61b94c62018-08-20 18:32:39 +02002
3import socket
Ole Troan7f991832018-12-06 17:35:12 +01004from util import ip4_range, reassemble4_ether
Mohsin Kazmi61b94c62018-08-20 18:32:39 +02005import unittest
6from framework import VppTestCase, VppTestRunner
7from template_bd import BridgeDomain
8
snaramre5d4b8912019-12-13 23:39:35 +00009from scapy.layers.l2 import Ether
10from scapy.packet import Raw
Mohsin Kazmi61b94c62018-08-20 18:32:39 +020011from scapy.layers.inet import IP, UDP
12from scapy.layers.vxlan import VXLAN
Paul Vinciguerra2f156312020-05-02 22:34:40 -040013
Neale Ranns097fa662018-05-01 05:17:55 -070014from vpp_ip_route import VppIpRoute, VppRoutePath
15from vpp_ip import INVALID_INDEX
Mohsin Kazmi61b94c62018-08-20 18:32:39 +020016
Mohsin Kazmi61b94c62018-08-20 18:32:39 +020017
18class TestVxlanGbp(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020019 """VXLAN GBP Test Case"""
Mohsin Kazmi61b94c62018-08-20 18:32:39 +020020
21 @property
22 def frame_request(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020023 """Ethernet frame modeling a generic request"""
24 return (
25 Ether(src="00:00:00:00:00:01", dst="00:00:00:00:00:02")
26 / IP(src="1.2.3.4", dst="4.3.2.1")
27 / UDP(sport=10000, dport=20000)
28 / Raw(b"\xa5" * 100)
29 )
Mohsin Kazmi61b94c62018-08-20 18:32:39 +020030
31 @property
32 def frame_reply(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020033 """Ethernet frame modeling a generic reply"""
34 return (
35 Ether(src="00:00:00:00:00:02", dst="00:00:00:00:00:01")
36 / IP(src="4.3.2.1", dst="1.2.3.4")
37 / UDP(sport=20000, dport=10000)
38 / Raw(b"\xa5" * 100)
39 )
Mohsin Kazmi61b94c62018-08-20 18:32:39 +020040
41 def encapsulate(self, pkt, vni):
42 """
43 Encapsulate the original payload frame by adding VXLAN GBP header with
44 its UDP, IP and Ethernet fields
45 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020046 return (
47 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
48 / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
49 / UDP(sport=self.dport, dport=self.dport, chksum=0)
50 / VXLAN(vni=vni, flags=self.flags, gpflags=self.gpflags, gpid=self.sclass)
51 / pkt
52 )
Mohsin Kazmi61b94c62018-08-20 18:32:39 +020053
54 def ip_range(self, start, end):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020055 """range of remote ip's"""
Mohsin Kazmi61b94c62018-08-20 18:32:39 +020056 return ip4_range(self.pg0.remote_ip4, start, end)
57
58 def decapsulate(self, pkt):
59 """
60 Decapsulate the original payload frame by removing VXLAN header
61 """
62 # check if is set G and I flag
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020063 self.assertEqual(pkt[VXLAN].flags, int("0x88", 16))
Mohsin Kazmi61b94c62018-08-20 18:32:39 +020064 return pkt[VXLAN].payload
65
66 # Method for checking VXLAN GBP encapsulation.
67 #
68 def check_encapsulation(self, pkt, vni, local_only=False, mcast_pkt=False):
69 # TODO: add error messages
70 # Verify source MAC is VPP_MAC and destination MAC is MY_MAC resolved
71 # by VPP using ARP.
72 self.assertEqual(pkt[Ether].src, self.pg0.local_mac)
73 if not local_only:
74 if not mcast_pkt:
75 self.assertEqual(pkt[Ether].dst, self.pg0.remote_mac)
76 else:
77 self.assertEqual(pkt[Ether].dst, type(self).mcast_mac)
78 # Verify VXLAN GBP tunnel source IP is VPP_IP and destination IP is
79 # MY_IP.
80 self.assertEqual(pkt[IP].src, self.pg0.local_ip4)
81 if not local_only:
82 if not mcast_pkt:
83 self.assertEqual(pkt[IP].dst, self.pg0.remote_ip4)
84 else:
85 self.assertEqual(pkt[IP].dst, type(self).mcast_ip4)
86 # Verify UDP destination port is VXLAN GBP 48879, source UDP port could
87 # be arbitrary.
88 self.assertEqual(pkt[UDP].dport, type(self).dport)
Vladimir Isaev698eb872020-05-21 16:34:17 +030089 # Verify UDP checksum
90 self.assert_udp_checksum_valid(pkt)
Mohsin Kazmi61b94c62018-08-20 18:32:39 +020091 # Verify VNI
92 # pkt.show()
93 self.assertEqual(pkt[VXLAN].vni, vni)
94 # Verify Source Class
95 self.assertEqual(pkt[VXLAN].gpid, 0)
96
97 @classmethod
98 def create_vxlan_gbp_flood_test_bd(cls, vni, n_ucast_tunnels):
99 # Create 2 ucast vxlan tunnels under bd
100 ip_range_start = 10
101 ip_range_end = ip_range_start + n_ucast_tunnels
Neale Ranns097fa662018-05-01 05:17:55 -0700102 next_hop_address = cls.pg0.remote_ip4
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200103 for dest_ip4 in ip4_range(cls.pg0.remote_ip4, ip_range_start, ip_range_end):
Paul Vinciguerra2f156312020-05-02 22:34:40 -0400104 # add host route so dest_ip4 will not be resolved
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200105 rip = VppIpRoute(
106 cls,
107 dest_ip4,
108 32,
109 [VppRoutePath(next_hop_address, INVALID_INDEX)],
110 register=False,
111 )
Neale Ranns097fa662018-05-01 05:17:55 -0700112 rip.add_vpp_config()
Neale Ranns79a05f52018-09-11 07:39:43 -0700113 r = cls.vapi.vxlan_gbp_tunnel_add_del(
Paul Vinciguerra1b534f52019-06-15 20:31:31 -0400114 tunnel={
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200115 "src": cls.pg0.local_ip4,
116 "dst": dest_ip4,
117 "vni": vni,
118 "instance": INVALID_INDEX,
119 "mcast_sw_if_index": INVALID_INDEX,
120 "mode": 1,
Paul Vinciguerra1b534f52019-06-15 20:31:31 -0400121 },
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200122 is_add=1,
Paul Vinciguerra1b534f52019-06-15 20:31:31 -0400123 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200124 cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index, bd_id=vni)
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200125
126 # Class method to start the VXLAN GBP test case.
127 # Overrides setUpClass method in VppTestCase class.
128 # Python try..except statement is used to ensure that the tear down of
129 # the class will be executed even if exception is raised.
130 # @param cls The class pointer.
131 @classmethod
132 def setUpClass(cls):
133 super(TestVxlanGbp, cls).setUpClass()
134
135 try:
136 cls.dport = 48879
137 cls.flags = 0x88
138 cls.gpflags = 0x0
139 cls.sclass = 0
140
141 # Create 2 pg interfaces.
142 cls.create_pg_interfaces(range(4))
143 for pg in cls.pg_interfaces:
144 pg.admin_up()
145
146 # Configure IPv4 addresses on VPP pg0.
147 cls.pg0.config_ip4()
148
149 # Resolve MAC address for VPP's IP address on pg0.
150 cls.pg0.resolve_arp()
151
152 # Create VXLAN GBP VTEP on VPP pg0, and put vxlan_gbp_tunnel0 and
153 # pg1 into BD.
154 cls.single_tunnel_bd = 1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200155 cls.single_tunnel_vni = 0xABCDE
Neale Ranns79a05f52018-09-11 07:39:43 -0700156 r = cls.vapi.vxlan_gbp_tunnel_add_del(
Paul Vinciguerra1b534f52019-06-15 20:31:31 -0400157 tunnel={
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200158 "src": cls.pg0.local_ip4,
159 "dst": cls.pg0.remote_ip4,
160 "vni": cls.single_tunnel_vni,
161 "instance": INVALID_INDEX,
162 "mcast_sw_if_index": INVALID_INDEX,
163 "mode": 1,
Paul Vinciguerra1b534f52019-06-15 20:31:31 -0400164 },
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200165 is_add=1,
Paul Vinciguerra1b534f52019-06-15 20:31:31 -0400166 )
Ole Troana5b2eec2019-03-11 19:23:25 +0100167 cls.vapi.sw_interface_set_l2_bridge(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200168 rx_sw_if_index=r.sw_if_index, bd_id=cls.single_tunnel_bd
169 )
170 cls.vapi.sw_interface_set_l2_bridge(
171 rx_sw_if_index=cls.pg1.sw_if_index, bd_id=cls.single_tunnel_bd
172 )
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200173
174 # Setup vni 2 to test multicast flooding
175 cls.n_ucast_tunnels = 2
176 # Setup vni 3 to test unicast flooding
177 cls.ucast_flood_bd = 3
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200178 cls.create_vxlan_gbp_flood_test_bd(cls.ucast_flood_bd, cls.n_ucast_tunnels)
Ole Troana5b2eec2019-03-11 19:23:25 +0100179 cls.vapi.sw_interface_set_l2_bridge(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200180 rx_sw_if_index=cls.pg3.sw_if_index, bd_id=cls.ucast_flood_bd
181 )
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200182 except Exception:
183 super(TestVxlanGbp, cls).tearDownClass()
184 raise
185
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700186 @classmethod
187 def tearDownClass(cls):
188 super(TestVxlanGbp, cls).tearDownClass()
189
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200190 def assert_eq_pkts(self, pkt1, pkt2):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200191 """Verify the Ether, IP, UDP, payload are equal in both
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200192 packets
193 """
194 self.assertEqual(pkt1[Ether].src, pkt2[Ether].src)
195 self.assertEqual(pkt1[Ether].dst, pkt2[Ether].dst)
196 self.assertEqual(pkt1[IP].src, pkt2[IP].src)
197 self.assertEqual(pkt1[IP].dst, pkt2[IP].dst)
198 self.assertEqual(pkt1[UDP].sport, pkt2[UDP].sport)
199 self.assertEqual(pkt1[UDP].dport, pkt2[UDP].dport)
200 self.assertEqual(pkt1[Raw], pkt2[Raw])
201
202 def test_decap(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200203 """Decapsulation test
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200204 Send encapsulated frames from pg0
205 Verify receipt of decapsulated frames on pg1
206 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200207 encapsulated_pkt = self.encapsulate(self.frame_request, self.single_tunnel_vni)
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200208
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200209 self.pg0.add_stream(
210 [
211 encapsulated_pkt,
212 ]
213 )
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200214
215 self.pg1.enable_capture()
216
217 self.pg_start()
218
219 # Pick first received frame and check if it's the non-encapsulated
220 # frame
221 out = self.pg1.get_capture(1)
222 pkt = out[0]
223 self.assert_eq_pkts(pkt, self.frame_request)
224
225 def test_encap(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200226 """Encapsulation test
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200227 Send frames from pg1
228 Verify receipt of encapsulated frames on pg0
229 """
230 self.pg1.add_stream([self.frame_reply])
231
232 self.pg0.enable_capture()
233
234 self.pg_start()
235
Paul Vinciguerra8feeaff2019-03-27 11:25:48 -0700236 # Pick first received frame and check if it's correctly encapsulated.
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200237 out = self.pg0.get_capture(1)
238 pkt = out[0]
Neale Ranns91fd9102020-04-03 07:46:28 +0000239 self.check_encapsulation(pkt, self.single_tunnel_vni)
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200240
241 payload = self.decapsulate(pkt)
242 self.assert_eq_pkts(payload, self.frame_reply)
243
244 def test_ucast_flood(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200245 """Unicast flood test
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200246 Send frames from pg3
247 Verify receipt of encapsulated frames on pg0
248 """
249 self.pg3.add_stream([self.frame_reply])
250
251 self.pg0.enable_capture()
252
253 self.pg_start()
254
Paul Vinciguerra8feeaff2019-03-27 11:25:48 -0700255 # Get packet from each tunnel and assert it's correctly encapsulated.
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200256 out = self.pg0.get_capture(self.n_ucast_tunnels)
257 for pkt in out:
258 self.check_encapsulation(pkt, self.ucast_flood_bd, True)
259 payload = self.decapsulate(pkt)
260 self.assert_eq_pkts(payload, self.frame_reply)
261
262 def test_encap_big_packet(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200263 """Encapsulation test send big frame from pg1
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200264 Verify receipt of encapsulated frames on pg0
265 """
266
267 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [1500, 0, 0, 0])
268
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200269 frame = (
270 Ether(src="00:00:00:00:00:02", dst="00:00:00:00:00:01")
271 / IP(src="4.3.2.1", dst="1.2.3.4")
272 / UDP(sport=20000, dport=10000)
273 / Raw(b"\xa5" * 1450)
274 )
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200275
276 self.pg1.add_stream([frame])
277
278 self.pg0.enable_capture()
279
280 self.pg_start()
281
282 # Pick first received frame and check if it's correctly encapsulated.
283 out = self.pg0.get_capture(2)
Ole Troan7f991832018-12-06 17:35:12 +0100284 pkt = reassemble4_ether(out)
Neale Ranns91fd9102020-04-03 07:46:28 +0000285 self.check_encapsulation(pkt, self.single_tunnel_vni)
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200286
287 payload = self.decapsulate(pkt)
288 self.assert_eq_pkts(payload, frame)
289
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200290 # Method to define VPP actions before tear down of the test case.
291 # Overrides tearDown method in VppTestCase class.
292 # @param self The object pointer.
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200293 def tearDown(self):
294 super(TestVxlanGbp, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700295
296 def show_commands_at_teardown(self):
297 self.logger.info(self.vapi.cli("show bridge-domain 1 detail"))
298 self.logger.info(self.vapi.cli("show bridge-domain 3 detail"))
299 self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
300 self.logger.info(self.vapi.cli("show error"))
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200301
302
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200303if __name__ == "__main__":
Mohsin Kazmi61b94c62018-08-20 18:32:39 +0200304 unittest.main(testRunner=VppTestRunner)