#!/usr/bin/env python3 """IP{4,6} over IP{v,6} tunnel functional tests""" import unittest from scapy.layers.inet6 import IPv6, Ether, IP, UDP, IPv6ExtHdrFragment, Raw from scapy.all import fragment, fragment6, RandShort, defragment6 from framework import VppTestCase, VppTestRunner from vpp_ip import DpoProto from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, FibPathProto from vpp_ipip_tun_interface import VppIpIpTunInterface from vpp_teib import VppTeib from vpp_papi import VppEnum from socket import AF_INET, AF_INET6, inet_pton from util import reassemble4 """ Testipip is a subclass of VPPTestCase classes. IPIP tests. """ def ipip_add_tunnel(test, src, dst, table_id=0, dscp=0x0, flags=0): """ Add a IPIP tunnel """ return test.vapi.ipip_add_tunnel( tunnel={ 'src': src, 'dst': dst, 'table_id': table_id, 'instance': 0xffffffff, 'dscp': dscp, 'flags': flags } ) # the number of packets to send when injecting traffic. # a multiple of 8 minus one, so we test all by 8/4/2/1 loops N_PACKETS = 64 - 1 class TestIPIP(VppTestCase): """ IPIP Test Case """ @classmethod def setUpClass(cls): super(TestIPIP, cls).setUpClass() cls.create_pg_interfaces(range(2)) cls.interfaces = list(cls.pg_interfaces) @classmethod def tearDownClass(cls): super(TestIPIP, cls).tearDownClass() def setUp(self): super(TestIPIP, self).setUp() for i in self.interfaces: i.admin_up() i.config_ip4() i.config_ip6() i.disable_ipv6_ra() i.resolve_arp() i.resolve_ndp() def tearDown(self): super(TestIPIP, self).tearDown() if not self.vpp_dead: for i in self.pg_interfaces: i.unconfig_ip4() i.unconfig_ip6() i.admin_down() def validate(self, rx, expected): self.assertEqual(rx, expected.__class__(expected)) def generate_ip4_frags(self, payload_length, fragment_size): p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) p_payload = UDP(sport=1234, dport=1234) / self.payload(payload_length) p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4) outer_ip4 = (p_ether / IP(src=self.pg1.remote_ip4, id=RandShort(), dst=self.pg0.local_ip4) / p_ip4 / p_payload) frags = fragment(outer_ip4, fragment_size) p4_reply = (p_ip4 / p_payload) p4_reply.ttl -= 1 return frags, p4_reply def verify_ip4ip4_encaps(self, a, p_ip4s, p_ip4_encaps): for i, p_ip4 in enumerate(p_ip4s): p_ip4.dst = a p4 = (self.p_ether / p_ip4 / self.p_payload) p_ip4_inner = p_ip4 p_ip4_inner.ttl -= 1 p4_reply = (p_ip4_encaps[i] / p_ip4_inner / self.p_payload) p4_reply.ttl -= 1 p4_reply.id = 0 rx = self.send_and_expect(self.pg0, p4 * N_PACKETS, self.pg1) for p in rx: self.validate(p[1], p4_reply) self.assert_packet_checksums_valid(p) def verify_ip6ip4_encaps(self, a, p_ip6s, p_ip4_encaps): for i, p_ip6 in enumerate(p_ip6s): p_ip6.dst = a p6 = (self.p_ether / p_ip6 / self.p_payload) p_inner_ip6 = p_ip6 p_inner_ip6.hlim -= 1 p6_reply = (p_ip4_encaps[i] / p_inner_ip6 / self.p_payload) p6_reply.ttl -= 1 rx = self.send_and_expect(self.pg0, p6 * N_PACKETS, self.pg1) for p in rx: self.validate(p[1], p6_reply) self.assert_packet_checksums_valid(p) def test_ipip4(self): """ ip{v4,v6} over ip4 test """ self.pg1.generate_remote_hosts(5) self.pg1.configure_ipv4_neighbors() e = VppEnum.vl_api_tunnel_encap_decap_flags_t d = VppEnum.vl_api_ip_dscp_t self.p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) self.p_payload = UDP(sport=1234, dport=1234) / Raw(b'X' * 100) # create a TOS byte by shifting a DSCP code point 2 bits. those 2 bits # are for the ECN. dscp = d.IP_API_DSCP_AF31 << 2 ecn = 3 dscp_ecn = d.IP_API_DSCP_AF31 << 2 | ecn # IPv4 transport that copies the DCSP from the payload tun_dscp = VppIpIpTunInterface( self, self.pg0, self.pg0.local_ip4, self.pg1.remote_hosts[0].ip4, flags=e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP) tun_dscp.add_vpp_config() # IPv4 transport that copies the DCSP and ECN from the payload tun_dscp_ecn = VppIpIpTunInterface( self, self.pg0, self.pg0.local_ip4, self.pg1.remote_hosts[1].ip4, flags=(e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP | e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN)) tun_dscp_ecn.add_vpp_config() # IPv4 transport that copies the ECN from the payload and sets the # DF bit on encap. copies the ECN on decap tun_ecn = VppIpIpTunInterface( self, self.pg0, self.pg0.local_ip4, self.pg1.remote_hosts[2].ip4, flags=(e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN | e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF | e.TUNNEL_API_ENCAP_DECAP_FLAG_DECAP_COPY_ECN)) tun_ecn.add_vpp_config() # IPv4 transport that sets a fixed DSCP in the encap and copies # the DF bit tun = VppIpIpTunInterface( self, self.pg0, self.pg0.local_ip4, self.pg1.remote_hosts[3].ip4, dscp=d.IP_API_DSCP_AF11, flags=e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF) tun.add_vpp_config() # array of all the tunnels tuns = [tun_dscp, tun_dscp_ecn, tun_ecn, tun] # addresses for prefixes routed via each tunnel a4s = ["" for i in range(len(tuns))] a6s = ["" for i in range(len(tuns))] # IP headers with each combination of DSCp/ECN tested p_ip6s = [IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=dscp), IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=dscp_ecn), IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=ecn), IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=0xff)] p_ip4s = [IP(src="1.2.3.4", dst="130.67.0.1", tos=dscp, flags='DF'), IP(src="1.2.3.4", dst="130.67.0.1", tos=dscp_ecn), IP(src="1.2.3.4", dst="130.67.0.1", tos=ecn), IP(src="1.2.3.4", dst="130.67.0.1", tos=0xff)] # Configure each tunnel for i, t in enumerate(tuns): # Set interface up and enable IP on it self.vapi.sw_interface_set_flags(t.sw_if_index, 1) self.vapi.sw_interface_set_unnumbered( sw_if_index=self.pg0.sw_if_index, unnumbered_sw_if_index=t.sw_if_index) # prefix for route / destination address for packets a4s[i] = "130.67.%d.0" % i a6
/*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __CNAT_SRC_POLICY_H__
#define __CNAT_SRC_POLICY_H__
// #include <vnet/udp/udp.h>
#include <cnat/cnat_types.h>
#include <cnat/cnat_session.h>
#include <cnat/cnat_translation.h>
typedef enum
{
CNAT_SPORT_PROTO_TCP,
CNAT_SPORT_PROTO_UDP,
CNAT_SPORT_PROTO_ICMP,
CNAT_SPORT_PROTO_ICMP6,
CNAT_N_SPORT_PROTO
} cnat_sport_proto_t;
typedef enum cnat_source_policy_errors_
{
CNAT_SOURCE_ERROR_EXHAUSTED_PORTS = 1,
CNAT_SOURCE_ERROR_USE_DEFAULT = 2,
} cnat_source_policy_errors_t;
typedef struct cnat_src_port_allocator_
{
/* Source ports bitmap for snat */
clib_bitmap_t *bmap;
/* Lock for src_ports access */
clib_spinlock_t lock;
} cnat_src_port_allocator_t;
/* function to use to compute source (IP, port) for a new session to a vip */
typedef cnat_source_policy_errors_t (*cnat_vip_source_policy_t)
(vlib_main_t * vm, vlib_buffer_t * b, cnat_session_t * session,
u32 * rsession_flags, const cnat_translation_t * ct,
cnat_node_ctx_t * ctx);
typedef struct cnat_src_policy_main_
{
cnat_vip_source_policy_t vip_policy;
cnat_vip_source_policy_t default_policy;
/* Per proto source ports allocator for snat */
cnat_src_port_allocator_t *src_ports;
} cnat_src_policy_main_t;
extern cnat_src_policy_main_t cnat_src_policy_main;
void cnat_register_vip_src_policy (cnat_vip_source_policy_t fp);
int cnat_allocate_port (u16 * port, ip_protocol_t iproto);
void cnat_free_port (u16 port, ip_protocol_t iproto);
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
#endif