ethernet: check destination mac for L3 in ethernet-input node
[vpp.git] / test / test_dvr.py
1 #!/usr/bin/env python3
2 import unittest
3
4 from framework import VppTestCase
5 from asfframework import VppTestRunner
6 from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathType
7 from vpp_l2 import L2_PORT_TYPE
8 from vpp_sub_interface import L2_VTR_OP, VppDot1QSubint
9 from vpp_acl import AclRule, VppAcl, VppAclInterface
10
11 from scapy.packet import Raw
12 from scapy.layers.l2 import Ether, Dot1Q
13 from scapy.layers.inet import IP, UDP
14 from socket import AF_INET
15 from ipaddress import IPv4Network
16
17 NUM_PKTS = 67
18
19
20 class TestDVR(VppTestCase):
21     """Distributed Virtual Router"""
22
23     @classmethod
24     def setUpClass(cls):
25         super(TestDVR, cls).setUpClass()
26
27     @classmethod
28     def tearDownClass(cls):
29         super(TestDVR, cls).tearDownClass()
30
31     def setUp(self):
32         super(TestDVR, self).setUp()
33
34         self.create_pg_interfaces(range(4))
35         self.create_loopback_interfaces(1)
36
37         for i in self.pg_interfaces:
38             i.admin_up()
39
40         self.loop0.config_ip4()
41
42     def tearDown(self):
43         for i in self.pg_interfaces:
44             i.admin_down()
45         self.loop0.unconfig_ip4()
46
47         super(TestDVR, self).tearDown()
48
49     def assert_same_mac_addr(self, tx, rx):
50         t_eth = tx[Ether]
51         for p in rx:
52             r_eth = p[Ether]
53             self.assertEqual(t_eth.src, r_eth.src)
54             self.assertEqual(t_eth.dst, r_eth.dst)
55
56     def assert_has_vlan_tag(self, tag, rx):
57         for p in rx:
58             r_1q = p[Dot1Q]
59             self.assertEqual(tag, r_1q.vlan)
60
61     def assert_has_no_tag(self, rx):
62         for p in rx:
63             self.assertFalse(p.haslayer(Dot1Q))
64
65     def test_dvr(self):
66         """Distributed Virtual Router"""
67
68         #
69         # A packet destined to an IP address that is L2 bridged via
70         # a non-tag interface
71         #
72         ip_non_tag_bridged = "10.10.10.10"
73         ip_tag_bridged = "10.10.10.11"
74         any_src_addr = "1.1.1.1"
75
76         pkt_no_tag = (
77             Ether(src=self.pg0.remote_mac, dst=self.loop0.local_mac)
78             / IP(src=any_src_addr, dst=ip_non_tag_bridged)
79             / UDP(sport=1234, dport=1234)
80             / Raw(b"\xa5" * 100)
81         )
82         pkt_tag = (
83             Ether(src=self.pg0.remote_mac, dst=self.loop0.local_mac)
84             / IP(src=any_src_addr, dst=ip_tag_bridged)
85             / UDP(sport=1234, dport=1234)
86             / Raw(b"\xa5" * 100)
87         )
88
89         #
90         # Two sub-interfaces so we can test VLAN tag push/pop
91         #
92         sub_if_on_pg2 = VppDot1QSubint(self, self.pg2, 92)
93         sub_if_on_pg3 = VppDot1QSubint(self, self.pg3, 93)
94         sub_if_on_pg2.admin_up()
95         sub_if_on_pg3.admin_up()
96
97         #
98         # Put all the interfaces into a new bridge domain
99         #
100         self.vapi.sw_interface_set_l2_bridge(
101             rx_sw_if_index=self.pg0.sw_if_index, bd_id=1
102         )
103         self.vapi.sw_interface_set_l2_bridge(
104             rx_sw_if_index=self.pg1.sw_if_index, bd_id=1
105         )
106         self.vapi.sw_interface_set_l2_bridge(
107             rx_sw_if_index=sub_if_on_pg2.sw_if_index, bd_id=1
108         )
109         self.vapi.sw_interface_set_l2_bridge(
110             rx_sw_if_index=sub_if_on_pg3.sw_if_index, bd_id=1
111         )
112         self.vapi.sw_interface_set_l2_bridge(
113             rx_sw_if_index=self.loop0.sw_if_index, bd_id=1, port_type=L2_PORT_TYPE.BVI
114         )
115
116         self.vapi.l2_interface_vlan_tag_rewrite(
117             sw_if_index=sub_if_on_pg2.sw_if_index,
118             vtr_op=L2_VTR_OP.L2_POP_1,
119             push_dot1q=92,
120         )
121         self.vapi.l2_interface_vlan_tag_rewrite(
122             sw_if_index=sub_if_on_pg3.sw_if_index,
123             vtr_op=L2_VTR_OP.L2_POP_1,
124             push_dot1q=93,
125         )
126
127         #
128         # Add routes to bridge the traffic via a tagged an nontagged interface
129         #
130         route_no_tag = VppIpRoute(
131             self,
132             ip_non_tag_bridged,
133             32,
134             [
135                 VppRoutePath(
136                     "0.0.0.0", self.pg1.sw_if_index, type=FibPathType.FIB_PATH_TYPE_DVR
137                 )
138             ],
139         )
140         route_no_tag.add_vpp_config()
141
142         #
143         # Inject the packet that arrives and leaves on a non-tagged interface
144         # Since it's 'bridged' expect that the MAC headed is unchanged.
145         #
146         rx = self.send_and_expect(self.pg0, pkt_no_tag * NUM_PKTS, self.pg1)
147         self.assert_same_mac_addr(pkt_no_tag, rx)
148         self.assert_has_no_tag(rx)
149
150         #
151         # Add routes to bridge the traffic via a tagged interface
152         #
153         route_with_tag = VppIpRoute(
154             self,
155             ip_tag_bridged,
156             32,
157             [
158                 VppRoutePath(
159                     "0.0.0.0",
160                     sub_if_on_pg3.sw_if_index,
161                     type=FibPathType.FIB_PATH_TYPE_DVR,
162                 )
163             ],
164         )
165         route_with_tag.add_vpp_config()
166
167         #
168         # Inject the packet that arrives non-tag and leaves on a tagged
169         # interface
170         #
171         rx = self.send_and_expect(self.pg0, pkt_tag * NUM_PKTS, self.pg3)
172         self.assert_same_mac_addr(pkt_tag, rx)
173         self.assert_has_vlan_tag(93, rx)
174
175         #
176         # Tag to tag
177         #
178         pkt_tag_to_tag = (
179             Ether(src=self.pg2.remote_mac, dst=self.loop0.local_mac)
180             / Dot1Q(vlan=92)
181             / IP(src=any_src_addr, dst=ip_tag_bridged)
182             / UDP(sport=1234, dport=1234)
183             / Raw(b"\xa5" * 100)
184         )
185
186         rx = self.send_and_expect(self.pg2, pkt_tag_to_tag * NUM_PKTS, self.pg3)
187         self.assert_same_mac_addr(pkt_tag_to_tag, rx)
188         self.assert_has_vlan_tag(93, rx)
189
190         #
191         # Tag to non-Tag
192         #
193         pkt_tag_to_non_tag = (
194             Ether(src=self.pg2.remote_mac, dst=self.loop0.local_mac)
195             / Dot1Q(vlan=92)
196             / IP(src=any_src_addr, dst=ip_non_tag_bridged)
197             / UDP(sport=1234, dport=1234)
198             / Raw(b"\xa5" * 100)
199         )
200
201         rx = self.send_and_expect(self.pg2, pkt_tag_to_non_tag * NUM_PKTS, self.pg1)
202         self.assert_same_mac_addr(pkt_tag_to_tag, rx)
203         self.assert_has_no_tag(rx)
204
205         #
206         # Add an output L3 ACL that will block the traffic
207         #
208         rule_1 = AclRule(
209             is_permit=0,
210             proto=17,
211             ports=1234,
212             src_prefix=IPv4Network((any_src_addr, 32)),
213             dst_prefix=IPv4Network((ip_non_tag_bridged, 32)),
214         )
215         acl = VppAcl(self, rules=[rule_1])
216         acl.add_vpp_config()
217
218         #
219         # Apply the ACL on the output interface
220         #
221         acl_if1 = VppAclInterface(
222             self, sw_if_index=self.pg1.sw_if_index, n_input=0, acls=[acl]
223         )
224         acl_if1.add_vpp_config()
225
226         #
227         # Send packet's that should match the ACL and be dropped
228         #
229         rx = self.send_and_assert_no_replies(self.pg2, pkt_tag_to_non_tag * NUM_PKTS)
230
231         #
232         # cleanup
233         #
234         acl_if1.remove_vpp_config()
235         acl.remove_vpp_config()
236
237         self.vapi.sw_interface_set_l2_bridge(
238             rx_sw_if_index=self.pg0.sw_if_index, bd_id=1, enable=0
239         )
240         self.vapi.sw_interface_set_l2_bridge(
241             rx_sw_if_index=self.pg1.sw_if_index, bd_id=1, enable=0
242         )
243         self.vapi.sw_interface_set_l2_bridge(
244             rx_sw_if_index=sub_if_on_pg2.sw_if_index, bd_id=1, enable=0
245         )
246         self.vapi.sw_interface_set_l2_bridge(
247             rx_sw_if_index=sub_if_on_pg3.sw_if_index, bd_id=1, enable=0
248         )
249         self.vapi.sw_interface_set_l2_bridge(
250             rx_sw_if_index=self.loop0.sw_if_index,
251             bd_id=1,
252             port_type=L2_PORT_TYPE.BVI,
253             enable=0,
254         )
255
256         #
257         # Do a FIB dump to make sure the paths are correctly reported as DVR
258         #
259         routes = self.vapi.ip_route_dump(0)
260
261         for r in routes:
262             if ip_tag_bridged == str(r.route.prefix.network_address):
263                 self.assertEqual(
264                     r.route.paths[0].sw_if_index, sub_if_on_pg3.sw_if_index
265                 )
266                 self.assertEqual(r.route.paths[0].type, FibPathType.FIB_PATH_TYPE_DVR)
267             if ip_non_tag_bridged == str(r.route.prefix.network_address):
268                 self.assertEqual(r.route.paths[0].sw_if_index, self.pg1.sw_if_index)
269                 self.assertEqual(r.route.paths[0].type, FibPathType.FIB_PATH_TYPE_DVR)
270
271         #
272         # the explicit route delete is require so it happens before
273         # the sbu-interface delete. subinterface delete is required
274         # because that object type does not use the object registry
275         #
276         route_no_tag.remove_vpp_config()
277         route_with_tag.remove_vpp_config()
278         sub_if_on_pg3.remove_vpp_config()
279         sub_if_on_pg2.remove_vpp_config()
280
281
282 if __name__ == "__main__":
283     unittest.main(testRunner=VppTestRunner)