8 from config import config
9 from framework import VppTestCase
10 from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder
11 from scapy.layers.inet import IP, TCP, UDP, ICMP
12 from scapy.layers.inet import IPerror, UDPerror
13 from scapy.layers.l2 import Ether
17 class TestDET44(VppTestCase):
18 """ Deterministic NAT Test Cases """
22 super(TestDET44, cls).setUpClass()
23 cls.vapi.cli("set log class det44 level debug")
25 cls.tcp_port_in = 6303
26 cls.tcp_external_port = 6303
27 cls.udp_port_in = 6304
28 cls.udp_external_port = 6304
30 cls.nat_addr = '10.0.0.3'
32 cls.create_pg_interfaces(range(3))
33 cls.interfaces = list(cls.pg_interfaces)
35 for i in cls.interfaces:
40 cls.pg0.generate_remote_hosts(2)
41 cls.pg0.configure_ipv4_neighbors()
44 def tearDownClass(cls):
45 super(TestDET44, cls).tearDownClass()
48 super(TestDET44, self).setUp()
49 self.vapi.det44_plugin_enable_disable(enable=1)
52 super(TestDET44, self).tearDown()
54 self.vapi.det44_plugin_enable_disable(enable=0)
56 def show_commands_at_teardown(self):
57 self.logger.info(self.vapi.cli("show det44 interfaces"))
58 self.logger.info(self.vapi.cli("show det44 timeouts"))
59 self.logger.info(self.vapi.cli("show det44 mappings"))
60 self.logger.info(self.vapi.cli("show det44 sessions"))
62 def verify_capture_in(self, capture, in_if):
64 Verify captured packets on inside network
66 :param capture: Captured packets
67 :param in_if: Inside interface
70 for packet in capture:
72 self.assert_packet_checksums_valid(packet)
73 self.assertEqual(packet[IP].dst, in_if.remote_ip4)
74 if packet.haslayer(TCP):
75 self.assertEqual(packet[TCP].dport, self.tcp_port_in)
76 elif packet.haslayer(UDP):
77 self.assertEqual(packet[UDP].dport, self.udp_port_in)
79 self.assertEqual(packet[ICMP].id, self.icmp_id_in)
82 self.logger.error(ppp("Unexpected or invalid packet "
83 "(inside network):", packet))
87 def verify_ipfix_max_entries_per_user(self, data, limit, src_addr):
89 Verify IPFIX maximum entries per user exceeded event
91 :param data: Decoded IPFIX data records
92 :param limit: Number of maximum entries per user
93 :param src_addr: IPv4 source address
95 self.assertEqual(1, len(data))
98 self.assertEqual(scapy.compat.orb(record[230]), 13)
99 # natQuotaExceededEvent
100 self.assertEqual(struct.pack("I", 3), record[466])
102 self.assertEqual(struct.pack("I", limit), record[473])
104 self.assertEqual(socket.inet_pton(socket.AF_INET, src_addr), record[8])
106 def initiate_tcp_session(self, in_if, out_if):
108 Initiates TCP session 3 WAY HAND SHAKE
110 :param in_if: Inside interface
111 :param out_if: Outside interface
115 p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) /
116 IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) /
117 TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
120 self.pg_enable_capture(self.pg_interfaces)
122 capture = out_if.get_capture(1)
124 self.tcp_port_out = p[TCP].sport
126 # SYN + ACK packet out->in
127 p = (Ether(src=out_if.remote_mac, dst=out_if.local_mac) /
128 IP(src=out_if.remote_ip4, dst=self.nat_addr) /
129 TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
132 self.pg_enable_capture(self.pg_interfaces)
137 p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) /
138 IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) /
139 TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
142 self.pg_enable_capture(self.pg_interfaces)
144 out_if.get_capture(1)
146 def create_stream_in(self, in_if, out_if, ttl=64):
148 Create packet stream for inside network
150 :param in_if: Inside interface
151 :param out_if: Outside interface
152 :param ttl: TTL of generated packets
156 p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
157 IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) /
158 TCP(sport=self.tcp_port_in, dport=self.tcp_external_port))
162 p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
163 IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) /
164 UDP(sport=self.udp_port_in, dport=self.udp_external_port))
168 p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
169 IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) /
170 ICMP(id=self.icmp_id_in, type='echo-request'))
175 def create_stream_out(self, out_if, dst_ip=None, ttl=64):
177 Create packet stream for outside network
179 :param out_if: Outside interface
180 :param dst_ip: Destination IP address (Default use global NAT address)
181 :param ttl: TTL of generated packets
184 dst_ip = self.nat_addr
187 p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
188 IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
189 TCP(dport=self.tcp_port_out, sport=self.tcp_external_port))
193 p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
194 IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
195 UDP(dport=self.udp_port_out, sport=self.udp_external_port))
199 p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
200 IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
201 ICMP(id=self.icmp_external_id, type='echo-reply'))
206 def verify_capture_out(self, capture, nat_ip=None):
208 Verify captured packets on outside network
210 :param capture: Captured packets
211 :param nat_ip: Translated IP address (Default use global NAT address)
212 :param same_port: Source port number is not translated (Default False)
215 nat_ip = self.nat_addr
216 for packet in capture:
218 self.assertEqual(packet[IP].src, nat_ip)
219 if packet.haslayer(TCP):
220 self.tcp_port_out = packet[TCP].sport
221 elif packet.haslayer(UDP):
222 self.udp_port_out = packet[UDP].sport
224 self.icmp_external_id = packet[ICMP].id
226 self.logger.error(ppp("Unexpected or invalid packet "
227 "(outside network):", packet))
230 def test_deterministic_mode(self):
231 """ NAT plugin run deterministic mode """
232 in_addr = '172.16.255.0'
233 out_addr = '172.17.255.50'
234 in_addr_t = '172.16.255.20'
238 self.vapi.det44_add_del_map(is_add=1, in_addr=in_addr,
239 in_plen=in_plen, out_addr=out_addr,
242 rep1 = self.vapi.det44_forward(in_addr_t)
243 self.assertEqual(str(rep1.out_addr), out_addr)
244 rep2 = self.vapi.det44_reverse(rep1.out_port_hi, out_addr)
246 self.assertEqual(str(rep2.in_addr), in_addr_t)
248 deterministic_mappings = self.vapi.det44_map_dump()
249 self.assertEqual(len(deterministic_mappings), 1)
250 dsm = deterministic_mappings[0]
251 self.assertEqual(in_addr, str(dsm.in_addr))
252 self.assertEqual(in_plen, dsm.in_plen)
253 self.assertEqual(out_addr, str(dsm.out_addr))
254 self.assertEqual(out_plen, dsm.out_plen)
256 def test_set_timeouts(self):
257 """ Set deterministic NAT timeouts """
258 timeouts_before = self.vapi.det44_get_timeouts()
260 self.vapi.det44_set_timeouts(
261 udp=timeouts_before.udp + 10,
262 tcp_established=timeouts_before.tcp_established + 10,
263 tcp_transitory=timeouts_before.tcp_transitory + 10,
264 icmp=timeouts_before.icmp + 10)
266 timeouts_after = self.vapi.det44_get_timeouts()
268 self.assertNotEqual(timeouts_before.udp, timeouts_after.udp)
269 self.assertNotEqual(timeouts_before.icmp, timeouts_after.icmp)
270 self.assertNotEqual(timeouts_before.tcp_established,
271 timeouts_after.tcp_established)
272 self.assertNotEqual(timeouts_before.tcp_transitory,
273 timeouts_after.tcp_transitory)
276 """ DET44 translation test (TCP, UDP, ICMP) """
280 self.vapi.det44_add_del_map(is_add=1, in_addr=self.pg0.remote_ip4,
282 out_addr=socket.inet_aton(nat_ip),
285 self.vapi.det44_interface_add_del_feature(
286 sw_if_index=self.pg0.sw_if_index,
287 is_add=1, is_inside=1)
288 self.vapi.det44_interface_add_del_feature(
289 sw_if_index=self.pg1.sw_if_index,
290 is_add=1, is_inside=0)
293 pkts = self.create_stream_in(self.pg0, self.pg1)
294 self.pg0.add_stream(pkts)
295 self.pg_enable_capture(self.pg_interfaces)
297 capture = self.pg1.get_capture(len(pkts))
298 self.verify_capture_out(capture, nat_ip)
301 pkts = self.create_stream_out(self.pg1, nat_ip)
302 self.pg1.add_stream(pkts)
303 self.pg_enable_capture(self.pg_interfaces)
305 capture = self.pg0.get_capture(len(pkts))
306 self.verify_capture_in(capture, self.pg0)
309 sessions = self.vapi.det44_session_dump(self.pg0.remote_ip4)
310 self.assertEqual(len(sessions), 3)
314 self.assertEqual(str(s.ext_addr), self.pg1.remote_ip4)
315 self.assertEqual(s.in_port, self.tcp_port_in)
316 self.assertEqual(s.out_port, self.tcp_port_out)
317 self.assertEqual(s.ext_port, self.tcp_external_port)
321 self.assertEqual(str(s.ext_addr), self.pg1.remote_ip4)
322 self.assertEqual(s.in_port, self.udp_port_in)
323 self.assertEqual(s.out_port, self.udp_port_out)
324 self.assertEqual(s.ext_port, self.udp_external_port)
328 self.assertEqual(str(s.ext_addr), self.pg1.remote_ip4)
329 self.assertEqual(s.in_port, self.icmp_id_in)
330 self.assertEqual(s.out_port, self.icmp_external_id)
332 def test_multiple_users(self):
333 """ Deterministic NAT multiple users """
339 host0 = self.pg0.remote_hosts[0]
340 host1 = self.pg0.remote_hosts[1]
342 self.vapi.det44_add_del_map(is_add=1, in_addr=host0.ip4, in_plen=24,
343 out_addr=socket.inet_aton(nat_ip),
345 self.vapi.det44_interface_add_del_feature(
346 sw_if_index=self.pg0.sw_if_index,
347 is_add=1, is_inside=1)
348 self.vapi.det44_interface_add_del_feature(
349 sw_if_index=self.pg1.sw_if_index,
350 is_add=1, is_inside=0)
353 p = (Ether(src=host0.mac, dst=self.pg0.local_mac) /
354 IP(src=host0.ip4, dst=self.pg1.remote_ip4) /
355 TCP(sport=port_in, dport=external_port))
356 self.pg0.add_stream(p)
357 self.pg_enable_capture(self.pg_interfaces)
359 capture = self.pg1.get_capture(1)
364 self.assertEqual(ip.src, nat_ip)
365 self.assertEqual(ip.dst, self.pg1.remote_ip4)
366 self.assertEqual(tcp.dport, external_port)
367 port_out0 = tcp.sport
369 self.logger.error(ppp("Unexpected or invalid packet:", p))
373 p = (Ether(src=host1.mac, dst=self.pg0.local_mac) /
374 IP(src=host1.ip4, dst=self.pg1.remote_ip4) /
375 TCP(sport=port_in, dport=external_port))
376 self.pg0.add_stream(p)
377 self.pg_enable_capture(self.pg_interfaces)
379 capture = self.pg1.get_capture(1)
384 self.assertEqual(ip.src, nat_ip)
385 self.assertEqual(ip.dst, self.pg1.remote_ip4)
386 self.assertEqual(tcp.dport, external_port)
387 port_out1 = tcp.sport
389 self.logger.error(ppp("Unexpected or invalid packet:", p))
392 dms = self.vapi.det44_map_dump()
393 self.assertEqual(1, len(dms))
394 self.assertEqual(2, dms[0].ses_num)
397 p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
398 IP(src=self.pg1.remote_ip4, dst=nat_ip) /
399 TCP(sport=external_port, dport=port_out0))
400 self.pg1.add_stream(p)
401 self.pg_enable_capture(self.pg_interfaces)
403 capture = self.pg0.get_capture(1)
408 self.assertEqual(ip.src, self.pg1.remote_ip4)
409 self.assertEqual(ip.dst, host0.ip4)
410 self.assertEqual(tcp.dport, port_in)
411 self.assertEqual(tcp.sport, external_port)
413 self.logger.error(ppp("Unexpected or invalid packet:", p))
417 p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
418 IP(src=self.pg1.remote_ip4, dst=nat_ip) /
419 TCP(sport=external_port, dport=port_out1))
420 self.pg1.add_stream(p)
421 self.pg_enable_capture(self.pg_interfaces)
423 capture = self.pg0.get_capture(1)
428 self.assertEqual(ip.src, self.pg1.remote_ip4)
429 self.assertEqual(ip.dst, host1.ip4)
430 self.assertEqual(tcp.dport, port_in)
431 self.assertEqual(tcp.sport, external_port)
433 self.logger.error(ppp("Unexpected or invalid packet", p))
436 # session close api test
437 self.vapi.det44_close_session_out(socket.inet_aton(nat_ip),
441 dms = self.vapi.det44_map_dump()
442 self.assertEqual(dms[0].ses_num, 1)
444 self.vapi.det44_close_session_in(host0.ip4,
448 dms = self.vapi.det44_map_dump()
449 self.assertEqual(dms[0].ses_num, 0)
451 def test_tcp_session_close_detection_in(self):
452 """ DET44 TCP session close from inside network """
453 self.vapi.det44_add_del_map(is_add=1, in_addr=self.pg0.remote_ip4,
455 out_addr=socket.inet_aton(self.nat_addr),
457 self.vapi.det44_interface_add_del_feature(
458 sw_if_index=self.pg0.sw_if_index,
459 is_add=1, is_inside=1)
460 self.vapi.det44_interface_add_del_feature(
461 sw_if_index=self.pg1.sw_if_index,
462 is_add=1, is_inside=0)
464 self.initiate_tcp_session(self.pg0, self.pg1)
466 # close the session from inside
468 # FIN packet in -> out
469 p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
470 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
471 TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
473 self.pg0.add_stream(p)
474 self.pg_enable_capture(self.pg_interfaces)
476 self.pg1.get_capture(1)
480 # ACK packet out -> in
481 p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
482 IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
483 TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
487 # FIN packet out -> in
488 p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
489 IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
490 TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
494 self.pg1.add_stream(pkts)
495 self.pg_enable_capture(self.pg_interfaces)
497 self.pg0.get_capture(2)
499 # ACK packet in -> out
500 p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
501 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
502 TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
504 self.pg0.add_stream(p)
505 self.pg_enable_capture(self.pg_interfaces)
507 self.pg1.get_capture(1)
509 # Check if deterministic NAT44 closed the session
510 dms = self.vapi.det44_map_dump()
511 self.assertEqual(0, dms[0].ses_num)
513 self.logger.error("TCP session termination failed")
516 def test_tcp_session_close_detection_out(self):
517 """ Deterministic NAT TCP session close from outside network """
518 self.vapi.det44_add_del_map(is_add=1, in_addr=self.pg0.remote_ip4,
520 out_addr=socket.inet_aton(self.nat_addr),
522 self.vapi.det44_interface_add_del_feature(
523 sw_if_index=self.pg0.sw_if_index,
524 is_add=1, is_inside=1)
525 self.vapi.det44_interface_add_del_feature(
526 sw_if_index=self.pg1.sw_if_index,
527 is_add=1, is_inside=0)
529 self.initiate_tcp_session(self.pg0, self.pg1)
531 # close the session from outside
533 # FIN packet out -> in
534 p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
535 IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
536 TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
538 self.pg1.add_stream(p)
539 self.pg_enable_capture(self.pg_interfaces)
541 self.pg0.get_capture(1)
545 # ACK packet in -> out
546 p = (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,
552 # ACK packet in -> out
553 p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
554 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
555 TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
559 self.pg0.add_stream(pkts)
560 self.pg_enable_capture(self.pg_interfaces)
562 self.pg1.get_capture(2)
564 # ACK packet out -> in
565 p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
566 IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
567 TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
569 self.pg1.add_stream(p)
570 self.pg_enable_capture(self.pg_interfaces)
572 self.pg0.get_capture(1)
574 # Check if deterministic NAT44 closed the session
575 dms = self.vapi.det44_map_dump()
576 self.assertEqual(0, dms[0].ses_num)
578 self.logger.error("TCP session termination failed")
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))
602 self.virtual_sleep(15)
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(config.extended, "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()
666 capture = self.pg2.get_capture(2)
667 ipfix = IPFIXDecoder()
668 # first load template
670 self.assertTrue(p.haslayer(IPFIX))
671 if p.haslayer(Template):
672 ipfix.add_template(p.getlayer(Template))
673 # verify events in data set
676 data = ipfix.decode_data_set(p.getlayer(Set))
677 self.verify_ipfix_max_entries_per_user(data,
680 self.vapi.nat_ipfix_enable_disable(domain_id=1, src_port=4739,