blob: 11d33ef0acdbc4084d8a542f3f493e89d161761c [file] [log] [blame]
Filip Varga603e7542020-07-21 10:27:39 +02001#!/usr/bin/env python3
2
3import socket
4import struct
5import unittest
6import scapy.compat
Klement Sekerab23ffd72021-05-31 16:08:53 +02007from time import sleep
8from config import config
9from framework import VppTestCase
Filip Varga603e7542020-07-21 10:27:39 +020010from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder
11from scapy.layers.inet import IP, TCP, UDP, ICMP
12from scapy.layers.inet import IPerror, UDPerror
13from scapy.layers.l2 import Ether
14from util import ppp
Dmitry Valter34fa0ce2024-03-11 10:38:46 +000015from config import config
Filip Varga603e7542020-07-21 10:27:39 +020016
17
Dmitry Valter34fa0ce2024-03-11 10:38:46 +000018@unittest.skipIf("nat" in config.excluded_plugins, "Exclude NAT plugin tests")
Filip Varga603e7542020-07-21 10:27:39 +020019class TestDET44(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020020 """Deterministic NAT Test Cases"""
Filip Varga603e7542020-07-21 10:27:39 +020021
22 @classmethod
23 def setUpClass(cls):
24 super(TestDET44, cls).setUpClass()
25 cls.vapi.cli("set log class det44 level debug")
26
27 cls.tcp_port_in = 6303
28 cls.tcp_external_port = 6303
29 cls.udp_port_in = 6304
30 cls.udp_external_port = 6304
31 cls.icmp_id_in = 6305
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020032 cls.nat_addr = "10.0.0.3"
Filip Varga603e7542020-07-21 10:27:39 +020033
34 cls.create_pg_interfaces(range(3))
35 cls.interfaces = list(cls.pg_interfaces)
36
37 for i in cls.interfaces:
38 i.admin_up()
39 i.config_ip4()
40 i.resolve_arp()
41
42 cls.pg0.generate_remote_hosts(2)
43 cls.pg0.configure_ipv4_neighbors()
44
45 @classmethod
46 def tearDownClass(cls):
47 super(TestDET44, cls).tearDownClass()
48
49 def setUp(self):
50 super(TestDET44, self).setUp()
51 self.vapi.det44_plugin_enable_disable(enable=1)
52
53 def tearDown(self):
54 super(TestDET44, self).tearDown()
55 if not self.vpp_dead:
56 self.vapi.det44_plugin_enable_disable(enable=0)
57
58 def show_commands_at_teardown(self):
59 self.logger.info(self.vapi.cli("show det44 interfaces"))
60 self.logger.info(self.vapi.cli("show det44 timeouts"))
61 self.logger.info(self.vapi.cli("show det44 mappings"))
62 self.logger.info(self.vapi.cli("show det44 sessions"))
63
64 def verify_capture_in(self, capture, in_if):
65 """
66 Verify captured packets on inside network
67
68 :param capture: Captured packets
69 :param in_if: Inside interface
70 """
71 fired = False
72 for packet in capture:
73 try:
74 self.assert_packet_checksums_valid(packet)
75 self.assertEqual(packet[IP].dst, in_if.remote_ip4)
76 if packet.haslayer(TCP):
77 self.assertEqual(packet[TCP].dport, self.tcp_port_in)
78 elif packet.haslayer(UDP):
79 self.assertEqual(packet[UDP].dport, self.udp_port_in)
80 else:
81 self.assertEqual(packet[ICMP].id, self.icmp_id_in)
82 except:
83 fired = True
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020084 self.logger.error(
85 ppp("Unexpected or invalid packet (inside network):", packet)
86 )
Filip Varga603e7542020-07-21 10:27:39 +020087 if fired:
88 raise
89
90 def verify_ipfix_max_entries_per_user(self, data, limit, src_addr):
91 """
92 Verify IPFIX maximum entries per user exceeded event
93
94 :param data: Decoded IPFIX data records
95 :param limit: Number of maximum entries per user
96 :param src_addr: IPv4 source address
97 """
98 self.assertEqual(1, len(data))
99 record = data[0]
100 # natEvent
101 self.assertEqual(scapy.compat.orb(record[230]), 13)
102 # natQuotaExceededEvent
Vladislav Grishenkoda34f4a2023-09-14 22:14:38 +0500103 self.assertEqual(struct.pack("!I", 3), record[466])
Filip Varga603e7542020-07-21 10:27:39 +0200104 # maxEntriesPerUser
Vladislav Grishenkoda34f4a2023-09-14 22:14:38 +0500105 self.assertEqual(struct.pack("!I", limit), record[473])
Filip Varga603e7542020-07-21 10:27:39 +0200106 # sourceIPv4Address
107 self.assertEqual(socket.inet_pton(socket.AF_INET, src_addr), record[8])
108
109 def initiate_tcp_session(self, in_if, out_if):
110 """
111 Initiates TCP session 3 WAY HAND SHAKE
112
113 :param in_if: Inside interface
114 :param out_if: Outside interface
115 """
116
117 # SYN packet in->out
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200118 p = (
119 Ether(src=in_if.remote_mac, dst=in_if.local_mac)
120 / IP(src=in_if.remote_ip4, dst=out_if.remote_ip4)
121 / TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, flags="S")
122 )
Filip Varga603e7542020-07-21 10:27:39 +0200123 in_if.add_stream(p)
124 self.pg_enable_capture(self.pg_interfaces)
125 self.pg_start()
126 capture = out_if.get_capture(1)
127 p = capture[0]
128 self.tcp_port_out = p[TCP].sport
129
130 # SYN + ACK packet out->in
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200131 p = (
132 Ether(src=out_if.remote_mac, dst=out_if.local_mac)
133 / IP(src=out_if.remote_ip4, dst=self.nat_addr)
134 / TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, flags="SA")
135 )
Filip Varga603e7542020-07-21 10:27:39 +0200136 out_if.add_stream(p)
137 self.pg_enable_capture(self.pg_interfaces)
138 self.pg_start()
139 in_if.get_capture(1)
140
141 # ACK packet in->out
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200142 p = (
143 Ether(src=in_if.remote_mac, dst=in_if.local_mac)
144 / IP(src=in_if.remote_ip4, dst=out_if.remote_ip4)
145 / TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, flags="A")
146 )
Filip Varga603e7542020-07-21 10:27:39 +0200147 in_if.add_stream(p)
148 self.pg_enable_capture(self.pg_interfaces)
149 self.pg_start()
150 out_if.get_capture(1)
151
152 def create_stream_in(self, in_if, out_if, ttl=64):
153 """
154 Create packet stream for inside network
155
156 :param in_if: Inside interface
157 :param out_if: Outside interface
158 :param ttl: TTL of generated packets
159 """
160 pkts = []
161 # TCP
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200162 p = (
163 Ether(dst=in_if.local_mac, src=in_if.remote_mac)
164 / IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl)
165 / TCP(sport=self.tcp_port_in, dport=self.tcp_external_port)
166 )
Filip Varga603e7542020-07-21 10:27:39 +0200167 pkts.append(p)
168
169 # UDP
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200170 p = (
171 Ether(dst=in_if.local_mac, src=in_if.remote_mac)
172 / IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl)
173 / UDP(sport=self.udp_port_in, dport=self.udp_external_port)
174 )
Filip Varga603e7542020-07-21 10:27:39 +0200175 pkts.append(p)
176
177 # ICMP
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200178 p = (
179 Ether(dst=in_if.local_mac, src=in_if.remote_mac)
180 / IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl)
181 / ICMP(id=self.icmp_id_in, type="echo-request")
182 )
Filip Varga603e7542020-07-21 10:27:39 +0200183 pkts.append(p)
184
185 return pkts
186
187 def create_stream_out(self, out_if, dst_ip=None, ttl=64):
188 """
189 Create packet stream for outside network
190
191 :param out_if: Outside interface
192 :param dst_ip: Destination IP address (Default use global NAT address)
193 :param ttl: TTL of generated packets
194 """
195 if dst_ip is None:
196 dst_ip = self.nat_addr
197 pkts = []
198 # TCP
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200199 p = (
200 Ether(dst=out_if.local_mac, src=out_if.remote_mac)
201 / IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl)
202 / TCP(dport=self.tcp_port_out, sport=self.tcp_external_port)
203 )
Filip Varga603e7542020-07-21 10:27:39 +0200204 pkts.append(p)
205
206 # UDP
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200207 p = (
208 Ether(dst=out_if.local_mac, src=out_if.remote_mac)
209 / IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl)
210 / UDP(dport=self.udp_port_out, sport=self.udp_external_port)
211 )
Filip Varga603e7542020-07-21 10:27:39 +0200212 pkts.append(p)
213
214 # ICMP
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200215 p = (
216 Ether(dst=out_if.local_mac, src=out_if.remote_mac)
217 / IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl)
218 / ICMP(id=self.icmp_external_id, type="echo-reply")
219 )
Filip Varga603e7542020-07-21 10:27:39 +0200220 pkts.append(p)
221
222 return pkts
223
224 def verify_capture_out(self, capture, nat_ip=None):
225 """
226 Verify captured packets on outside network
227
228 :param capture: Captured packets
229 :param nat_ip: Translated IP address (Default use global NAT address)
230 :param same_port: Source port number is not translated (Default False)
231 """
232 if nat_ip is None:
233 nat_ip = self.nat_addr
234 for packet in capture:
235 try:
236 self.assertEqual(packet[IP].src, nat_ip)
237 if packet.haslayer(TCP):
238 self.tcp_port_out = packet[TCP].sport
239 elif packet.haslayer(UDP):
240 self.udp_port_out = packet[UDP].sport
241 else:
242 self.icmp_external_id = packet[ICMP].id
243 except:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200244 self.logger.error(
245 ppp("Unexpected or invalid packet (outside network):", packet)
246 )
Filip Varga603e7542020-07-21 10:27:39 +0200247 raise
248
249 def test_deterministic_mode(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200250 """NAT plugin run deterministic mode"""
251 in_addr = "172.16.255.0"
252 out_addr = "172.17.255.50"
253 in_addr_t = "172.16.255.20"
Filip Varga603e7542020-07-21 10:27:39 +0200254 in_plen = 24
255 out_plen = 32
256
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200257 self.vapi.det44_add_del_map(
258 is_add=1,
259 in_addr=in_addr,
260 in_plen=in_plen,
261 out_addr=out_addr,
262 out_plen=out_plen,
263 )
Filip Varga603e7542020-07-21 10:27:39 +0200264
265 rep1 = self.vapi.det44_forward(in_addr_t)
266 self.assertEqual(str(rep1.out_addr), out_addr)
267 rep2 = self.vapi.det44_reverse(rep1.out_port_hi, out_addr)
268
269 self.assertEqual(str(rep2.in_addr), in_addr_t)
270
271 deterministic_mappings = self.vapi.det44_map_dump()
272 self.assertEqual(len(deterministic_mappings), 1)
273 dsm = deterministic_mappings[0]
274 self.assertEqual(in_addr, str(dsm.in_addr))
275 self.assertEqual(in_plen, dsm.in_plen)
276 self.assertEqual(out_addr, str(dsm.out_addr))
277 self.assertEqual(out_plen, dsm.out_plen)
278
279 def test_set_timeouts(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200280 """Set deterministic NAT timeouts"""
Filip Varga603e7542020-07-21 10:27:39 +0200281 timeouts_before = self.vapi.det44_get_timeouts()
282
283 self.vapi.det44_set_timeouts(
284 udp=timeouts_before.udp + 10,
285 tcp_established=timeouts_before.tcp_established + 10,
286 tcp_transitory=timeouts_before.tcp_transitory + 10,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200287 icmp=timeouts_before.icmp + 10,
288 )
Filip Varga603e7542020-07-21 10:27:39 +0200289
290 timeouts_after = self.vapi.det44_get_timeouts()
291
292 self.assertNotEqual(timeouts_before.udp, timeouts_after.udp)
293 self.assertNotEqual(timeouts_before.icmp, timeouts_after.icmp)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200294 self.assertNotEqual(
295 timeouts_before.tcp_established, timeouts_after.tcp_established
296 )
297 self.assertNotEqual(
298 timeouts_before.tcp_transitory, timeouts_after.tcp_transitory
299 )
Filip Varga603e7542020-07-21 10:27:39 +0200300
301 def test_in(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200302 """DET44 translation test (TCP, UDP, ICMP)"""
Filip Varga603e7542020-07-21 10:27:39 +0200303
304 nat_ip = "10.0.0.10"
305
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200306 self.vapi.det44_add_del_map(
307 is_add=1,
308 in_addr=self.pg0.remote_ip4,
309 in_plen=32,
310 out_addr=socket.inet_aton(nat_ip),
311 out_plen=32,
312 )
Filip Varga603e7542020-07-21 10:27:39 +0200313
314 self.vapi.det44_interface_add_del_feature(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200315 sw_if_index=self.pg0.sw_if_index, is_add=1, is_inside=1
316 )
Filip Varga603e7542020-07-21 10:27:39 +0200317 self.vapi.det44_interface_add_del_feature(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200318 sw_if_index=self.pg1.sw_if_index, is_add=1, is_inside=0
319 )
Filip Varga603e7542020-07-21 10:27:39 +0200320
321 # in2out
322 pkts = self.create_stream_in(self.pg0, self.pg1)
323 self.pg0.add_stream(pkts)
324 self.pg_enable_capture(self.pg_interfaces)
325 self.pg_start()
326 capture = self.pg1.get_capture(len(pkts))
327 self.verify_capture_out(capture, nat_ip)
328
329 # out2in
330 pkts = self.create_stream_out(self.pg1, nat_ip)
331 self.pg1.add_stream(pkts)
332 self.pg_enable_capture(self.pg_interfaces)
333 self.pg_start()
334 capture = self.pg0.get_capture(len(pkts))
335 self.verify_capture_in(capture, self.pg0)
336
337 # session dump test
338 sessions = self.vapi.det44_session_dump(self.pg0.remote_ip4)
339 self.assertEqual(len(sessions), 3)
340
341 # TCP session
342 s = sessions[0]
343 self.assertEqual(str(s.ext_addr), self.pg1.remote_ip4)
344 self.assertEqual(s.in_port, self.tcp_port_in)
345 self.assertEqual(s.out_port, self.tcp_port_out)
346 self.assertEqual(s.ext_port, self.tcp_external_port)
347
348 # UDP session
349 s = sessions[1]
350 self.assertEqual(str(s.ext_addr), self.pg1.remote_ip4)
351 self.assertEqual(s.in_port, self.udp_port_in)
352 self.assertEqual(s.out_port, self.udp_port_out)
353 self.assertEqual(s.ext_port, self.udp_external_port)
354
355 # ICMP session
356 s = sessions[2]
357 self.assertEqual(str(s.ext_addr), self.pg1.remote_ip4)
358 self.assertEqual(s.in_port, self.icmp_id_in)
359 self.assertEqual(s.out_port, self.icmp_external_id)
360
361 def test_multiple_users(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200362 """Deterministic NAT multiple users"""
Filip Varga603e7542020-07-21 10:27:39 +0200363
364 nat_ip = "10.0.0.10"
365 port_in = 80
366 external_port = 6303
367
368 host0 = self.pg0.remote_hosts[0]
369 host1 = self.pg0.remote_hosts[1]
370
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200371 self.vapi.det44_add_del_map(
372 is_add=1,
373 in_addr=host0.ip4,
374 in_plen=24,
375 out_addr=socket.inet_aton(nat_ip),
376 out_plen=32,
377 )
Filip Varga603e7542020-07-21 10:27:39 +0200378 self.vapi.det44_interface_add_del_feature(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200379 sw_if_index=self.pg0.sw_if_index, is_add=1, is_inside=1
380 )
Filip Varga603e7542020-07-21 10:27:39 +0200381 self.vapi.det44_interface_add_del_feature(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200382 sw_if_index=self.pg1.sw_if_index, is_add=1, is_inside=0
383 )
Filip Varga603e7542020-07-21 10:27:39 +0200384
385 # host0 to out
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200386 p = (
387 Ether(src=host0.mac, dst=self.pg0.local_mac)
388 / IP(src=host0.ip4, dst=self.pg1.remote_ip4)
389 / TCP(sport=port_in, dport=external_port)
390 )
Filip Varga603e7542020-07-21 10:27:39 +0200391 self.pg0.add_stream(p)
392 self.pg_enable_capture(self.pg_interfaces)
393 self.pg_start()
394 capture = self.pg1.get_capture(1)
395 p = capture[0]
396 try:
397 ip = p[IP]
398 tcp = p[TCP]
399 self.assertEqual(ip.src, nat_ip)
400 self.assertEqual(ip.dst, self.pg1.remote_ip4)
401 self.assertEqual(tcp.dport, external_port)
402 port_out0 = tcp.sport
403 except:
404 self.logger.error(ppp("Unexpected or invalid packet:", p))
405 raise
406
407 # host1 to out
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200408 p = (
409 Ether(src=host1.mac, dst=self.pg0.local_mac)
410 / IP(src=host1.ip4, dst=self.pg1.remote_ip4)
411 / TCP(sport=port_in, dport=external_port)
412 )
Filip Varga603e7542020-07-21 10:27:39 +0200413 self.pg0.add_stream(p)
414 self.pg_enable_capture(self.pg_interfaces)
415 self.pg_start()
416 capture = self.pg1.get_capture(1)
417 p = capture[0]
418 try:
419 ip = p[IP]
420 tcp = p[TCP]
421 self.assertEqual(ip.src, nat_ip)
422 self.assertEqual(ip.dst, self.pg1.remote_ip4)
423 self.assertEqual(tcp.dport, external_port)
424 port_out1 = tcp.sport
425 except:
426 self.logger.error(ppp("Unexpected or invalid packet:", p))
427 raise
428
429 dms = self.vapi.det44_map_dump()
430 self.assertEqual(1, len(dms))
431 self.assertEqual(2, dms[0].ses_num)
432
433 # out to host0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200434 p = (
435 Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
436 / IP(src=self.pg1.remote_ip4, dst=nat_ip)
437 / TCP(sport=external_port, dport=port_out0)
438 )
Filip Varga603e7542020-07-21 10:27:39 +0200439 self.pg1.add_stream(p)
440 self.pg_enable_capture(self.pg_interfaces)
441 self.pg_start()
442 capture = self.pg0.get_capture(1)
443 p = capture[0]
444 try:
445 ip = p[IP]
446 tcp = p[TCP]
447 self.assertEqual(ip.src, self.pg1.remote_ip4)
448 self.assertEqual(ip.dst, host0.ip4)
449 self.assertEqual(tcp.dport, port_in)
450 self.assertEqual(tcp.sport, external_port)
451 except:
452 self.logger.error(ppp("Unexpected or invalid packet:", p))
453 raise
454
455 # out to host1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200456 p = (
457 Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
458 / IP(src=self.pg1.remote_ip4, dst=nat_ip)
459 / TCP(sport=external_port, dport=port_out1)
460 )
Filip Varga603e7542020-07-21 10:27:39 +0200461 self.pg1.add_stream(p)
462 self.pg_enable_capture(self.pg_interfaces)
463 self.pg_start()
464 capture = self.pg0.get_capture(1)
465 p = capture[0]
466 try:
467 ip = p[IP]
468 tcp = p[TCP]
469 self.assertEqual(ip.src, self.pg1.remote_ip4)
470 self.assertEqual(ip.dst, host1.ip4)
471 self.assertEqual(tcp.dport, port_in)
472 self.assertEqual(tcp.sport, external_port)
473 except:
474 self.logger.error(ppp("Unexpected or invalid packet", p))
475 raise
476
477 # session close api test
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200478 self.vapi.det44_close_session_out(
479 socket.inet_aton(nat_ip), port_out1, self.pg1.remote_ip4, external_port
480 )
Filip Varga603e7542020-07-21 10:27:39 +0200481 dms = self.vapi.det44_map_dump()
482 self.assertEqual(dms[0].ses_num, 1)
483
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200484 self.vapi.det44_close_session_in(
485 host0.ip4, port_in, self.pg1.remote_ip4, external_port
486 )
Filip Varga603e7542020-07-21 10:27:39 +0200487 dms = self.vapi.det44_map_dump()
488 self.assertEqual(dms[0].ses_num, 0)
489
490 def test_tcp_session_close_detection_in(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200491 """DET44 TCP session close from inside network"""
492 self.vapi.det44_add_del_map(
493 is_add=1,
494 in_addr=self.pg0.remote_ip4,
495 in_plen=32,
496 out_addr=socket.inet_aton(self.nat_addr),
497 out_plen=32,
498 )
Filip Varga603e7542020-07-21 10:27:39 +0200499 self.vapi.det44_interface_add_del_feature(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200500 sw_if_index=self.pg0.sw_if_index, is_add=1, is_inside=1
501 )
Filip Varga603e7542020-07-21 10:27:39 +0200502 self.vapi.det44_interface_add_del_feature(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200503 sw_if_index=self.pg1.sw_if_index, is_add=1, is_inside=0
504 )
Filip Varga603e7542020-07-21 10:27:39 +0200505
506 self.initiate_tcp_session(self.pg0, self.pg1)
507
508 # close the session from inside
509 try:
510 # FIN packet in -> out
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200511 p = (
512 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
513 / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4)
514 / TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, flags="F")
515 )
Filip Varga603e7542020-07-21 10:27:39 +0200516 self.pg0.add_stream(p)
517 self.pg_enable_capture(self.pg_interfaces)
518 self.pg_start()
519 self.pg1.get_capture(1)
520
521 pkts = []
522
523 # ACK packet out -> in
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200524 p = (
525 Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
526 / IP(src=self.pg1.remote_ip4, dst=self.nat_addr)
527 / TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, flags="A")
528 )
Filip Varga603e7542020-07-21 10:27:39 +0200529 pkts.append(p)
530
531 # FIN packet out -> in
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200532 p = (
533 Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
534 / IP(src=self.pg1.remote_ip4, dst=self.nat_addr)
535 / TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, flags="F")
536 )
Filip Varga603e7542020-07-21 10:27:39 +0200537 pkts.append(p)
538
539 self.pg1.add_stream(pkts)
540 self.pg_enable_capture(self.pg_interfaces)
541 self.pg_start()
542 self.pg0.get_capture(2)
543
544 # ACK packet in -> out
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200545 p = (
546 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
547 / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4)
548 / TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, flags="A")
549 )
Filip Varga603e7542020-07-21 10:27:39 +0200550 self.pg0.add_stream(p)
551 self.pg_enable_capture(self.pg_interfaces)
552 self.pg_start()
553 self.pg1.get_capture(1)
554
555 # Check if deterministic NAT44 closed the session
556 dms = self.vapi.det44_map_dump()
557 self.assertEqual(0, dms[0].ses_num)
558 except:
559 self.logger.error("TCP session termination failed")
560 raise
561
562 def test_tcp_session_close_detection_out(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200563 """Deterministic NAT TCP session close from outside network"""
564 self.vapi.det44_add_del_map(
565 is_add=1,
566 in_addr=self.pg0.remote_ip4,
567 in_plen=32,
568 out_addr=socket.inet_aton(self.nat_addr),
569 out_plen=32,
570 )
Filip Varga603e7542020-07-21 10:27:39 +0200571 self.vapi.det44_interface_add_del_feature(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200572 sw_if_index=self.pg0.sw_if_index, is_add=1, is_inside=1
573 )
Filip Varga603e7542020-07-21 10:27:39 +0200574 self.vapi.det44_interface_add_del_feature(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200575 sw_if_index=self.pg1.sw_if_index, is_add=1, is_inside=0
576 )
Filip Varga603e7542020-07-21 10:27:39 +0200577
578 self.initiate_tcp_session(self.pg0, self.pg1)
579
580 # close the session from outside
581 try:
582 # FIN packet out -> in
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200583 p = (
584 Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
585 / IP(src=self.pg1.remote_ip4, dst=self.nat_addr)
586 / TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, flags="F")
587 )
Filip Varga603e7542020-07-21 10:27:39 +0200588 self.pg1.add_stream(p)
589 self.pg_enable_capture(self.pg_interfaces)
590 self.pg_start()
591 self.pg0.get_capture(1)
592
593 pkts = []
594
595 # ACK packet in -> out
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200596 p = (
597 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
598 / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4)
599 / TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, flags="A")
600 )
Filip Varga603e7542020-07-21 10:27:39 +0200601 pkts.append(p)
602
603 # ACK packet in -> out
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200604 p = (
605 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
606 / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4)
607 / TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, flags="F")
608 )
Filip Varga603e7542020-07-21 10:27:39 +0200609 pkts.append(p)
610
611 self.pg0.add_stream(pkts)
612 self.pg_enable_capture(self.pg_interfaces)
613 self.pg_start()
614 self.pg1.get_capture(2)
615
616 # ACK packet out -> in
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200617 p = (
618 Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
619 / IP(src=self.pg1.remote_ip4, dst=self.nat_addr)
620 / TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, flags="A")
621 )
Filip Varga603e7542020-07-21 10:27:39 +0200622 self.pg1.add_stream(p)
623 self.pg_enable_capture(self.pg_interfaces)
624 self.pg_start()
625 self.pg0.get_capture(1)
626
627 # Check if deterministic NAT44 closed the session
628 dms = self.vapi.det44_map_dump()
629 self.assertEqual(0, dms[0].ses_num)
630 except:
631 self.logger.error("TCP session termination failed")
632 raise
633
Filip Varga603e7542020-07-21 10:27:39 +0200634 def test_session_timeout(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200635 """Deterministic NAT session timeouts"""
636 self.vapi.det44_add_del_map(
637 is_add=1,
638 in_addr=self.pg0.remote_ip4,
639 in_plen=32,
640 out_addr=socket.inet_aton(self.nat_addr),
641 out_plen=32,
642 )
Filip Varga603e7542020-07-21 10:27:39 +0200643 self.vapi.det44_interface_add_del_feature(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200644 sw_if_index=self.pg0.sw_if_index, is_add=1, is_inside=1
645 )
Filip Varga603e7542020-07-21 10:27:39 +0200646 self.vapi.det44_interface_add_del_feature(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200647 sw_if_index=self.pg1.sw_if_index, is_add=1, is_inside=0
648 )
Filip Varga603e7542020-07-21 10:27:39 +0200649
650 self.initiate_tcp_session(self.pg0, self.pg1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200651 self.vapi.det44_set_timeouts(udp=5, tcp_established=5, tcp_transitory=5, icmp=5)
Filip Varga603e7542020-07-21 10:27:39 +0200652 pkts = self.create_stream_in(self.pg0, self.pg1)
653 self.pg0.add_stream(pkts)
654 self.pg_enable_capture(self.pg_interfaces)
655 self.pg_start()
656 self.pg1.get_capture(len(pkts))
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +0200657 self.virtual_sleep(15)
Filip Varga603e7542020-07-21 10:27:39 +0200658
659 dms = self.vapi.det44_map_dump()
660 self.assertEqual(0, dms[0].ses_num)
661
662 # TODO: ipfix needs to be separated from NAT base plugin
Klement Sekerab23ffd72021-05-31 16:08:53 +0200663 @unittest.skipUnless(config.extended, "part of extended tests")
Filip Varga603e7542020-07-21 10:27:39 +0200664 def test_session_limit_per_user(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200665 """Deterministic NAT maximum sessions per user limit"""
666 self.vapi.det44_add_del_map(
667 is_add=1,
668 in_addr=self.pg0.remote_ip4,
669 in_plen=32,
670 out_addr=socket.inet_aton(self.nat_addr),
671 out_plen=32,
672 )
Filip Varga603e7542020-07-21 10:27:39 +0200673 self.vapi.det44_interface_add_del_feature(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200674 sw_if_index=self.pg0.sw_if_index, is_add=1, is_inside=1
675 )
Filip Varga603e7542020-07-21 10:27:39 +0200676 self.vapi.det44_interface_add_del_feature(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200677 sw_if_index=self.pg1.sw_if_index, is_add=1, is_inside=0
678 )
679 self.vapi.set_ipfix_exporter(
680 collector_address=self.pg2.remote_ip4,
681 src_address=self.pg2.local_ip4,
682 path_mtu=512,
683 template_interval=10,
684 )
685 self.vapi.nat_ipfix_enable_disable(domain_id=1, src_port=4739, enable=1)
Filip Varga603e7542020-07-21 10:27:39 +0200686
687 pkts = []
688 for port in range(1025, 2025):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200689 p = (
690 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
691 / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4)
692 / UDP(sport=port, dport=port)
693 )
Filip Varga603e7542020-07-21 10:27:39 +0200694 pkts.append(p)
695
696 self.pg0.add_stream(pkts)
697 self.pg_enable_capture(self.pg_interfaces)
698 self.pg_start()
699 self.pg1.get_capture(len(pkts))
700
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200701 p = (
702 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
703 / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4)
704 / UDP(sport=3001, dport=3002)
705 )
Filip Varga603e7542020-07-21 10:27:39 +0200706 self.pg0.add_stream(p)
707 self.pg_enable_capture(self.pg_interfaces)
708 self.pg_start()
709 self.pg1.assert_nothing_captured()
710
711 # verify ICMP error packet
712 capture = self.pg0.get_capture(1)
713 p = capture[0]
714 self.assertTrue(p.haslayer(ICMP))
715 icmp = p[ICMP]
716 self.assertEqual(icmp.type, 3)
717 self.assertEqual(icmp.code, 1)
718 self.assertTrue(icmp.haslayer(IPerror))
719 inner_ip = icmp[IPerror]
720 self.assertEqual(inner_ip[UDPerror].sport, 3001)
721 self.assertEqual(inner_ip[UDPerror].dport, 3002)
722
723 dms = self.vapi.det44_map_dump()
724
725 self.assertEqual(1000, dms[0].ses_num)
726
727 # verify IPFIX logging
728 self.vapi.ipfix_flush()
Vladislav Grishenkoda34f4a2023-09-14 22:14:38 +0500729 capture = self.pg2.get_capture(7)
Filip Varga603e7542020-07-21 10:27:39 +0200730 ipfix = IPFIXDecoder()
731 # first load template
732 for p in capture:
733 self.assertTrue(p.haslayer(IPFIX))
734 if p.haslayer(Template):
735 ipfix.add_template(p.getlayer(Template))
736 # verify events in data set
737 for p in capture:
738 if p.haslayer(Data):
739 data = ipfix.decode_data_set(p.getlayer(Set))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200740 self.verify_ipfix_max_entries_per_user(data, 1000, self.pg0.remote_ip4)
741 self.vapi.nat_ipfix_enable_disable(domain_id=1, src_port=4739, enable=0)