SPAN:add l2 mirror
[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.hosts_nr = 10           # Number of hosts
30         cls.pkts_per_burst = 257    # Number of packets per burst
31         # create 3 pg interfaces
32         cls.create_pg_interfaces(range(3))
33
34         cls.bd_id = 55
35         cls.sub_if = VppDot1QSubint(cls, cls.pg0, 100)
36         cls.dst_sub_if = VppDot1QSubint(cls, cls.pg2, 300)
37         cls.dst_sub_if.set_vtr(L2_VTR_OP.L2_POP_1, tag=300)
38         # packet flows mapping pg0 -> pg1, pg2 -> pg3, etc.
39         cls.flows = dict()
40         cls.flows[cls.pg0] = [cls.pg1]
41
42         # packet sizes
43         cls.pg_if_packet_sizes = [64, 512]  # , 1518, 9018]
44
45         cls.interfaces = list(cls.pg_interfaces)
46
47         # Create host MAC and IPv4 lists
48         # cls.MY_MACS = dict()
49         # cls.MY_IP4S = dict()
50         cls.create_host_lists(cls.hosts_nr)
51
52         # setup all interfaces
53         for i in cls.interfaces:
54             i.admin_up()
55             i.config_ip4()
56             i.resolve_arp()
57
58         cls.vxlan = cls.vapi.vxlan_add_del_tunnel(
59             src_addr=cls.pg2.local_ip4n,
60             dst_addr=cls.pg2.remote_ip4n,
61             vni=1111,
62             is_add=1)
63
64     def setUp(self):
65         super(TestSpan, self).setUp()
66         self.reset_packet_infos()
67
68     def tearDown(self):
69         super(TestSpan, self).tearDown()
70         if not self.vpp_dead:
71             self.logger.info(self.vapi.ppcli("show interface span"))
72
73     def xconnect(self, a, b, is_add=1):
74         self.vapi.sw_interface_set_l2_xconnect(a, b, enable=is_add)
75         self.vapi.sw_interface_set_l2_xconnect(b, a, enable=is_add)
76
77     def bridge(self, sw_if_index, is_add=1):
78         self.vapi.sw_interface_set_l2_bridge(
79             sw_if_index, bd_id=self.bd_id, enable=is_add)
80
81     def _remove_tag(self, packet, vlan, tag_type):
82         self.assertEqual(packet.type, tag_type)
83         payload = packet.payload
84         self.assertEqual(payload.vlan, vlan)
85         inner_type = payload.type
86         payload = payload.payload
87         packet.remove_payload()
88         packet.add_payload(payload)
89         packet.type = inner_type
90
91     def remove_tags(self, packet, tags):
92         for t in tags:
93             self._remove_tag(packet, t.vlan, t.dot1)
94         return packet
95
96     def decap_gre(self, pkt):
97         """
98         Decapsulate the original payload frame by removing GRE header
99         """
100         self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
101         self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
102
103         self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
104         self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
105
106         return pkt[GRE].payload
107
108     def decap_vxlan(self, pkt):
109         """
110         Decapsulate the original payload frame by removing VXLAN header
111         """
112         self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
113         self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
114
115         self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
116         self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
117
118         return pkt[VXLAN].payload
119
120     @classmethod
121     def create_host_lists(self, count):
122         """ Method to create required number of MAC and IPv4 addresses.
123         Create required number of host MAC addresses and distribute them among
124         interfaces. Create host IPv4 address for every host MAC address too.
125
126         :param count: Number of hosts to create MAC and IPv4 addresses for.
127         """
128         # mapping between packet-generator index and lists of test hosts
129         self.hosts_by_pg_idx = dict()
130
131         for pg_if in self.pg_interfaces:
132             # self.MY_MACS[i.sw_if_index] = []
133             # self.MY_IP4S[i.sw_if_index] = []
134             self.hosts_by_pg_idx[pg_if.sw_if_index] = []
135             hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
136             for j in range(0, count):
137                 host = Host(
138                     "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
139                     "172.17.1%02x.%u" % (pg_if.sw_if_index, j))
140                 hosts.append(host)
141
142     def create_stream(self, src_if, packet_sizes, do_dot1=False):
143         pkts = []
144         for i in range(0, self.pkts_per_burst):
145             dst_if = self.flows[src_if][0]
146             pkt_info = self.create_packet_info(src_if, dst_if)
147             payload = self.info_to_payload(pkt_info)
148             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
149                  IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) /
150                  UDP(sport=1234, dport=1234) /
151                  Raw(payload))
152             if do_dot1:
153                 p = self.sub_if.add_dot1_layer(p)
154             pkt_info.data = p.copy()
155             size = packet_sizes[(i / 2) % len(packet_sizes)]
156             self.extend_packet(p, size)
157             pkts.append(p)
158         return pkts
159
160     def verify_capture(self, dst_if, capture_pg1, capture_pg2):
161         last_info = dict()
162         for i in self.interfaces:
163             last_info[i.sw_if_index] = None
164         dst_sw_if_index = dst_if.sw_if_index
165         self.assertEqual(
166             len(capture_pg1),
167             len(capture_pg2),
168             "Different number of outgoing and mirrored packets : %u != %u" %
169             (len(capture_pg1),
170              len(capture_pg2)))
171         for pkt_pg1, pkt_pg2 in zip(capture_pg1, capture_pg2):
172             try:
173                 ip1 = pkt_pg1[IP]
174                 udp1 = pkt_pg1[UDP]
175                 raw1 = pkt_pg1[Raw]
176
177                 if pkt_pg1[Ether] != pkt_pg2[Ether]:
178                     self.logger.error("Different ethernet header of "
179                                       "outgoing and mirrored packet")
180                     raise
181                 if ip1 != pkt_pg2[IP]:
182                     self.logger.error(
183                         "Different ip header of outgoing and mirrored packet")
184                     raise
185                 if udp1 != pkt_pg2[UDP]:
186                     self.logger.error(
187                         "Different udp header of outgoing and mirrored packet")
188                     raise
189                 if raw1 != pkt_pg2[Raw]:
190                     self.logger.error(
191                         "Different raw data of outgoing and mirrored packet")
192                     raise
193
194                 payload_info = self.payload_to_info(str(raw1))
195                 packet_index = payload_info.index
196                 self.assertEqual(payload_info.dst, dst_sw_if_index)
197                 self.logger.debug(
198                     "Got packet on port %s: src=%u (id=%u)" %
199                     (dst_if.name, payload_info.src, packet_index))
200                 next_info = self.get_next_packet_info_for_interface2(
201                     payload_info.src, dst_sw_if_index,
202                     last_info[payload_info.src])
203                 last_info[payload_info.src] = next_info
204                 self.assertTrue(next_info is not None)
205                 self.assertEqual(packet_index, next_info.index)
206                 saved_packet = next_info.data
207                 # Check standard fields
208                 self.assertEqual(ip1.src, saved_packet[IP].src)
209                 self.assertEqual(ip1.dst, saved_packet[IP].dst)
210                 self.assertEqual(udp1.sport, saved_packet[UDP].sport)
211                 self.assertEqual(udp1.dport, saved_packet[UDP].dport)
212             except:
213                 self.logger.error("Unexpected or invalid packets:")
214                 self.logger.error(ppp("pg1 packet:", pkt_pg1))
215                 self.logger.error(ppp("pg2 packet:", pkt_pg2))
216                 raise
217         for i in self.interfaces:
218             remaining_packet = self.get_next_packet_info_for_interface2(
219                 i, dst_sw_if_index, last_info[i.sw_if_index])
220             self.assertTrue(remaining_packet is None,
221                             "Port %u: Packet expected from source %u didn't"
222                             " arrive" % (dst_sw_if_index, i.sw_if_index))
223
224     def test_device_span(self):
225         """ SPAN device rx mirror test
226
227         Test scenario:
228             1. config
229                3 interfaces, pg0 l2xconnected with pg1
230             2. sending l2 eth packets between 2 interfaces (pg0, pg1) and
231                mirrored to pg2
232                64B, 512B, 1518B, 9018B (ether_size)
233                burst of packets per interface
234         """
235
236         # Create bi-directional cross-connects between pg0 and pg1
237         self.xconnect(self.pg0.sw_if_index, self.pg1.sw_if_index)
238         # Create incoming packet streams for packet-generator interfaces
239         pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes)
240         self.pg0.add_stream(pkts)
241
242         # Enable SPAN on pg0 (mirrored to pg2)
243         self.vapi.sw_interface_span_enable_disable(
244             self.pg0.sw_if_index, self.pg2.sw_if_index)
245
246         self.logger.info(self.vapi.ppcli("show interface span"))
247         # Enable packet capturing and start packet sending
248         self.pg_enable_capture(self.pg_interfaces)
249         self.pg_start()
250
251         # Verify packets outgoing packet streams on mirrored interface (pg2)
252         self.logger.info("Verifying capture on interfaces %s and %s" %
253                          (self.pg1.name, self.pg2.name))
254         pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
255         self.verify_capture(
256             self.pg1,
257             self.pg1.get_capture(),
258             self.pg2.get_capture(pg2_expected))
259
260         # Disable SPAN on pg0 (mirrored to pg2)
261         self.vapi.sw_interface_span_enable_disable(
262             self.pg0.sw_if_index, self.pg2.sw_if_index, state=0)
263         self.xconnect(self.pg0.sw_if_index, self.pg1.sw_if_index, is_add=0)
264
265     def test_span_l2_rx(self):
266         """ SPAN l2 rx mirror test """
267
268         self.sub_if.admin_up()
269
270         self.bridge(self.pg2.sw_if_index)
271         # Create bi-directional cross-connects between pg0 and pg1
272         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
273         # Create incoming packet streams for packet-generator interfaces
274         pkts = self.create_stream(
275             self.pg0, self.pg_if_packet_sizes, do_dot1=True)
276         self.pg0.add_stream(pkts)
277
278         # Enable SPAN on pg0 (mirrored to pg2)
279         self.vapi.sw_interface_span_enable_disable(
280             self.sub_if.sw_if_index, self.pg2.sw_if_index, is_l2=1)
281
282         self.logger.info(self.vapi.ppcli("show interface span"))
283         # Enable packet capturing and start packet sending
284         self.pg_enable_capture(self.pg_interfaces)
285         self.pg_start()
286
287         # Verify packets outgoing packet streams on mirrored interface (pg2)
288         self.logger.info("Verifying capture on interfaces %s and %s" %
289                          (self.pg1.name, self.pg2.name))
290         pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
291         pg1_pkts = self.pg1.get_capture()
292         pg2_pkts = self.pg2.get_capture(pg2_expected)
293         self.verify_capture(
294             self.pg1,
295             pg1_pkts,
296             pg2_pkts)
297
298         self.bridge(self.pg2.sw_if_index, is_add=0)
299         # Disable SPAN on pg0 (mirrored to pg2)
300         self.vapi.sw_interface_span_enable_disable(
301             self.sub_if.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1)
302         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
303
304     def test_span_l2_rx_dst_vxlan(self):
305         """ SPAN l2 rx mirror into vxlan test """
306
307         self.sub_if.admin_up()
308         self.vapi.sw_interface_set_flags(self.vxlan.sw_if_index,
309                                          admin_up_down=1)
310
311         self.bridge(self.vxlan.sw_if_index, is_add=1)
312         # Create bi-directional cross-connects between pg0 and pg1
313         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
314         # Create incoming packet streams for packet-generator interfaces
315         pkts = self.create_stream(
316             self.pg0, self.pg_if_packet_sizes, do_dot1=True)
317         self.pg0.add_stream(pkts)
318
319         # Enable SPAN on pg0 sub if (mirrored to vxlan)
320         self.vapi.sw_interface_span_enable_disable(
321             self.sub_if.sw_if_index, self.vxlan.sw_if_index, is_l2=1)
322
323         self.logger.info(self.vapi.ppcli("show interface span"))
324         # Enable packet capturing and start packet sending
325         self.pg_enable_capture(self.pg_interfaces)
326         self.pg_start()
327
328         # Verify packets outgoing packet streams on mirrored interface (pg2)
329         self.logger.info("Verifying capture on interfaces %s and %s" %
330                          (self.pg1.name, self.pg2.name))
331         pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
332         pg1_pkts = self.pg1.get_capture()
333         pg2_pkts = [self.decap_vxlan(p)
334                     for p in self.pg2.get_capture(pg2_expected)]
335         self.verify_capture(
336             self.pg1,
337             pg1_pkts,
338             pg2_pkts)
339
340         self.bridge(self.vxlan.sw_if_index, is_add=0)
341         # Disable SPAN on pg0 sub if (mirrored to vxlan)
342         self.vapi.sw_interface_span_enable_disable(
343             self.sub_if.sw_if_index, self.vxlan.sw_if_index, state=0, is_l2=1)
344         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
345
346     def test_span_l2_rx_dst_gre_subif_vtr(self):
347         """ SPAN l2 rx mirror into gre-subif+vtr """
348
349         self.sub_if.admin_up()
350
351         gre_if = VppGreInterface(self, self.pg2.local_ip4,
352                                  self.pg2.remote_ip4,
353                                  is_teb=1)
354
355         gre_if.add_vpp_config()
356         gre_if.admin_up()
357
358         gre_sub_if = VppDot1QSubint(self, gre_if, 500)
359         gre_sub_if.set_vtr(L2_VTR_OP.L2_POP_1, tag=500)
360         gre_sub_if.admin_up()
361
362         self.bridge(gre_sub_if.sw_if_index)
363         # Create bi-directional cross-connects between pg0 and pg1
364         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
365
366         # Create incoming packet streams for packet-generator interfaces
367         pkts = self.create_stream(
368             self.pg0, self.pg_if_packet_sizes, do_dot1=True)
369         self.pg0.add_stream(pkts)
370
371         self.vapi.sw_interface_span_enable_disable(
372             self.sub_if.sw_if_index, gre_sub_if.sw_if_index, is_l2=1)
373
374         # Enable packet capturing and start packet sending
375         self.pg_enable_capture(self.pg_interfaces)
376         self.pg_start()
377
378         # Verify packets outgoing packet streams on mirrored interface (pg2)
379         self.logger.info("Verifying capture on interfaces %s and %s" %
380                          (self.pg1.name, self.pg2.name))
381         pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
382         pg1_pkts = self.pg1.get_capture()
383         pg2_pkts = self.pg2.get_capture(pg2_expected)
384         pg2_decaped = [self.remove_tags(self.decap_gre(
385             p), [Tag(dot1=DOT1Q, vlan=500)]) for p in pg2_pkts]
386         self.verify_capture(
387             self.pg1,
388             pg1_pkts,
389             pg2_decaped)
390
391         self.bridge(gre_sub_if.sw_if_index, is_add=0)
392         # Disable SPAN on pg0 sub if
393         self.vapi.sw_interface_span_enable_disable(
394             self.sub_if.sw_if_index, gre_sub_if.sw_if_index, state=0,
395             is_l2=1)
396         gre_if.remove_vpp_config()
397         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
398
399     def test_span_l2_rx_dst_vtr(self):
400         """ SPAN l2 rx mirror into subif+vtr """
401
402         self.sub_if.admin_up()
403         self.dst_sub_if.admin_up()
404
405         self.bridge(self.dst_sub_if.sw_if_index)
406         # Create bi-directional cross-connects between pg0 and pg1
407         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
408
409         # Create incoming packet streams for packet-generator interfaces
410         pkts = self.create_stream(
411             self.pg0, self.pg_if_packet_sizes, do_dot1=True)
412         self.pg0.add_stream(pkts)
413
414         self.vapi.sw_interface_span_enable_disable(
415             self.sub_if.sw_if_index, self.dst_sub_if.sw_if_index, is_l2=1)
416
417         # Enable packet capturing and start packet sending
418         self.pg_enable_capture(self.pg_interfaces)
419         self.pg_start()
420
421         # Verify packets outgoing packet streams on mirrored interface (pg2)
422         self.logger.info("Verifying capture on interfaces %s and %s" %
423                          (self.pg1.name, self.pg2.name))
424         pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
425         pg1_pkts = self.pg1.get_capture()
426         pg2_pkts = self.pg2.get_capture(pg2_expected)
427         pg2_untagged = [self.remove_tags(p, [Tag(dot1=DOT1Q, vlan=300)])
428                         for p in pg2_pkts]
429         self.verify_capture(
430             self.pg1,
431             pg1_pkts,
432             pg2_untagged)
433
434         self.bridge(self.dst_sub_if.sw_if_index, is_add=0)
435         # Disable SPAN on pg0 sub if (mirrored to vxlan)
436         self.vapi.sw_interface_span_enable_disable(
437             self.sub_if.sw_if_index, self.dst_sub_if.sw_if_index, state=0,
438             is_l2=1)
439         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
440
441     def test_l2_tx_span(self):
442         """ SPAN l2 tx mirror test """
443
444         self.sub_if.admin_up()
445         self.bridge(self.pg2.sw_if_index)
446         # Create bi-directional cross-connects between pg0 and pg1
447         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
448         # Create incoming packet streams for packet-generator interfaces
449         pkts = self.create_stream(
450             self.pg0, self.pg_if_packet_sizes, do_dot1=True)
451         self.pg0.add_stream(pkts)
452
453         # Enable SPAN on pg0 (mirrored to pg2)
454         self.vapi.sw_interface_span_enable_disable(
455             self.pg1.sw_if_index, self.pg2.sw_if_index, is_l2=1, state=2)
456
457         self.logger.info(self.vapi.ppcli("show interface span"))
458         # Enable packet capturing and start packet sending
459         self.pg_enable_capture(self.pg_interfaces)
460         self.pg_start()
461
462         # Verify packets outgoing packet streams on mirrored interface (pg2)
463         self.logger.info("Verifying capture on interfaces %s and %s" %
464                          (self.pg1.name, self.pg2.name))
465         pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
466         pg1_pkts = self.pg1.get_capture()
467         pg2_pkts = self.pg2.get_capture(pg2_expected)
468         self.verify_capture(
469             self.pg1,
470             pg1_pkts,
471             pg2_pkts)
472
473         self.bridge(self.pg2.sw_if_index, is_add=0)
474         # Disable SPAN on pg0 (mirrored to pg2)
475         self.vapi.sw_interface_span_enable_disable(
476             self.pg1.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1)
477         self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
478
479
480 if __name__ == '__main__':
481     unittest.main(testRunner=VppTestRunner)