Initial commit for phase 2, Add some simple validation.
[vpp.git] / test / test_span.py
1 #!/usr/bin/env python
2
3 import unittest
4
5 from scapy.packet import Raw
6 from scapy.layers.l2 import Ether, Dot1Q, GRE
7 from scapy.layers.inet import IP, UDP
8 from scapy.layers.vxlan import VXLAN
9
10 from framework import VppTestCase, VppTestRunner
11 from util import Host, ppp
12 from vpp_sub_interface import VppDot1QSubint, VppDot1ADSubint
13 from vpp_gre_interface import VppGreInterface, VppGre6Interface
14 from vpp_papi_provider import L2_VTR_OP
15 from collections import namedtuple
16
17 Tag = namedtuple('Tag', ['dot1', 'vlan'])
18 DOT1AD = 0x88A8
19 DOT1Q = 0x8100
20
21
22 class TestSpan(VppTestCase):
23     """ SPAN Test Case """
24
25     @classmethod
26     def setUpClass(cls):
27         super(TestSpan, cls).setUpClass()
28         # Test variables
29         cls.pkts_per_burst = 257    # Number of packets per burst
30         # create 3 pg interfaces
31         cls.create_pg_interfaces(range(3))
32
33         cls.bd_id = 55
34         cls.sub_if = VppDot1QSubint(cls, cls.pg0, 100)
35         cls.dst_sub_if = VppDot1QSubint(cls, cls.pg2, 300)
36         cls.dst_sub_if.set_vtr(L2_VTR_OP.L2_POP_1, tag=300)
37         # packet flows mapping pg0 -> pg1, pg2 -> pg3, etc.
38         cls.flows = dict()
39         cls.flows[cls.pg0] = [cls.pg1]
40         cls.flows[cls.pg1] = [cls.pg0]
41
42         # packet sizes
43         cls.pg_if_packet_sizes = [64, 512]  # , 1518, 9018]
44
45         cls.interfaces = list(cls.pg_interfaces)
46
47         # setup all interfaces
48         for i in cls.interfaces:
49             i.admin_up()
50             i.config_ip4()
51             i.resolve_arp()
52
53         cls.vxlan = cls.vapi.vxlan_add_del_tunnel(
54             src_addr=cls.pg2.local_ip4n,
55             dst_addr=cls.pg2.remote_ip4n,
56             vni=1111,
57             is_add=1)
58
59     def setUp(self):
60         super(TestSpan, self).setUp()
61         self.reset_packet_infos()
62
63     def tearDown(self):
64         super(TestSpan, self).tearDown()
65         if not self.vpp_dead:
66             self.logger.info(self.vapi.ppcli("show interface span"))
67
68     def xconnect(self, a, b, is_add=1):
69         self.vapi.sw_interface_set_l2_xconnect(a, b, enable=is_add)
70         self.vapi.sw_interface_set_l2_xconnect(b, a, enable=is_add)
71
72     def bridge(self, sw_if_index, is_add=1):
73         self.vapi.sw_interface_set_l2_bridge(
74             sw_if_index, bd_id=self.bd_id, enable=is_add)
75
76     def _remove_tag(self, packet, vlan, tag_type):
77         self.assertEqual(packet.type, tag_type)
78         payload = packet.payload
79         self.assertEqual(payload.vlan, vlan)
80         inner_type = payload.type
81         payload = payload.payload
82         packet.remove_payload()
83         packet.add_payload(payload)
84         packet.type = inner_type
85
86     def remove_tags(self, packet, tags):
87         for t in tags:
88             self._remove_tag(packet, t.vlan, t.dot1)
89         return packet
90
91     def decap_gre(self, pkt):
92         """
93         Decapsulate the original payload frame by removing GRE header
94         """
95         self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
96         self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
97
98         self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
99         self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
100
101         return pkt[GRE].payload
102
103     def decap_vxlan(self, pkt):
104         """
105         Decapsulate the original payload frame by removing VXLAN header
106         """
107         self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
108         self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
109
110         self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
111         self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
112
113         return pkt[VXLAN].payload
114
115     def create_stream(self, src_if, packet_sizes, do_dot1=False):
116         pkts = []
117         dst_if = self.flows[src_if][0]
118         for i in range(0, self.pkts_per_burst):
119             pkt_info = self.create_packet_info(src_if, dst_if)
120             payload = self.info_to_payload(pkt_info)
121             size = packet_sizes[(i / 2) % len(packet_sizes)]
122             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
123                  IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) /
124                  UDP(sport=10000 + src_if.sw_if_index * 1000 + i, dport=1234) /
125                  Raw(payload))
126             if do_dot1:
127                 p = self.sub_if.add_dot1_layer(p)
128             pkt_info.data = p.copy()
129             self.extend_packet(p, size)
130             pkts.append(p)
131         return pkts
132
133     def verify_capture(self, cap1, cap2):
134         self.assertEqual(len(cap1), len(cap2),
135                          "Different number of sent and mirrored packets :"
136                          "%u != %u" % (len(cap1), len(cap2)))
137
138         pkts1 = [(pkt[Ether] / pkt[IP] / pkt[UDP]) for pkt in cap1]
139         pkts2 = [(pkt[Ether] / pkt[IP] / pkt[UDP]) for pkt in cap2]
140
141         self.assertEqual(pkts1.sort(), pkts2.sort())
142
143     def test_device_span(self):
144         """ SPAN device rx mirror test
145
146         Test scenario:
147             1. config
148                3 interfaces, pg0 l2xconnected with pg1
149             2. sending l2 eth packets between 2 interfaces (pg0, pg1) and
150                mirrored to pg2
151                64B, 512B, 1518B, 9018B (ether_size)
152                burst of packets per interface
153         """
154
155         # Create bi-directional cross-connects between pg0 and pg1
156         self.xconnect(self.pg0.sw_if_index, self.pg1.sw_if_index)
157         # Create incoming packet streams for packet-generator interfaces
158         pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes)
159         self.pg0.add_stream(pkts)
160
161         # Enable SPAN on pg0 (mirrored to pg2)
162         self.vapi.sw_interface_span_enable_disable(
163             self.pg0.sw_if_index, self.pg2.sw_if_index)
164
165         self.logger.info(self.vapi.ppcli("show interface span"))
166         # Enable packet capturing and start packet sending
167         self.pg_enable_capture(self.pg_interfaces)
168         self.pg_start()
169
170         # Verify packets outgoing packet streams on mirrored interface (pg2)
171         self.logger.info("Verifying capture on interfaces %s and %s" %
172                          (self.pg1.name, self.pg2.name))
173
174         n_pkts = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
175         pg1_pkts = self.pg1.get_capture(n_pkts)
176         pg2_pkts = self.pg2.get_capture(n_pkts)
177
178         # Disable SPAN on pg0 (mirrored to pg2)
179         self.vapi.sw_interface_span_enable_disable(
180             self.pg0.sw_if_index, self.pg2.sw_if_index, state=0)
181         self.xconnect(self.pg0.sw_if_index, self.pg1.sw_if_index, is_add=0)
182
183         self.verify_capture(pg1_pkts, pg2_pkts)
184
185     def test_span_l2_rx(self):
186         """ SPAN l2 rx mirror test """
187
188         self.sub_if.admin_up()
189
190         self.bridge(self.pg2.sw_if_index)
191         # Create bi-directional cross-connects between pg0 and pg1
192         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
193         # Create incoming packet streams for packet-generator interfaces
194         pkts = self.create_stream(
195             self.pg0, self.pg_if_packet_sizes, do_dot1=True)
196         self.pg0.add_stream(pkts)
197
198         # Enable SPAN on pg0 (mirrored to pg2)
199         self.vapi.sw_interface_span_enable_disable(
200             self.sub_if.sw_if_index, self.pg2.sw_if_index, is_l2=1)
201
202         self.logger.info(self.vapi.ppcli("show interface span"))
203         # Enable packet capturing and start packet sending
204         self.pg_enable_capture(self.pg_interfaces)
205         self.pg_start()
206
207         # Verify packets outgoing packet streams on mirrored interface (pg2)
208         self.logger.info("Verifying capture on interfaces %s and %s" %
209                          (self.pg1.name, self.pg2.name))
210         pg2_expected = len(pkts)
211         pg1_pkts = self.pg1.get_capture(pg2_expected)
212         pg2_pkts = self.pg2.get_capture(pg2_expected)
213         self.bridge(self.pg2.sw_if_index, is_add=0)
214
215         # Disable SPAN on pg0 (mirrored to pg2)
216         self.vapi.sw_interface_span_enable_disable(
217             self.sub_if.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1)
218         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
219
220         self.verify_capture(pg1_pkts, pg2_pkts)
221
222     def test_span_l2_rx_dst_vxlan(self):
223         """ SPAN l2 rx mirror into vxlan test """
224
225         self.sub_if.admin_up()
226         self.vapi.sw_interface_set_flags(self.vxlan.sw_if_index,
227                                          admin_up_down=1)
228
229         self.bridge(self.vxlan.sw_if_index, is_add=1)
230         # Create bi-directional cross-connects between pg0 and pg1
231         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
232         # Create incoming packet streams for packet-generator interfaces
233         pkts = self.create_stream(
234             self.pg0, self.pg_if_packet_sizes, do_dot1=True)
235         self.pg0.add_stream(pkts)
236
237         # Enable SPAN on pg0 sub if (mirrored to vxlan)
238         self.vapi.sw_interface_span_enable_disable(
239             self.sub_if.sw_if_index, self.vxlan.sw_if_index, is_l2=1)
240
241         self.logger.info(self.vapi.ppcli("show interface span"))
242         # Enable packet capturing and start packet sending
243         self.pg_enable_capture(self.pg_interfaces)
244         self.pg_start()
245
246         # Verify packets outgoing packet streams on mirrored interface (pg2)
247         self.logger.info("Verifying capture on interfaces %s and %s" %
248                          (self.pg1.name, self.pg2.name))
249         pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
250         pg1_pkts = self.pg1.get_capture()
251         pg2_pkts = [self.decap_vxlan(p)
252                     for p in self.pg2.get_capture(pg2_expected)]
253
254         self.bridge(self.vxlan.sw_if_index, is_add=0)
255         # Disable SPAN on pg0 sub if (mirrored to vxlan)
256         self.vapi.sw_interface_span_enable_disable(
257             self.sub_if.sw_if_index, self.vxlan.sw_if_index, state=0, is_l2=1)
258         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
259         self.verify_capture(pg1_pkts, pg2_pkts)
260
261     def test_span_l2_rx_dst_gre_subif_vtr(self):
262         """ SPAN l2 rx mirror into gre-subif+vtr """
263
264         self.sub_if.admin_up()
265
266         gre_if = VppGreInterface(self, self.pg2.local_ip4,
267                                  self.pg2.remote_ip4,
268                                  is_teb=1)
269
270         gre_if.add_vpp_config()
271         gre_if.admin_up()
272
273         gre_sub_if = VppDot1QSubint(self, gre_if, 500)
274         gre_sub_if.set_vtr(L2_VTR_OP.L2_POP_1, tag=500)
275         gre_sub_if.admin_up()
276
277         self.bridge(gre_sub_if.sw_if_index)
278         # Create bi-directional cross-connects between pg0 and pg1
279         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
280
281         # Create incoming packet streams for packet-generator interfaces
282         pkts = self.create_stream(
283             self.pg0, self.pg_if_packet_sizes, do_dot1=True)
284         self.pg0.add_stream(pkts)
285
286         self.vapi.sw_interface_span_enable_disable(
287             self.sub_if.sw_if_index, gre_sub_if.sw_if_index, is_l2=1)
288
289         # Enable packet capturing and start packet sending
290         self.pg_enable_capture(self.pg_interfaces)
291         self.pg_start()
292
293         # Verify packets outgoing packet streams on mirrored interface (pg2)
294         self.logger.info("Verifying capture on interfaces %s and %s" %
295                          (self.pg1.name, self.pg2.name))
296         pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
297         pg1_pkts = self.pg1.get_capture()
298         pg2_pkts = self.pg2.get_capture(pg2_expected)
299         pg2_decaped = [self.remove_tags(self.decap_gre(
300             p), [Tag(dot1=DOT1Q, vlan=500)]) for p in pg2_pkts]
301         self.bridge(gre_sub_if.sw_if_index, is_add=0)
302
303         # Disable SPAN on pg0 sub if
304         self.vapi.sw_interface_span_enable_disable(
305             self.sub_if.sw_if_index, gre_sub_if.sw_if_index, state=0, is_l2=1)
306         gre_if.remove_vpp_config()
307         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
308
309         self.verify_capture(pg1_pkts, pg2_decaped)
310
311     def test_span_l2_rx_dst_vtr(self):
312         """ SPAN l2 rx mirror into subif+vtr """
313
314         self.sub_if.admin_up()
315         self.dst_sub_if.admin_up()
316
317         self.bridge(self.dst_sub_if.sw_if_index)
318         # Create bi-directional cross-connects between pg0 and pg1
319         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
320
321         # Create incoming packet streams for packet-generator interfaces
322         pkts = self.create_stream(
323             self.pg0, self.pg_if_packet_sizes, do_dot1=True)
324         self.pg0.add_stream(pkts)
325
326         self.vapi.sw_interface_span_enable_disable(
327             self.sub_if.sw_if_index, self.dst_sub_if.sw_if_index, is_l2=1)
328
329         # Enable packet capturing and start packet sending
330         self.pg_enable_capture(self.pg_interfaces)
331         self.pg_start()
332
333         # Verify packets outgoing packet streams on mirrored interface (pg2)
334         self.logger.info("Verifying capture on interfaces %s and %s" %
335                          (self.pg1.name, self.pg2.name))
336         pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
337         pg1_pkts = self.pg1.get_capture()
338         pg2_pkts = self.pg2.get_capture(pg2_expected)
339         pg2_untagged = [self.remove_tags(p, [Tag(dot1=DOT1Q, vlan=300)])
340                         for p in pg2_pkts]
341
342         self.bridge(self.dst_sub_if.sw_if_index, is_add=0)
343         # Disable SPAN on pg0 sub if (mirrored to vxlan)
344         self.vapi.sw_interface_span_enable_disable(
345             self.sub_if.sw_if_index, self.dst_sub_if.sw_if_index, state=0,
346             is_l2=1)
347         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
348
349         self.verify_capture(pg1_pkts, pg2_untagged)
350
351     def test_l2_tx_span(self):
352         """ SPAN l2 tx mirror test """
353
354         self.sub_if.admin_up()
355         self.bridge(self.pg2.sw_if_index)
356         # Create bi-directional cross-connects between pg0 and pg1
357         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
358         # Create incoming packet streams for packet-generator interfaces
359         pkts = self.create_stream(
360             self.pg0, self.pg_if_packet_sizes, do_dot1=True)
361         self.pg0.add_stream(pkts)
362
363         # Enable SPAN on pg1 (mirrored to pg2)
364         self.vapi.sw_interface_span_enable_disable(
365             self.pg1.sw_if_index, self.pg2.sw_if_index, is_l2=1, state=2)
366
367         self.logger.info(self.vapi.ppcli("show interface span"))
368         # Enable packet capturing and start packet sending
369         self.pg_enable_capture(self.pg_interfaces)
370         self.pg_start()
371
372         # Verify packets outgoing packet streams on mirrored interface (pg2)
373         self.logger.info("Verifying capture on interfaces %s and %s" %
374                          (self.pg1.name, self.pg2.name))
375         pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
376         pg1_pkts = self.pg1.get_capture()
377         pg2_pkts = self.pg2.get_capture(pg2_expected)
378         self.bridge(self.pg2.sw_if_index, is_add=0)
379         # Disable SPAN on pg0 (mirrored to pg2)
380         self.vapi.sw_interface_span_enable_disable(
381             self.pg1.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1)
382         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
383
384         self.verify_capture(pg1_pkts, pg2_pkts)
385
386     def test_l2_rx_tx_span(self):
387         """ SPAN l2 rx tx mirror test """
388
389         self.sub_if.admin_up()
390         self.bridge(self.pg2.sw_if_index)
391         # Create bi-directional cross-connects between pg0 and pg1
392         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
393
394         # Create incoming packet streams for packet-generator interfaces
395         pg0_pkts = self.create_stream(
396             self.pg0, self.pg_if_packet_sizes, do_dot1=True)
397         self.pg0.add_stream(pg0_pkts)
398         pg1_pkts = self.create_stream(
399             self.pg1, self.pg_if_packet_sizes, do_dot1=False)
400         self.pg1.add_stream(pg1_pkts)
401
402         # Enable SPAN on pg0 (mirrored to pg2)
403         self.vapi.sw_interface_span_enable_disable(
404             self.sub_if.sw_if_index, self.pg2.sw_if_index, is_l2=1, state=3)
405         self.logger.info(self.vapi.ppcli("show interface span"))
406
407         # Enable packet capturing and start packet sending
408         self.pg_enable_capture(self.pg_interfaces)
409         self.pg_start()
410
411         # Verify packets outgoing packet streams on mirrored interface (pg2)
412         self.logger.info("Verifying capture on interfaces %s and %s" %
413                          (self.pg1.name, self.pg2.name))
414         pg0_expected = self.get_packet_count_for_if_idx(self.pg0.sw_if_index)
415         pg1_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
416         pg2_expected = pg0_expected + pg1_expected
417
418         pg0_pkts = self.pg0.get_capture(pg0_expected)
419         pg1_pkts = self.pg1.get_capture(pg1_expected)
420         pg2_pkts = self.pg2.get_capture(pg2_expected)
421
422         self.bridge(self.pg2.sw_if_index, is_add=0)
423         # Disable SPAN on pg0 (mirrored to pg2)
424         self.vapi.sw_interface_span_enable_disable(
425             self.sub_if.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1)
426         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
427
428         self.verify_capture(pg0_pkts + pg1_pkts, pg2_pkts)
429
430
431 if __name__ == '__main__':
432     unittest.main(testRunner=VppTestRunner)