1ead1e536SRenato Botelho do Couto#!/usr/bin/env python3
28a9c8f14SOle Troan"""IP4 and IP6 MTU functional tests"""
38a9c8f14SOle Troan
48a9c8f14SOle Troan#
58a9c8f14SOle Troan# Add tests for:
68a9c8f14SOle Troan# - sub interfaces
78a9c8f14SOle Troan# - Verify that adjacencies inherit MTU correctly
88a9c8f14SOle Troan# - Verify that sub-interfaces inherit MTU correctly
98a9c8f14SOle Troan# - Different types of interfaces?
108a9c8f14SOle Troan#
118a9c8f14SOle Troanimport unittest
128a9c8f14SOle Troanfrom scapy.layers.inet6 import IPv6, Ether, IP, UDP, ICMPv6PacketTooBig
138a9c8f14SOle Troanfrom scapy.layers.inet import ICMP
148a9c8f14SOle Troanfrom framework import VppTestCase, VppTestRunner
15c0a93143SNeale Rannsfrom vpp_ip import DpoProto
16097fa66bSNeale Rannsfrom vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto
178a9c8f14SOle Troanfrom socket import AF_INET, AF_INET6, inet_pton
187f99183aSOle Troanfrom util import reassemble4
197f99183aSOle Troan
208a9c8f14SOle Troan
218a9c8f14SOle Troan""" Test_mtu is a subclass of VPPTestCase classes.
228a9c8f14SOle Troan    MTU tests.
238a9c8f14SOle Troan"""
248a9c8f14SOle Troan
258a9c8f14SOle Troan
268a9c8f14SOle Troanclass TestMTU(VppTestCase):
278a9c8f14SOle Troan    """ MTU Test Case """
287f99183aSOle Troan    maxDiff = None
298a9c8f14SOle Troan
308a9c8f14SOle Troan    @classmethod
318a9c8f14SOle Troan    def setUpClass(cls):
328a9c8f14SOle Troan        super(TestMTU, cls).setUpClass()
338a9c8f14SOle Troan        cls.create_pg_interfaces(range(2))
348a9c8f14SOle Troan        cls.interfaces = list(cls.pg_interfaces)
358a9c8f14SOle Troan
367f9b7f9fSPaul Vinciguerra    @classmethod
377f9b7f9fSPaul Vinciguerra    def tearDownClass(cls):
387f9b7f9fSPaul Vinciguerra        super(TestMTU, cls).tearDownClass()
397f9b7f9fSPaul Vinciguerra
400c794531SPaul Vinciguerra    def setUp(self):
410c794531SPaul Vinciguerra        super(TestMTU, self).setUp()
420c794531SPaul Vinciguerra        for i in self.interfaces:
438a9c8f14SOle Troan            i.admin_up()
448a9c8f14SOle Troan            i.config_ip4()
458a9c8f14SOle Troan            i.config_ip6()
468a9c8f14SOle Troan            i.disable_ipv6_ra()
478a9c8f14SOle Troan            i.resolve_arp()
488a9c8f14SOle Troan            i.resolve_ndp()
498a9c8f14SOle Troan
508a9c8f14SOle Troan    def tearDown(self):
518a9c8f14SOle Troan        super(TestMTU, self).tearDown()
528a9c8f14SOle Troan        if not self.vpp_dead:
538a9c8f14SOle Troan            for i in self.pg_interfaces:
548a9c8f14SOle Troan                i.unconfig_ip4()
558a9c8f14SOle Troan                i.unconfig_ip6()
568a9c8f14SOle Troan                i.admin_down()
578a9c8f14SOle Troan
588a9c8f14SOle Troan    def validate(self, rx, expected):
597f99183aSOle Troan        self.assertEqual(rx, expected.__class__(expected))
608a9c8f14SOle Troan
618a9c8f14SOle Troan    def validate_bytes(self, rx, expected):
628a9c8f14SOle Troan        self.assertEqual(rx, expected)
638a9c8f14SOle Troan
648a9c8f14SOle Troan    def payload(self, len):
658a9c8f14SOle Troan        return 'x' * len
668a9c8f14SOle Troan
678a9c8f14SOle Troan    def get_mtu(self, sw_if_index):
687a99823cSPaul Vinciguerra        rv = self.vapi.sw_interface_dump(sw_if_index=sw_if_index)
698a9c8f14SOle Troan        for i in rv:
708a9c8f14SOle Troan            if i.sw_if_index == sw_if_index:
71d723161eSOle Troan                return i.mtu[0]
728a9c8f14SOle Troan        return 0
738a9c8f14SOle Troan
748a9c8f14SOle Troan    def test_ip4_mtu(self):
758a9c8f14SOle Troan        """ IP4 MTU test """
768a9c8f14SOle Troan
778a9c8f14SOle Troan        p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
788a9c8f14SOle Troan        p_ip4 = IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4,
798a9c8f14SOle Troan                   flags='DF')
808a9c8f14SOle Troan
818a9c8f14SOle Troan        current_mtu = self.get_mtu(self.pg1.sw_if_index)
828a9c8f14SOle Troan
838a9c8f14SOle Troan        p_payload = UDP(sport=1234, dport=1234) / self.payload(
848a9c8f14SOle Troan            current_mtu - 20 - 8)
858a9c8f14SOle Troan
868a9c8f14SOle Troan        p4 = p_ether / p_ip4 / p_payload
878a9c8f14SOle Troan        p4_reply = p_ip4 / p_payload
888a9c8f14SOle Troan        p4_reply.ttl -= 1
898a9c8f14SOle Troan        rx = self.send_and_expect(self.pg0, p4*11, self.pg1)
908a9c8f14SOle Troan        for p in rx:
918a9c8f14SOle Troan            self.validate(p[1], p4_reply)
928a9c8f14SOle Troan
938a9c8f14SOle Troan        # MTU
94d723161eSOle Troan        self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [576, 0, 0, 0])
95d723161eSOle Troan        self.assertEqual(576, self.get_mtu(self.pg1.sw_if_index))
968a9c8f14SOle Troan
978a9c8f14SOle Troan        # Should fail. Too large MTU
988a9c8f14SOle Troan        p_icmp4 = ICMP(type='dest-unreach', code='fragmentation-needed',
998a9c8f14SOle Troan                       nexthopmtu=576, chksum=0x2dbb)
1008a9c8f14SOle Troan        icmp4_reply = (IP(src=self.pg0.local_ip4,
1018a9c8f14SOle Troan                          dst=self.pg0.remote_ip4,
1028a9c8f14SOle Troan                          ttl=254, len=576, id=0) /
1038a9c8f14SOle Troan                       p_icmp4 / p_ip4 / p_payload)
1047f99183aSOle Troan        n = icmp4_reply.__class__(icmp4_reply)
1057f99183aSOle Troan        s = bytes(icmp4_reply)
1068a9c8f14SOle Troan        icmp4_reply = s[0:576]
1078a9c8f14SOle Troan        rx = self.send_and_expect(self.pg0, p4*11, self.pg0)
1088a9c8f14SOle Troan        for p in rx:
1098a9c8f14SOle Troan            # p.show2()
1108a9c8f14SOle Troan            # n.show2()
1117f99183aSOle Troan            self.validate_bytes(bytes(p[1]), icmp4_reply)
1128a9c8f14SOle Troan
1138a9c8f14SOle Troan        # Now with DF off. Expect fragments.
1148a9c8f14SOle Troan        # First go with 1500 byte packets.
1158a9c8f14SOle Troan        p_payload = UDP(sport=1234, dport=1234) / self.payload(
1168a9c8f14SOle Troan            1500 - 20 - 8)
1178a9c8f14SOle Troan        p4 = p_ether / p_ip4 / p_payload
1188a9c8f14SOle Troan        p4.flags = 0
1198a9c8f14SOle Troan        p4_reply = p_ip4 / p_payload
1200b6a857dSNeale Ranns        p4_reply.ttl = p_ip4.ttl - 1
1218a9c8f14SOle Troan        p4_reply.flags = 0
1228a9c8f14SOle Troan        p4_reply.id = 256
1238a9c8f14SOle Troan        self.pg_enable_capture()
1248a9c8f14SOle Troan        self.pg0.add_stream(p4*1)
1258a9c8f14SOle Troan        self.pg_start()
1268a9c8f14SOle Troan        rx = self.pg1.get_capture(3)
1277f99183aSOle Troan        reass_pkt = reassemble4(rx)
1288a9c8f14SOle Troan        self.validate(reass_pkt, p4_reply)
129313f7e2fSOle Troan
1308a9c8f14SOle Troan        '''
1318a9c8f14SOle Troan        # Now what happens with a 9K frame
1328a9c8f14SOle Troan        p_payload = UDP(sport=1234, dport=1234) / self.payload(
1338a9c8f14SOle Troan            current_mtu - 20 - 8)
1348a9c8f14SOle Troan        p4 = p_ether / p_ip4 / p_payload
1358a9c8f14SOle Troan        p4.flags = 0
1368a9c8f14SOle Troan        p4_reply = p_ip4 / p_payload
1378a9c8f14SOle Troan        p4_reply.ttl = 62 # check this
1388a9c8f14SOle Troan        p4_reply.flags = 0
1398a9c8f14SOle Troan        p4_reply.id = 512
1408a9c8f14SOle Troan
1418a9c8f14SOle Troan        self.pg_enable_capture()
1428a9c8f14SOle Troan        self.pg0.add_stream(p4*1)
1438a9c8f14SOle Troan        self.pg_start()
1448a9c8f14SOle Troan        rx = self.pg1.get_capture(16)
1457f99183aSOle Troan        reass_pkt = reassemble4(rx)
1468a9c8f14SOle Troan        reass_pkt.show2()
1478a9c8f14SOle Troan        p4_reply.show2()
1488a9c8f14SOle Troan        self.validate(reass_pkt, p4_reply)
1498a9c8f14SOle Troan        '''
150313f7e2fSOle Troan
1518a9c8f14SOle Troan        # Reset MTU
152da6e11b4SOle Troan        self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index,
153d723161eSOle Troan                                       [current_mtu, 0, 0, 0])
1548a9c8f14SOle Troan
1558a9c8f14SOle Troan    def test_ip6_mtu(self):
1568a9c8f14SOle Troan        """ IP6 MTU test """
1578a9c8f14SOle Troan
158da6e11b4SOle Troan        current_mtu = self.get_mtu(self.pg1.sw_if_index)
159da6e11b4SOle Troan
1608a9c8f14SOle Troan        p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1618a9c8f14SOle Troan        p_ip6 = IPv6(src=self.pg0.remote_ip6, dst=self.pg1.remote_ip6)
1628a9c8f14SOle Troan
1638a9c8f14SOle Troan        p_payload = UDP(sport=1234, dport=1234) / self.payload(
1648a9c8f14SOle Troan            current_mtu - 40 - 8)
1658a9c8f14SOle Troan
1668a9c8f14SOle Troan        p6 = p_ether / p_ip6 / p_payload
1678a9c8f14SOle Troan        p6_reply = p_ip6 / p_payload
1688a9c8f14SOle Troan        p6_reply.hlim -= 1
1698a9c8f14SOle Troan        rx = self.send_and_expect(self.pg0, p6*9, self.pg1)
1708a9c8f14SOle Troan        for p in rx:
1718a9c8f14SOle Troan            self.validate(p[1], p6_reply)
1728a9c8f14SOle Troan
1738a9c8f14SOle Troan        # MTU (only checked on encap)
174d723161eSOle Troan        self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1280, 0, 0, 0])
175d723161eSOle Troan        self.assertEqual(1280, self.get_mtu(self.pg1.sw_if_index))
1768a9c8f14SOle Troan
1778a9c8f14SOle Troan        # Should fail. Too large MTU
1788a9c8f14SOle Troan        p_icmp6 = ICMPv6PacketTooBig(mtu=1280, cksum=0x4c7a)
1798a9c8f14SOle Troan        icmp6_reply = (IPv6(src=self.pg0.local_ip6,
1808a9c8f14SOle Troan                            dst=self.pg0.remote_ip6,
181282093f1SOle Troan                            hlim=255, plen=1240) /
1828a9c8f14SOle Troan                       p_icmp6 / p_ip6 / p_payload)
1838a9c8f14SOle Troan        icmp6_reply[2].hlim -= 1
1847f99183aSOle Troan        n = icmp6_reply.__class__(icmp6_reply)
1857f99183aSOle Troan        s = bytes(icmp6_reply)
186282093f1SOle Troan        icmp6_reply_str = s[0:1280]
1878a9c8f14SOle Troan
1888a9c8f14SOle Troan        rx = self.send_and_expect(self.pg0, p6*9, self.pg0)
1898a9c8f14SOle Troan        for p in rx:
1907f99183aSOle Troan            self.validate_bytes(bytes(p[1]), icmp6_reply_str)
1918a9c8f14SOle Troan
1928a9c8f14SOle Troan        # Reset MTU
193d723161eSOle Troan        self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index,
194d723161eSOle Troan                                       [current_mtu, 0, 0, 0])
1958a9c8f14SOle Troan
1968a9c8f14SOle Troan
1978a9c8f14SOle Troanif __name__ == '__main__':
1988a9c8f14SOle Troan    unittest.main(testRunner=VppTestRunner)
199