L2 Emulation
[vpp.git] / test / test_dvr.py
1 #!/usr/bin/env python
2 import random
3 import socket
4 import unittest
5
6 from framework import VppTestCase, VppTestRunner
7 from vpp_sub_interface import VppSubInterface, VppDot1QSubint
8 from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto, VppIpMRoute, \
9     VppMRoutePath, MRouteEntryFlags, MRouteItfFlags
10 from vpp_papi_provider import L2_VTR_OP
11
12 from scapy.packet import Raw
13 from scapy.layers.l2 import Ether, Dot1Q, ARP
14 from scapy.layers.inet import IP, UDP
15 from util import ppp
16
17
18 class TestDVR(VppTestCase):
19     """ Distributed Virtual Router """
20
21     def setUp(self):
22         super(TestDVR, self).setUp()
23
24         self.create_pg_interfaces(range(4))
25         self.create_loopback_interfaces(range(1))
26
27         for i in self.pg_interfaces:
28             i.admin_up()
29
30         self.loop0.config_ip4()
31
32     def tearDown(self):
33         for i in self.pg_interfaces:
34             i.admin_down()
35         self.loop0.unconfig_ip4()
36
37         super(TestDVR, self).tearDown()
38
39     def send_and_assert_no_replies(self, intf, pkts):
40         self.vapi.cli("clear trace")
41         intf.add_stream(pkts)
42         self.pg_enable_capture(self.pg_interfaces)
43         self.pg_start()
44         for i in self.pg_interfaces:
45             i.get_capture(0)
46             i.assert_nothing_captured()
47
48     def send_and_expect(self, input, pkts, output):
49         self.vapi.cli("clear trace")
50         input.add_stream(pkts)
51         self.pg_enable_capture(self.pg_interfaces)
52         self.pg_start()
53         rx = output.get_capture(len(pkts))
54         return rx
55
56     def assert_same_mac_addr(self, tx, rx):
57         t_eth = tx[Ether]
58         for p in rx:
59             r_eth = p[Ether]
60             self.assertEqual(t_eth.src, r_eth.src)
61             self.assertEqual(t_eth.dst, r_eth.dst)
62
63     def assert_has_vlan_tag(self, tag, rx):
64         for p in rx:
65             r_1q = p[Dot1Q]
66             self.assertEqual(tag, r_1q.vlan)
67
68     def assert_has_no_tag(self, rx):
69         for p in rx:
70             self.assertFalse(p.haslayer(Dot1Q))
71
72     def test_dvr(self):
73         """ Distributed Virtual Router """
74
75         #
76         # A packet destined to an IP address that is L2 bridged via
77         # a non-tag interface
78         #
79         ip_non_tag_bridged = "10.10.10.10"
80         ip_tag_bridged = "10.10.10.11"
81         any_src_addr = "1.1.1.1"
82
83         pkt_no_tag = (Ether(src=self.pg0.remote_mac,
84                             dst=self.loop0.local_mac) /
85                       IP(src=any_src_addr,
86                          dst=ip_non_tag_bridged) /
87                       UDP(sport=1234, dport=1234) /
88                       Raw('\xa5' * 100))
89         pkt_tag = (Ether(src=self.pg0.remote_mac,
90                          dst=self.loop0.local_mac) /
91                    IP(src=any_src_addr,
92                       dst=ip_tag_bridged) /
93                    UDP(sport=1234, dport=1234) /
94                    Raw('\xa5' * 100))
95
96         #
97         # Two sub-interfaces so we can test VLAN tag push/pop
98         #
99         sub_if_on_pg2 = VppDot1QSubint(self, self.pg2, 92)
100         sub_if_on_pg3 = VppDot1QSubint(self, self.pg3, 93)
101         sub_if_on_pg2.admin_up()
102         sub_if_on_pg3.admin_up()
103
104         #
105         # Put all the interfaces into a new bridge domain
106         #
107         self.vapi.sw_interface_set_l2_bridge(self.pg0.sw_if_index, 1)
108         self.vapi.sw_interface_set_l2_bridge(self.pg1.sw_if_index, 1)
109         self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg2.sw_if_index, 1)
110         self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index, 1)
111         self.vapi.sw_interface_set_l2_bridge(self.loop0.sw_if_index, 1, bvi=1)
112
113         self.vapi.sw_interface_set_l2_tag_rewrite(sub_if_on_pg2.sw_if_index,
114                                                   L2_VTR_OP.L2_POP_1,
115                                                   92)
116         self.vapi.sw_interface_set_l2_tag_rewrite(sub_if_on_pg3.sw_if_index,
117                                                   L2_VTR_OP.L2_POP_1,
118                                                   93)
119
120         #
121         # Add routes to bridge the traffic via a tagged an nontagged interface
122         #
123         route_no_tag = VppIpRoute(
124             self, ip_non_tag_bridged, 32,
125             [VppRoutePath("0.0.0.0",
126                           self.pg1.sw_if_index,
127                           proto=DpoProto.DPO_PROTO_ETHERNET)])
128         route_no_tag.add_vpp_config()
129
130         #
131         # Inject the packet that arrives and leaves on a non-tagged interface
132         # Since it's 'bridged' expect that the MAC headed is unchanged.
133         #
134         self.pg0.add_stream(pkt_no_tag)
135
136         self.pg_enable_capture(self.pg_interfaces)
137         self.pg_start()
138
139         rx = self.pg1.get_capture(1)
140
141         self.assertEqual(rx[0][Ether].dst, pkt_no_tag[Ether].dst)
142         self.assertEqual(rx[0][Ether].src, pkt_no_tag[Ether].src)
143
144         #
145         # Add routes to bridge the traffic via a tagged interface
146         #
147         route_with_tag = VppIpRoute(
148             self, ip_tag_bridged, 32,
149             [VppRoutePath("0.0.0.0",
150                           sub_if_on_pg3.sw_if_index,
151                           proto=DpoProto.DPO_PROTO_ETHERNET)])
152         route_with_tag.add_vpp_config()
153
154         #
155         # Inject the packet that arrives and leaves on a non-tagged interface
156         # Since it's 'bridged' expect that the MAC headed is unchanged.
157         #
158         rx = self.send_and_expect(self.pg0, pkt_tag * 65, self.pg3)
159         self.assert_same_mac_addr(pkt_tag, rx)
160         self.assert_has_vlan_tag(93, rx)
161
162         #
163         # Tag to tag
164         #
165         pkt_tag_to_tag = (Ether(src=self.pg2.remote_mac,
166                                 dst=self.loop0.local_mac) /
167                           Dot1Q(vlan=92) /
168                           IP(src=any_src_addr,
169                              dst=ip_tag_bridged) /
170                           UDP(sport=1234, dport=1234) /
171                           Raw('\xa5' * 100))
172
173         rx = self.send_and_expect(self.pg2, pkt_tag_to_tag * 65, self.pg3)
174         self.assert_same_mac_addr(pkt_tag_to_tag, rx)
175         self.assert_has_vlan_tag(93, rx)
176
177         #
178         # Tag to non-Tag
179         #
180         pkt_tag_to_non_tag = (Ether(src=self.pg2.remote_mac,
181                                     dst=self.loop0.local_mac) /
182                               Dot1Q(vlan=92) /
183                               IP(src=any_src_addr,
184                                  dst=ip_non_tag_bridged) /
185                               UDP(sport=1234, dport=1234) /
186                               Raw('\xa5' * 100))
187
188         rx = self.send_and_expect(self.pg2, pkt_tag_to_non_tag * 65, self.pg1)
189         self.assert_same_mac_addr(pkt_tag_to_tag, rx)
190         self.assert_has_no_tag(rx)
191
192         #
193         # cleanup
194         #
195         self.vapi.sw_interface_set_l2_bridge(self.pg0.sw_if_index, 1,
196                                              enable=0)
197         self.vapi.sw_interface_set_l2_bridge(self.pg1.sw_if_index, 1,
198                                              enable=0)
199         self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg2.sw_if_index,
200                                              1, enable=0)
201         self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index,
202                                              1, enable=0)
203         self.vapi.sw_interface_set_l2_bridge(self.loop0.sw_if_index,
204                                              1, bvi=1, enable=0)
205
206         #
207         # the explicit route delete is require so it happens before
208         # the sbu-interface delete. subinterface delete is required
209         # because that object type does not use the object registry
210         #
211         route_no_tag.remove_vpp_config()
212         route_with_tag.remove_vpp_config()
213         sub_if_on_pg3.remove_vpp_config()
214         sub_if_on_pg2.remove_vpp_config()
215
216     def test_l2_emulation(self):
217         """ L2 Emulation """
218
219         #
220         # non distinct L3 packets, in the tag/non-tag combos
221         #
222         pkt_no_tag = (Ether(src=self.pg0.remote_mac,
223                             dst=self.pg1.remote_mac) /
224                       IP(src="2.2.2.2",
225                          dst="1.1.1.1") /
226                       UDP(sport=1234, dport=1234) /
227                       Raw('\xa5' * 100))
228         pkt_to_tag = (Ether(src=self.pg0.remote_mac,
229                             dst=self.pg2.remote_mac) /
230                       IP(src="2.2.2.2",
231                          dst="1.1.1.2") /
232                       UDP(sport=1234, dport=1234) /
233                       Raw('\xa5' * 100))
234         pkt_from_tag = (Ether(src=self.pg3.remote_mac,
235                               dst=self.pg2.remote_mac) /
236                         Dot1Q(vlan=93) /
237                         IP(src="2.2.2.2",
238                            dst="1.1.1.1") /
239                         UDP(sport=1234, dport=1234) /
240                         Raw('\xa5' * 100))
241         pkt_from_to_tag = (Ether(src=self.pg3.remote_mac,
242                                  dst=self.pg2.remote_mac) /
243                            Dot1Q(vlan=93) /
244                            IP(src="2.2.2.2",
245                               dst="1.1.1.2") /
246                            UDP(sport=1234, dport=1234) /
247                            Raw('\xa5' * 100))
248         pkt_bcast = (Ether(src=self.pg0.remote_mac,
249                            dst="ff:ff:ff:ff:ff:ff") /
250                      IP(src="2.2.2.2",
251                         dst="255.255.255.255") /
252                      UDP(sport=1234, dport=1234) /
253                      Raw('\xa5' * 100))
254
255         #
256         # A couple of sub-interfaces for tags
257         #
258         sub_if_on_pg2 = VppDot1QSubint(self, self.pg2, 92)
259         sub_if_on_pg3 = VppDot1QSubint(self, self.pg3, 93)
260         sub_if_on_pg2.admin_up()
261         sub_if_on_pg3.admin_up()
262
263         #
264         # Put all the interfaces into a new bridge domain
265         #
266         self.vapi.sw_interface_set_l2_bridge(self.pg0.sw_if_index, 1)
267         self.vapi.sw_interface_set_l2_bridge(self.pg1.sw_if_index, 1)
268         self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg2.sw_if_index, 1)
269         self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index, 1)
270         self.vapi.sw_interface_set_l2_tag_rewrite(sub_if_on_pg2.sw_if_index,
271                                                   L2_VTR_OP.L2_POP_1,
272                                                   92)
273         self.vapi.sw_interface_set_l2_tag_rewrite(sub_if_on_pg3.sw_if_index,
274                                                   L2_VTR_OP.L2_POP_1,
275                                                   93)
276
277         #
278         # Disable UU flooding, learning and ARM terminaation. makes this test
279         # easier as unicast packets are dropped if not extracted.
280         #
281         self.vapi.bridge_flags(1, 0, (1 << 0) | (1 << 3) | (1 << 4))
282
283         #
284         # Add a DVR route to steer traffic at L3
285         #
286         route_1 = VppIpRoute(self, "1.1.1.1", 32,
287                              [VppRoutePath("0.0.0.0",
288                                            self.pg1.sw_if_index,
289                                            proto=DpoProto.DPO_PROTO_ETHERNET)])
290         route_2 = VppIpRoute(self, "1.1.1.2", 32,
291                              [VppRoutePath("0.0.0.0",
292                                            sub_if_on_pg2.sw_if_index,
293                                            proto=DpoProto.DPO_PROTO_ETHERNET)])
294         route_1.add_vpp_config()
295         route_2.add_vpp_config()
296
297         #
298         # packets are dropped because bridge does not flood unkown unicast
299         #
300         self.send_and_assert_no_replies(self.pg0, pkt_no_tag)
301
302         #
303         # Enable L3 extraction on pgs
304         #
305         self.vapi.sw_interface_set_l2_emulation(self.pg0.sw_if_index)
306         self.vapi.sw_interface_set_l2_emulation(self.pg1.sw_if_index)
307         self.vapi.sw_interface_set_l2_emulation(sub_if_on_pg2.sw_if_index)
308         self.vapi.sw_interface_set_l2_emulation(sub_if_on_pg3.sw_if_index)
309
310         #
311         # now we expect the packet forward according to the DVR route
312         #
313         rx = self.send_and_expect(self.pg0, pkt_no_tag * 65, self.pg1)
314         self.assert_same_mac_addr(pkt_no_tag, rx)
315         self.assert_has_no_tag(rx)
316
317         rx = self.send_and_expect(self.pg0, pkt_to_tag * 65, self.pg2)
318         self.assert_same_mac_addr(pkt_to_tag, rx)
319         self.assert_has_vlan_tag(92, rx)
320
321         rx = self.send_and_expect(self.pg3, pkt_from_tag * 65, self.pg1)
322         self.assert_same_mac_addr(pkt_from_tag, rx)
323         self.assert_has_no_tag(rx)
324
325         rx = self.send_and_expect(self.pg3, pkt_from_to_tag * 65, self.pg2)
326         self.assert_same_mac_addr(pkt_from_tag, rx)
327         self.assert_has_vlan_tag(92, rx)
328
329         #
330         # but broadcast packets are still flooded
331         #
332         self.send_and_expect(self.pg0, pkt_bcast * 33, self.pg2)
333
334         #
335         # cleanup
336         #
337         self.vapi.sw_interface_set_l2_emulation(self.pg0.sw_if_index,
338                                                 enable=0)
339         self.vapi.sw_interface_set_l2_emulation(self.pg1.sw_if_index,
340                                                 enable=0)
341         self.vapi.sw_interface_set_l2_emulation(sub_if_on_pg2.sw_if_index,
342                                                 enable=0)
343         self.vapi.sw_interface_set_l2_emulation(sub_if_on_pg3.sw_if_index,
344                                                 enable=0)
345
346         self.vapi.sw_interface_set_l2_bridge(self.pg0.sw_if_index,
347                                              1, enable=0)
348         self.vapi.sw_interface_set_l2_bridge(self.pg1.sw_if_index,
349                                              1, enable=0)
350         self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg2.sw_if_index,
351                                              1, enable=0)
352         self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index,
353                                              1, enable=0)
354
355         route_1.remove_vpp_config()
356         route_2.remove_vpp_config()
357         sub_if_on_pg3.remove_vpp_config()
358         sub_if_on_pg2.remove_vpp_config()
359
360
361 if __name__ == '__main__':
362     unittest.main(testRunner=VppTestRunner)