diff options
author | Juraj Sloboda <jsloboda@cisco.com> | 2018-10-02 11:13:53 +0200 |
---|---|---|
committer | Florin Coras <florin.coras@gmail.com> | 2018-10-04 17:55:10 +0000 |
commit | 3048b63de968ef616baf14e1abbb6502bd4fdcbc (patch) | |
tree | c505c1cd8bfb30b073148bf4e7a4836f3e823447 | |
parent | 6f40a8a4c5792c5c5e5c77366e98b1f10370d685 (diff) |
Support reassembly for fragments coming to ip4-local node
Change-Id: I3aa4708c1c3cdda344f282d56b617677080eaaa1
Signed-off-by: Juraj Sloboda <jsloboda@cisco.com>
-rw-r--r-- | src/vnet/ip/ip4_forward.c | 7 | ||||
-rw-r--r-- | src/vnet/ip/ip6_forward.c | 1 | ||||
-rw-r--r-- | src/vnet/ip/lookup.h | 1 | ||||
-rw-r--r-- | test/test_reassembly.py | 119 |
4 files changed, 127 insertions, 1 deletions
diff --git a/src/vnet/ip/ip4_forward.c b/src/vnet/ip/ip4_forward.c index 3cd6d3bb2b1..69a8dbad805 100644 --- a/src/vnet/ip/ip4_forward.c +++ b/src/vnet/ip/ip4_forward.c @@ -1485,6 +1485,7 @@ enum ip_local_packet_type_e { IP_LOCAL_PACKET_TYPE_L4, IP_LOCAL_PACKET_TYPE_NAT, + IP_LOCAL_PACKET_TYPE_FRAG, }; /** @@ -1498,6 +1499,11 @@ ip4_local_classify (vlib_buffer_t * b, ip4_header_t * ip, u16 * next) { ip_lookup_main_t *lm = &ip4_main.lookup_main; + if (PREDICT_FALSE (ip4_is_fragment (ip))) + { + *next = IP_LOCAL_NEXT_REASSEMBLY; + return IP_LOCAL_PACKET_TYPE_FRAG; + } if (PREDICT_FALSE (b->flags & VNET_BUFFER_F_IS_NATED)) { *next = lm->local_next_by_ip_protocol[ip->protocol]; @@ -1644,6 +1650,7 @@ VLIB_REGISTER_NODE (ip4_local_node) = [IP_LOCAL_NEXT_PUNT] = "ip4-punt", [IP_LOCAL_NEXT_UDP_LOOKUP] = "ip4-udp-lookup", [IP_LOCAL_NEXT_ICMP] = "ip4-icmp-input", + [IP_LOCAL_NEXT_REASSEMBLY] = "ip4-reassembly", }, }; /* *INDENT-ON* */ diff --git a/src/vnet/ip/ip6_forward.c b/src/vnet/ip/ip6_forward.c index e05792fe28f..00af2822ac4 100644 --- a/src/vnet/ip/ip6_forward.c +++ b/src/vnet/ip/ip6_forward.c @@ -1389,6 +1389,7 @@ VLIB_REGISTER_NODE (ip6_local_node, static) = [IP_LOCAL_NEXT_PUNT] = "ip6-punt", [IP_LOCAL_NEXT_UDP_LOOKUP] = "ip6-udp-lookup", [IP_LOCAL_NEXT_ICMP] = "ip6-icmp-input", + [IP_LOCAL_NEXT_REASSEMBLY] = "ip6-reassembly", }, }; /* *INDENT-ON* */ diff --git a/src/vnet/ip/lookup.h b/src/vnet/ip/lookup.h index e16fa0757a0..93e1e029b41 100644 --- a/src/vnet/ip/lookup.h +++ b/src/vnet/ip/lookup.h @@ -111,6 +111,7 @@ typedef enum IP_LOCAL_NEXT_PUNT, IP_LOCAL_NEXT_UDP_LOOKUP, IP_LOCAL_NEXT_ICMP, + IP_LOCAL_NEXT_REASSEMBLY, IP_LOCAL_N_NEXT, } ip_local_next_t; diff --git a/test/test_reassembly.py b/test/test_reassembly.py index b2c271decac..d7cc627a318 100644 --- a/test/test_reassembly.py +++ b/test/test_reassembly.py @@ -6,7 +6,7 @@ from framework import VppTestCase, VppTestRunner from scapy.packet import Raw from scapy.layers.l2 import Ether, GRE -from scapy.layers.inet import IP, UDP +from scapy.layers.inet import IP, UDP, ICMP from util import ppp, fragment_rfc791, fragment_rfc8200 from scapy.layers.inet6 import IPv6, IPv6ExtHdrFragment, ICMPv6ParamProblem,\ ICMPv6TimeExceeded @@ -750,6 +750,123 @@ class TestIPv6Reassembly(VppTestCase): self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code") +class TestIPv4ReassemblyLocalNode(VppTestCase): + """ IPv4 Reassembly for packets coming to ip4-local node """ + + @classmethod + def setUpClass(cls): + super(TestIPv4ReassemblyLocalNode, cls).setUpClass() + + cls.create_pg_interfaces([0]) + cls.src_dst_if = cls.pg0 + + # setup all interfaces + for i in cls.pg_interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + cls.padding = " abcdefghijklmn" + cls.create_stream() + cls.create_fragments() + + def setUp(self): + """ Test setup - force timeout on existing reassemblies """ + super(TestIPv4ReassemblyLocalNode, self).setUp() + self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000, + expire_walk_interval_ms=10) + self.sleep(.25) + self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000, + expire_walk_interval_ms=10000) + + def tearDown(self): + super(TestIPv4ReassemblyLocalNode, self).tearDown() + self.logger.debug(self.vapi.ppcli("show ip4-reassembly details")) + + @classmethod + def create_stream(cls, packet_count=test_packet_count): + """Create input packet stream for defined interface. + + :param list packet_sizes: Required packet sizes. + """ + for i in range(0, packet_count): + info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if) + payload = cls.info_to_payload(info) + p = (Ether(dst=cls.src_dst_if.local_mac, + src=cls.src_dst_if.remote_mac) / + IP(id=info.index, src=cls.src_dst_if.remote_ip4, + dst=cls.src_dst_if.local_ip4) / + ICMP(type='echo-request', id=1234) / + Raw(payload)) + cls.extend_packet(p, 1518, cls.padding) + info.data = p + + @classmethod + def create_fragments(cls): + infos = cls._packet_infos + cls.pkt_infos = [] + for index, info in infos.iteritems(): + p = info.data + # cls.logger.debug(ppp("Packet:", p.__class__(str(p)))) + fragments_300 = fragment_rfc791(p, 300) + cls.pkt_infos.append((index, fragments_300)) + cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags] + cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" % + (len(infos), len(cls.fragments_300))) + + def verify_capture(self, capture): + """Verify captured packet stream. + + :param list capture: Captured packet stream. + """ + info = None + seen = set() + for packet in capture: + try: + self.logger.debug(ppp("Got packet:", packet)) + ip = packet[IP] + icmp = packet[ICMP] + payload_info = self.payload_to_info(str(packet[Raw])) + packet_index = payload_info.index + if packet_index in seen: + raise Exception(ppp("Duplicate packet received", packet)) + seen.add(packet_index) + self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index) + info = self._packet_infos[packet_index] + self.assertTrue(info is not None) + self.assertEqual(packet_index, info.index) + saved_packet = info.data + self.assertEqual(ip.src, saved_packet[IP].dst) + self.assertEqual(ip.dst, saved_packet[IP].src) + self.assertEqual(icmp.type, 0) # echo reply + self.assertEqual(icmp.id, saved_packet[ICMP].id) + self.assertEqual(icmp.payload, saved_packet[ICMP].payload) + except Exception: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + for index in self._packet_infos: + self.assertTrue(index in seen or index in dropped_packet_indexes, + "Packet with packet_index %d not received" % index) + + def test_reassembly(self): + """ basic reassembly """ + + self.pg_enable_capture() + self.src_dst_if.add_stream(self.fragments_300) + self.pg_start() + + packets = self.src_dst_if.get_capture(len(self.pkt_infos)) + self.verify_capture(packets) + + # run it all again to verify correctness + self.pg_enable_capture() + self.src_dst_if.add_stream(self.fragments_300) + self.pg_start() + + packets = self.src_dst_if.get_capture(len(self.pkt_infos)) + self.verify_capture(packets) + + class TestFIFReassembly(VppTestCase): """ Fragments in fragments reassembly """ |