map: Prevent IPv4 prefix spoofing during IPv6 -> IPv4
[vpp.git] / src / plugins / map / test / test_map_br.py
1 #!/usr/bin/env python3
2
3 import ipaddress
4 import unittest
5
6 from framework import VppTestCase, VppTestRunner
7 from vpp_ip import DpoProto
8 from vpp_ip_route import VppIpRoute, VppRoutePath
9 from util import fragment_rfc791, fragment_rfc8200
10
11 import scapy.compat
12 from scapy.layers.l2 import Ether
13 from scapy.packet import Raw
14 from scapy.layers.inet import IP, UDP, ICMP, TCP
15 from scapy.layers.inet6 import IPv6, ICMPv6TimeExceeded, IPv6ExtHdrFragment
16 from scapy.layers.inet6 import ICMPv6EchoRequest, ICMPv6EchoReply
17
18
19 class TestMAPBR(VppTestCase):
20     """ MAP-T Test Cases """
21
22     @classmethod
23     def setUpClass(cls):
24         super(TestMAPBR, cls).setUpClass()
25
26     @classmethod
27     def tearDownClass(cls):
28         super(TestMAPBR, cls).tearDownClass()
29
30     def setUp(self):
31         super(TestMAPBR, self).setUp()
32
33         #
34         # Create 2 pg interfaces.
35         # pg0 is IPv4
36         # pg1 is IPv6
37         #
38         self.create_pg_interfaces(range(2))
39
40         self.pg0.admin_up()
41         self.pg0.config_ip4()
42         self.pg1.generate_remote_hosts(20)
43         self.pg1.configure_ipv4_neighbors()
44         self.pg0.resolve_arp()
45
46         self.pg1.admin_up()
47         self.pg1.config_ip6()
48         self.pg1.generate_remote_hosts(20)
49         self.pg1.configure_ipv6_neighbors()
50
51         #
52         # BR configuration parameters used for all test.
53         #
54         self.ip4_prefix = '198.18.0.0/24'
55         self.ip6_prefix = '2001:db8:f0::/48'
56         self.ip6_src = '2001:db8:ffff:ff00::/64'
57         self.ea_bits_len = 12
58         self.psid_offset = 6
59         self.psid_length = 4
60         self.mtu = 1500
61         self.tag = 'MAP-T BR'
62
63         self.ipv4_internet_address = self.pg0.remote_ip4
64         self.ipv4_map_address = "198.18.0.12"
65         self.ipv4_udp_or_tcp_internet_port = 65000
66         self.ipv4_udp_or_tcp_map_port = 16606
67
68         self.ipv6_cpe_address = "2001:db8:f0:c30:0:c612:c:3"      # 198.18.0.12
69         self.ipv6_spoof_address = "2001:db8:f0:c30:0:c612:1c:3"   # 198.18.0.28
70         self.ipv6_spoof_prefix = "2001:db8:f0:c30:0:a00:c:3"      # 10.0.0.12
71         self.ipv6_spoof_psid = "2001:db8:f0:c30:0:c612:c:4"       # 4
72         self.ipv6_spoof_subnet = "2001:db8:f1:c30:0:c612:c:3"     # f1
73
74         self.ipv6_udp_or_tcp_internet_port = 65000
75         self.ipv6_udp_or_tcp_map_port = 16606
76         self.ipv6_udp_or_tcp_spoof_port = 16862
77
78         self.ipv6_map_address = (
79             "2001:db8:ffff:ff00:ac:1001:200:0")         # 176.16.1.2
80         self.ipv6_map_same_rule_diff_addr = (
81             "2001:db8:ffff:ff00:c6:1200:10:0")          # 198.18.0.16
82
83         self.map_br_prefix = "2001:db8:f0::"
84         self.map_br_prefix_len = 48
85         self.psid_number = 3
86
87         #
88         # Add an IPv6 route to the MAP-BR.
89         #
90         map_route = VppIpRoute(self,
91                                self.map_br_prefix,
92                                self.map_br_prefix_len,
93                                [VppRoutePath(self.pg1.remote_ip6,
94                                              self.pg1.sw_if_index)])
95         map_route.add_vpp_config()
96
97         ip4_map_route = VppIpRoute(self,
98                                    "198.18.0.0",
99                                    24,
100                                    [VppRoutePath(self.pg1.remote_ip4,
101                                                  self.pg1.sw_if_index)])
102         ip4_map_route.add_vpp_config()
103
104         #
105         # Add a MAP BR domain that maps from pg0 to pg1.
106         #
107         self.vapi.map_add_domain(ip4_prefix=self.ip4_prefix,
108                                  ip6_prefix=self.ip6_prefix,
109                                  ip6_src=self.ip6_src,
110                                  ea_bits_len=self.ea_bits_len,
111                                  psid_offset=self.psid_offset,
112                                  psid_length=self.psid_length,
113                                  mtu=self.mtu,
114                                  tag=self.tag)
115
116         #
117         # Set BR parameters.
118         #
119         self.vapi.map_param_set_fragmentation(inner=1, ignore_df=0)
120         self.vapi.map_param_set_fragmentation(inner=0, ignore_df=0)
121         self.vapi.map_param_set_icmp(ip4_err_relay_src=self.pg0.local_ip4)
122         self.vapi.map_param_set_traffic_class(copy=1)
123
124         #
125         # Enable MAP-T on interfaces.
126         #
127         self.vapi.map_if_enable_disable(is_enable=1,
128                                         sw_if_index=self.pg0.sw_if_index,
129                                         is_translation=1)
130
131         self.vapi.map_if_enable_disable(is_enable=1,
132                                         sw_if_index=self.pg1.sw_if_index,
133                                         is_translation=1)
134
135         self.vapi.map_if_enable_disable(is_enable=1,
136                                         sw_if_index=self.pg1.sw_if_index,
137                                         is_translation=1)
138
139     def tearDown(self):
140         super(TestMAPBR, self).tearDown()
141         for i in self.pg_interfaces:
142             i.unconfig_ip4()
143             i.unconfig_ip6()
144             i.admin_down()
145
146     #
147     # Spoofed IPv4 Source Address v6 -> v4 direction
148     # Send a packet with a wrong IPv4 address embedded in bits 72-103.
149     # The BR should either drop the packet, or rewrite the spoofed
150     # source IPv4 as the actual source IPv4 address.
151     # The BR really should drop the packet.
152     #
153
154     def test_map_t_spoof_ipv4_src_addr_ip6_to_ip4(self):
155         """ MAP-T spoof ipv4 src addr IPv6 -> IPv4 """
156
157         eth = Ether(src=self.pg1.remote_mac,
158                     dst=self.pg1.local_mac)
159         ip = IPv6(src=self.ipv6_spoof_address,
160                   dst=self.ipv6_map_address)
161         udp = UDP(sport=self.ipv6_udp_or_tcp_map_port,
162                   dport=self.ipv6_udp_or_tcp_internet_port)
163         payload = "a" * 82
164         tx_pkt = eth / ip / udp / payload
165
166         self.pg_send(self.pg1, tx_pkt * 1)
167
168         self.pg0.get_capture(0, timeout=1)
169         self.pg0.assert_nothing_captured("Should drop IPv4 spoof address")
170
171     #
172     # Spoofed IPv4 Source Prefix v6 -> v4 direction
173     # Send a packet with a wrong IPv4 prefix embedded in bits 72-103.
174     # The BR should either drop the packet, or rewrite the source IPv4
175     # to the prefix that matches the source IPv4 address.
176     #
177
178     def test_map_t_spoof_ipv4_src_prefix_ip6_to_ip4(self):
179         """ MAP-T spoof ipv4 src prefix IPv6 -> IPv4 """
180
181         eth = Ether(src=self.pg1.remote_mac,
182                     dst=self.pg1.local_mac)
183         ip = IPv6(src=self.ipv6_spoof_prefix,
184                   dst=self.ipv6_map_address)
185         udp = UDP(sport=self.ipv6_udp_or_tcp_map_port,
186                   dport=self.ipv6_udp_or_tcp_internet_port)
187         payload = "a" * 82
188         tx_pkt = eth / ip / udp / payload
189
190         self.pg_send(self.pg1, tx_pkt * 1)
191
192         self.pg0.get_capture(0, timeout=1)
193         self.pg0.assert_nothing_captured("Should drop IPv4 spoof prefix")
194
195     #
196     # Spoofed IPv6 PSID v6 -> v4 direction
197     # Send a packet with a wrong IPv6 port PSID
198     # The BR should drop the packet.
199     #
200
201     def test_map_t_spoof_psid_ip6_to_ip4(self):
202         """ MAP-T spoof psid IPv6 -> IPv4 """
203
204         eth = Ether(src=self.pg1.remote_mac,
205                     dst=self.pg1.local_mac)
206         ip = IPv6(src=self.ipv6_spoof_psid,
207                   dst=self.ipv6_map_address)
208         udp = UDP(sport=self.ipv6_udp_or_tcp_map_port,
209                   dport=self.ipv6_udp_or_tcp_internet_port)
210         payload = "a" * 82
211         tx_pkt = eth / ip / udp / payload
212
213         self.pg_send(self.pg1, tx_pkt * 1)
214
215         self.pg0.get_capture(0, timeout=1)
216         self.pg0.assert_nothing_captured("Should drop IPv6 spoof PSID")
217
218     #
219     # Spoofed IPv6 subnet field v6 -> v4 direction
220     # Send a packet with a wrong IPv6 subnet as "2001:db8:f1"
221     # The BR should drop the packet.
222     #
223
224     def test_map_t_spoof_subnet_ip6_to_ip4(self):
225         """ MAP-T spoof subnet IPv6 -> IPv4 """
226
227         eth = Ether(src=self.pg1.remote_mac,
228                     dst=self.pg1.local_mac)
229         ip = IPv6(src=self.ipv6_spoof_subnet,
230                   dst=self.ipv6_map_address)
231         udp = UDP(sport=self.ipv6_udp_or_tcp_map_port,
232                   dport=self.ipv6_udp_or_tcp_internet_port)
233         payload = "a" * 82
234         tx_pkt = eth / ip / udp / payload
235
236         self.pg_send(self.pg1, tx_pkt * 1)
237
238         self.pg0.get_capture(0, timeout=1)
239         self.pg0.assert_nothing_captured("Should drop IPv6 spoof subnet")
240
241     #
242     # Spoofed IPv6 port PSID v6 -> v4 direction
243     # Send a packet with a wrong IPv6 port PSID
244     # The BR should drop the packet.
245     #
246
247     def test_map_t_spoof_port_psid_ip6_to_ip4(self):
248         """ MAP-T spoof port psid IPv6 -> IPv4 """
249
250         eth = Ether(src=self.pg1.remote_mac,
251                     dst=self.pg1.local_mac)
252         ip = IPv6(src=self.ipv6_cpe_address,
253                   dst=self.ipv6_map_address)
254         udp = UDP(sport=self.ipv6_udp_or_tcp_spoof_port,
255                   dport=self.ipv6_udp_or_tcp_internet_port)
256         payload = "a" * 82
257         tx_pkt = eth / ip / udp / payload
258
259         self.pg_send(self.pg1, tx_pkt * 1)
260
261         self.pg0.get_capture(0, timeout=1)
262         self.pg0.assert_nothing_captured("Should drop IPv6 spoof port PSID")
263
264 if __name__ == '__main__':
265     unittest.main(testRunner=VppTestRunner)