8 from framework import VppTestCase, running_extended_tests
9 from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder
10 from scapy.layers.inet import IP, TCP, UDP, ICMP
11 from scapy.layers.inet import IPerror, UDPerror
12 from scapy.layers.l2 import Ether
16 class TestDET44(VppTestCase):
17 """ Deterministic NAT Test Cases """
21 super(TestDET44, cls).setUpClass()
22 cls.vapi.cli("set log class det44 level debug")
24 cls.tcp_port_in = 6303
25 cls.tcp_external_port = 6303
26 cls.udp_port_in = 6304
27 cls.udp_external_port = 6304
29 cls.nat_addr = '10.0.0.3'
31 cls.create_pg_interfaces(range(3))
32 cls.interfaces = list(cls.pg_interfaces)
34 for i in cls.interfaces:
39 cls.pg0.generate_remote_hosts(2)
40 cls.pg0.configure_ipv4_neighbors()
43 def tearDownClass(cls):
44 super(TestDET44, cls).tearDownClass()
47 super(TestDET44, self).setUp()
48 self.vapi.det44_plugin_enable_disable(enable=1)
51 super(TestDET44, self).tearDown()
53 self.vapi.det44_plugin_enable_disable(enable=0)
55 def show_commands_at_teardown(self):
56 self.logger.info(self.vapi.cli("show det44 interfaces"))
57 self.logger.info(self.vapi.cli("show det44 timeouts"))
58 self.logger.info(self.vapi.cli("show det44 mappings"))
59 self.logger.info(self.vapi.cli("show det44 sessions"))
61 def verify_capture_in(self, capture, in_if):
63 Verify captured packets on inside network
65 :param capture: Captured packets
66 :param in_if: Inside interface
69 for packet in capture:
71 self.assert_packet_checksums_valid(packet)
72 self.assertEqual(packet[IP].dst, in_if.remote_ip4)
73 if packet.haslayer(TCP):
74 self.assertEqual(packet[TCP].dport, self.tcp_port_in)
75 elif packet.haslayer(UDP):
76 self.assertEqual(packet[UDP].dport, self.udp_port_in)
78 self.assertEqual(packet[ICMP].id, self.icmp_id_in)
81 self.logger.error(ppp("Unexpected or invalid packet "
82 "(inside network):", packet))
86 def verify_ipfix_max_entries_per_user(self, data, limit, src_addr):
88 Verify IPFIX maximum entries per user exceeded event
90 :param data: Decoded IPFIX data records
91 :param limit: Number of maximum entries per user
92 :param src_addr: IPv4 source address
94 self.assertEqual(1, len(data))
97 self.assertEqual(scapy.compat.orb(record[230]), 13)
98 # natQuotaExceededEvent
99 self.assertEqual(struct.pack("I", 3), record[466])
101 self.assertEqual(struct.pack("I", limit), record[473])
103 self.assertEqual(socket.inet_pton(socket.AF_INET, src_addr), record[8])
105 def initiate_tcp_session(self, in_if, out_if):
107 Initiates TCP session 3 WAY HAND SHAKE
109 :param in_if: Inside interface
110 :param out_if: Outside interface
114 p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) /
115 IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) /
116 TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
119 self.pg_enable_capture(self.pg_interfaces)
121 capture = out_if.get_capture(1)
123 self.tcp_port_out = p[TCP].sport
125 # SYN + ACK packet out->in
126 p = (Ether(src=out_if.remote_mac, dst=out_if.local_mac) /
127 IP(src=out_if.remote_ip4, dst=self.nat_addr) /
128 TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
131 self.pg_enable_capture(self.pg_interfaces)
136 p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) /
137 IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) /
138 TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
141 self.pg_enable_capture(self.pg_interfaces)
143 out_if.get_capture(1)
145 def create_stream_in(self, in_if, out_if, ttl=64):
147 Create packet stream for inside network
149 :param in_if: Inside interface
150 :param out_if: Outside interface
151 :param ttl: TTL of generated packets
155 p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
156 IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) /
157 TCP(sport=self.tcp_port_in, dport=self.tcp_external_port))
161 p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
162 IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) /
163 UDP(sport=self.udp_port_in, dport=self.udp_external_port))
167 p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
168 IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) /
169 ICMP(id=self.icmp_id_in, type='echo-request'))
174 def create_stream_out(self, out_if, dst_ip=None, ttl=64):
176 Create packet stream for outside network
178 :param out_if: Outside interface
179 :param dst_ip: Destination IP address (Default use global NAT address)
180 :param ttl: TTL of generated packets
183 dst_ip = self.nat_addr
186 p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
187 IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
188 TCP(dport=self.tcp_port_out, sport=self.tcp_external_port))
192 p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
193 IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
194 UDP(dport=self.udp_port_out, sport=self.udp_external_port))
198 p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
199 IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
200 ICMP(id=self.icmp_external_id, type='echo-reply'))
205 def verify_capture_out(self, capture, nat_ip=None):
207 Verify captured packets on outside network
209 :param capture: Captured packets
210 :param nat_ip: Translated IP address (Default use global NAT address)
211 :param same_port: Source port number is not translated (Default False)
214 nat_ip = self.nat_addr
215 for packet in capture:
217 self.assertEqual(packet[IP].src, nat_ip)
218 if packet.haslayer(TCP):
219 self.tcp_port_out = packet[TCP].sport
220 elif packet.haslayer(UDP):
221 self.udp_port_out = packet[UDP].sport
223 self.icmp_external_id = packet[ICMP].id
225 self.logger.error(ppp("Unexpected or invalid packet "
226 "(outside network):", packet))
229 def test_deterministic_mode(self):
230 """ NAT plugin run deterministic mode """
231 in_addr = '172.16.255.0'
232 out_addr = '172.17.255.50'
233 in_addr_t = '172.16.255.20'
237 self.vapi.det44_add_del_map(is_add=1, in_addr=in_addr,
238 in_plen=in_plen, out_addr=out_addr,
241 rep1 = self.vapi.det44_forward(in_addr_t)
242 self.assertEqual(str(rep1.out_addr), out_addr)
243 rep2 = self.vapi.det44_reverse(rep1.out_port_hi, out_addr)
245 self.assertEqual(str(rep2.in_addr), in_addr_t)
247 deterministic_mappings = self.vapi.det44_map_dump()
248 self.assertEqual(len(deterministic_mappings), 1)
249 dsm = deterministic_mappings[0]
250 self.assertEqual(in_addr, str(dsm.in_addr))
251 self.assertEqual(in_plen, dsm.in_plen)
252 self.assertEqual(out_addr, str(dsm.out_addr))
253 self.assertEqual(out_plen, dsm.out_plen)
255 def test_set_timeouts(self):
256 """ Set deterministic NAT timeouts """
257 timeouts_before = self.vapi.det44_get_timeouts()
259 self.vapi.det44_set_timeouts(
260 udp=timeouts_before.udp + 10,
261 tcp_established=timeouts_before.tcp_established + 10,
262 tcp_transitory=timeouts_before.tcp_transitory + 10,
263 icmp=timeouts_before.icmp + 10)
265 timeouts_after = self.vapi.det44_get_timeouts()
267 self.assertNotEqual(timeouts_before.udp, timeouts_after.udp)
268 self.assertNotEqual(timeouts_before.icmp, timeouts_after.icmp)
269 self.assertNotEqual(timeouts_before.tcp_established,
270 timeouts_after.tcp_established)
271 self.assertNotEqual(timeouts_before.tcp_transitory,
272 timeouts_after.tcp_transitory)
275 """ DET44 translation test (TCP, UDP, ICMP) """
279 self.vapi.det44_add_del_map(is_add=1, in_addr=self.pg0.remote_ip4,
281 out_addr=socket.inet_aton(nat_ip),
284 self.vapi.det44_interface_add_del_feature(
285 sw_if_index=self.pg0.sw_if_index,
286 is_add=1, is_inside=1)
287 self.vapi.det44_interface_add_del_feature(
288 sw_if_index=self.pg1.sw_if_index,
289 is_add=1, is_inside=0)
292 pkts = self.create_stream_in(self.pg0, self.pg1)
293 self.pg0.add_stream(pkts)
294 self.pg_enable_capture(self.pg_interfaces)
296 capture = self.pg1.get_capture(len(pkts))
297 self.verify_capture_out(capture, nat_ip)
300 pkts = self.create_stream_out(self.pg1, nat_ip)
301 self.pg1.add_stream(pkts)
302 self.pg_enable_capture(self.pg_interfaces)
304 capture = self.pg0.get_capture(len(pkts))
305 self.verify_capture_in(capture, self.pg0)
308 sessions = self.vapi.det44_session_dump(self.pg0.remote_ip4)
309 self.assertEqual(len(sessions), 3)
313 self.assertEqual(str(s.ext_addr), self.pg1.remote_ip4)
314 self.assertEqual(s.in_port, self.tcp_port_in)
315 self.assertEqual(s.out_port, self.tcp_port_out)
316 self.assertEqual(s.ext_port, self.tcp_external_port)
320 self.assertEqual(str(s.ext_addr), self.pg1.remote_ip4)
321 self.assertEqual(s.in_port, self.udp_port_in)
322 self.assertEqual(s.out_port, self.udp_port_out)
323 self.assertEqual(s.ext_port, self.udp_external_port)
327 self.assertEqual(str(s.ext_addr), self.pg1.remote_ip4)
328 self.assertEqual(s.in_port, self.icmp_id_in)
329 self.assertEqual(s.out_port, self.icmp_external_id)
331 def test_multiple_users(self):
332 """ Deterministic NAT multiple users """
338 host0 = self.pg0.remote_hosts[0]
339 host1 = self.pg0.remote_hosts[1]
341 self.vapi.det44_add_del_map(is_add=1, in_addr=host0.ip4, in_plen=24,
342 out_addr=socket.inet_aton(nat_ip),
344 self.vapi.det44_interface_add_del_feature(
345 sw_if_index=self.pg0.sw_if_index,
346 is_add=1, is_inside=1)
347 self.vapi.det44_interface_add_del_feature(
348 sw_if_index=self.pg1.sw_if_index,
349 is_add=1, is_inside=0)
352 p = (Ether(src=host0.mac, dst=self.pg0.local_mac) /
353 IP(src=host0.ip4, dst=self.pg1.remote_ip4) /
354 TCP(sport=port_in, dport=external_port))
355 self.pg0.add_stream(p)
356 self.pg_enable_capture(self.pg_interfaces)
358 capture = self.pg1.get_capture(1)
363 self.assertEqual(ip.src, nat_ip)
364 self.assertEqual(ip.dst, self.pg1.remote_ip4)
365 self.assertEqual(tcp.dport, external_port)
366 port_out0 = tcp.sport
368 self.logger.error(ppp("Unexpected or invalid packet:", p))
372 p = (Ether(src=host1.mac, dst=self.pg0.local_mac) /
373 IP(src=host1.ip4, dst=self.pg1.remote_ip4) /
374 TCP(sport=port_in, dport=external_port))
375 self.pg0.add_stream(p)
376 self.pg_enable_capture(self.pg_interfaces)
378 capture = self.pg1.get_capture(1)
383 self.assertEqual(ip.src, nat_ip)
384 self.assertEqual(ip.dst, self.pg1.remote_ip4)
385 self.assertEqual(tcp.dport, external_port)
386 port_out1 = tcp.sport
388 self.logger.error(ppp("Unexpected or invalid packet:", p))
391 dms = self.vapi.det44_map_dump()
392 self.assertEqual(1, len(dms))
393 self.assertEqual(2, dms[0].ses_num)
396 p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
397 IP(src=self.pg1.remote_ip4, dst=nat_ip) /
398 TCP(sport=external_port, dport=port_out0))
399 self.pg1.add_stream(p)
400 self.pg_enable_capture(self.pg_interfaces)
402 capture = self.pg0.get_capture(1)
407 self.assertEqual(ip.src, self.pg1.remote_ip4)
408 self.assertEqual(ip.dst, host0.ip4)
409 self.assertEqual(tcp.dport, port_in)
410 self.assertEqual(tcp.sport, external_port)
412 self.logger.error(ppp("Unexpected or invalid packet:", p))
416 p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
417 IP(src=self.pg1.remote_ip4, dst=nat_ip) /
418 TCP(sport=external_port, dport=port_out1))
419 self.pg1.add_stream(p)
420 self.pg_enable_capture(self.pg_interfaces)
422 capture = self.pg0.get_capture(1)
427 self.assertEqual(ip.src, self.pg1.remote_ip4)
428 self.assertEqual(ip.dst, host1.ip4)
429 self.assertEqual(tcp.dport, port_in)
430 self.assertEqual(tcp.sport, external_port)
432 self.logger.error(ppp("Unexpected or invalid packet", p))
435 # session close api test
436 self.vapi.det44_close_session_out(socket.inet_aton(nat_ip),
440 dms = self.vapi.det44_map_dump()
441 self.assertEqual(dms[0].ses_num, 1)
443 self.vapi.det44_close_session_in(host0.ip4,
447 dms = self.vapi.det44_map_dump()
448 self.assertEqual(dms[0].ses_num, 0)
450 def test_tcp_session_close_detection_in(self):
451 """ DET44 TCP session close from inside network """
452 self.vapi.det44_add_del_map(is_add=1, in_addr=self.pg0.remote_ip4,
454 out_addr=socket.inet_aton(self.nat_addr),
456 self.vapi.det44_interface_add_del_feature(
457 sw_if_index=self.pg0.sw_if_index,
458 is_add=1, is_inside=1)
459 self.vapi.det44_interface_add_del_feature(
460 sw_if_index=self.pg1.sw_if_index,
461 is_add=1, is_inside=0)
463 self.initiate_tcp_session(self.pg0, self.pg1)
465 # close the session from inside
467 # FIN packet in -> out
468 p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
469 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
470 TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
472 self.pg0.add_stream(p)
473 self.pg_enable_capture(self.pg_interfaces)
475 self.pg1.get_capture(1)
479 # ACK packet out -> in
480 p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
481 IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
482 TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
486 # FIN packet out -> in
487 p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
488 IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
489 TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
493 self.pg1.add_stream(pkts)
494 self.pg_enable_capture(self.pg_interfaces)
496 self.pg0.get_capture(2)
498 # ACK packet in -> out
499 p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
500 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
501 TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
503 self.pg0.add_stream(p)
504 self.pg_enable_capture(self.pg_interfaces)
506 self.pg1.get_capture(1)
508 # Check if deterministic NAT44 closed the session
509 dms = self.vapi.det44_map_dump()
510 self.assertEqual(0, dms[0].ses_num)
512 self.logger.error("TCP session termination failed")
515 def test_tcp_session_close_detection_out(self):
516 """ Deterministic NAT TCP session close from outside network """
517 self.vapi.det44_add_del_map(is_add=1, in_addr=self.pg0.remote_ip4,
519 out_addr=socket.inet_aton(self.nat_addr),
521 self.vapi.det44_interface_add_del_feature(
522 sw_if_index=self.pg0.sw_if_index,
523 is_add=1, is_inside=1)
524 self.vapi.det44_interface_add_del_feature(
525 sw_if_index=self.pg1.sw_if_index,
526 is_add=1, is_inside=0)
528 self.initiate_tcp_session(self.pg0, self.pg1)
530 # close the session from outside
532 # FIN packet out -> in
533 p = (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,
537 self.pg1.add_stream(p)
538 self.pg_enable_capture(self.pg_interfaces)
540 self.pg0.get_capture(1)
544 # ACK packet in -> out
545 p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
546 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
547 TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
551 # ACK packet in -> out
552 p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
553 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
554 TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
558 self.pg0.add_stream(pkts)
559 self.pg_enable_capture(self.pg_interfaces)
561 self.pg1.get_capture(2)
563 # ACK packet out -> in
564 p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
565 IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
566 TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
568 self.pg1.add_stream(p)
569 self.pg_enable_capture(self.pg_interfaces)
571 self.pg0.get_capture(1)
573 # Check if deterministic NAT44 closed the session
574 dms = self.vapi.det44_map_dump()
575 self.assertEqual(0, dms[0].ses_num)
577 self.logger.error("TCP session termination failed")
580 @unittest.skipUnless(running_extended_tests, "part of extended tests")
581 def test_session_timeout(self):
582 """ Deterministic NAT session timeouts """
583 self.vapi.det44_add_del_map(is_add=1, in_addr=self.pg0.remote_ip4,
585 out_addr=socket.inet_aton(self.nat_addr),
587 self.vapi.det44_interface_add_del_feature(
588 sw_if_index=self.pg0.sw_if_index,
589 is_add=1, is_inside=1)
590 self.vapi.det44_interface_add_del_feature(
591 sw_if_index=self.pg1.sw_if_index,
592 is_add=1, is_inside=0)
594 self.initiate_tcp_session(self.pg0, self.pg1)
595 self.vapi.det44_set_timeouts(udp=5, tcp_established=5,
596 tcp_transitory=5, icmp=5)
597 pkts = self.create_stream_in(self.pg0, self.pg1)
598 self.pg0.add_stream(pkts)
599 self.pg_enable_capture(self.pg_interfaces)
601 self.pg1.get_capture(len(pkts))
604 dms = self.vapi.det44_map_dump()
605 self.assertEqual(0, dms[0].ses_num)
607 # TODO: ipfix needs to be separated from NAT base plugin
608 @unittest.skipUnless(running_extended_tests, "part of extended tests")
609 def test_session_limit_per_user(self):
610 """ Deterministic NAT maximum sessions per user limit """
611 self.vapi.det44_add_del_map(is_add=1, in_addr=self.pg0.remote_ip4,
613 out_addr=socket.inet_aton(self.nat_addr),
615 self.vapi.det44_interface_add_del_feature(
616 sw_if_index=self.pg0.sw_if_index,
617 is_add=1, is_inside=1)
618 self.vapi.det44_interface_add_del_feature(
619 sw_if_index=self.pg1.sw_if_index,
620 is_add=1, is_inside=0)
621 self.vapi.set_ipfix_exporter(collector_address=self.pg2.remote_ip4,
622 src_address=self.pg2.local_ip4,
624 template_interval=10)
625 self.vapi.nat_ipfix_enable_disable(domain_id=1, src_port=4739,
629 for port in range(1025, 2025):
630 p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
631 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
632 UDP(sport=port, dport=port))
635 self.pg0.add_stream(pkts)
636 self.pg_enable_capture(self.pg_interfaces)
638 self.pg1.get_capture(len(pkts))
640 p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
641 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
642 UDP(sport=3001, dport=3002))
643 self.pg0.add_stream(p)
644 self.pg_enable_capture(self.pg_interfaces)
646 self.pg1.assert_nothing_captured()
648 # verify ICMP error packet
649 capture = self.pg0.get_capture(1)
651 self.assertTrue(p.haslayer(ICMP))
653 self.assertEqual(icmp.type, 3)
654 self.assertEqual(icmp.code, 1)
655 self.assertTrue(icmp.haslayer(IPerror))
656 inner_ip = icmp[IPerror]
657 self.assertEqual(inner_ip[UDPerror].sport, 3001)
658 self.assertEqual(inner_ip[UDPerror].dport, 3002)
660 dms = self.vapi.det44_map_dump()
662 self.assertEqual(1000, dms[0].ses_num)
664 # verify IPFIX logging
665 self.vapi.ipfix_flush()
667 capture = self.pg2.get_capture(2)
668 ipfix = IPFIXDecoder()
669 # first load template
671 self.assertTrue(p.haslayer(IPFIX))
672 if p.haslayer(Template):
673 ipfix.add_template(p.getlayer(Template))
674 # verify events in data set
677 data = ipfix.decode_data_set(p.getlayer(Set))
678 self.verify_ipfix_max_entries_per_user(data,
681 self.vapi.nat_ipfix_enable_disable(domain_id=1, src_port=4739,