#!/usr/bin/env python3 import unittest import scapy.compat from scapy.packet import Raw from scapy.layers.l2 import Ether, Dot1Q, GRE from scapy.layers.inet import IP, UDP from scapy.layers.inet6 import IPv6 from scapy.volatile import RandMAC, RandIP from framework import VppTestCase, VppTestRunner from vpp_sub_interface import L2_VTR_OP, VppDot1QSubint from vpp_gre_interface import VppGreInterface from vpp_teib import VppTeib from vpp_ip import DpoProto from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, FibPathProto from util import ppp, ppc from vpp_papi import VppEnum class TestGREInputNodes(VppTestCase): """ GRE Input Nodes Test Case """ def setUp(self): super(TestGREInputNodes, self).setUp() # create 3 pg interfaces - set one in a non-default table. self.create_pg_interfaces(range(1)) for i in self.pg_interfaces: i.admin_up() i.config_ip4() def tearDown(self): for i in self.pg_interfaces: i.unconfig_ip4() i.admin_down() super(TestGREInputNodes, self).tearDown() def test_gre_input_node(self): """ GRE gre input nodes not registerd unless configured """ pkt = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) / GRE()) self.pg0.add_stream(pkt) self.pg_start() # no tunnel created, gre-input not registered err = self.statistics.get_counter( '/err/ip4-local/unknown ip protocol')[0] self.assertEqual(err, 1) err_count = err # create gre tunnel gre_if = VppGreInterface(self, self.pg0.local_ip4, "1.1.1.2") gre_if.add_vpp_config() self.pg0.add_stream(pkt) self.pg_start() # tunnel created, gre-input registered err = self.statistics.get_counter( '/err/ip4-local/unknown ip protocol')[0] # expect no new errors self.assertEqual(err, err_count) class TestGRE(VppTestCase): """ GRE Test Case """ @classmethod def setUpClass(cls): super(TestGRE, cls).setUpClass() @classmethod def tearDownClass(cls): super(TestGRE, cls).tearDownClass() def setUp(self): super(TestGRE, self).setUp() # create 3 pg interfaces - set one in a non-default table. self.create_pg_interfaces(range(5)) self.tbl = VppIpTable(self, 1) self.tbl.add_vpp_config() self.pg1.set_table_ip4(1) for i in self.pg_interfaces: i.admin_up() self.pg0.config_ip4() self.pg0.resolve_arp() self.pg1.config_ip4() self.pg1.resolve_arp() self.pg2.config_ip6() self.pg2.resolve_ndp() self.pg3.config_ip4() self.pg3.resolve_arp() self.pg4.config_ip4() self.pg4.resolve_arp() def tearDown(self): for i in self.pg_interfaces: i.unconfig_ip4() i.unconfig_ip6() i.admin_down() self.pg1.set_table_ip4(0) super(TestGRE, self).tearDown() def create_stream_ip4(self, src_if, src_ip, dst_ip, dscp=0, ecn=0): pkts = [] tos = (dscp << 2) | ecn for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=src_ip, dst=dst_ip, tos=tos) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_stream_ip6(self, src_if, src_ip, dst_ip, dscp=0, ecn=0): pkts = [] tc = (dscp << 2) | ecn for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IPv6(src=src_ip, dst=dst_ip, tc=tc) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_4o4(self, src_if, tunnel_src, tunnel_dst, src_ip, dst_ip): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=tunnel_src, dst=tunnel_dst) / GRE() / IP(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_6o4(self, src_if, tunnel_src, tunnel_dst, src_ip, dst_ip): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=tunnel_src, dst=tunnel_dst) / GRE() / IPv6(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_6o6(self, src_if, tunnel_src, tunnel_dst, src_ip, dst_ip): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IPv6(src=tunnel_src, dst=tunnel_dst) / GRE() / IPv6(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_l2o4(self, src_if, tunnel_src, tunnel_dst): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=tunnel_src, dst=tunnel_dst) / GRE() / Ether(dst=RandMAC('*:*:*:*:*:*'), src=RandMAC('*:*:*:*:*:*')) / IP(src=scapy.compat.raw(RandIP()), dst=scapy.compat.raw(RandIP())) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_vlano4(self, src_if, tunnel_src, tunnel_dst, vlan): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=tunnel_src, dst=tunnel_dst) / GRE() / Ether(dst=RandMAC('*:*:*:*:*:*'), src=RandMAC('*:*:*:*:*:*')) / Dot1Q(vlan=vlan) / IP(src=scapy.compat.raw(RandIP()), dst=scapy.compat.raw(RandIP())) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def verify_tunneled_4o4(self, src_if, capture, sent, tunnel_src, tunnel_dst, dscp=0, ecn=0): self.assertEqual(len(capture), len(sent)) tos = (dscp << 2) | ecn for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IP] rx_ip = rx[IP] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) self.assertEqual(rx_ip.tos, tos) rx_gre = rx[GRE] rx_ip = rx_gre[IP] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) # IP processing post pop has decremented the TTL self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_tunneled_6o6(self, src_if, capture, sent, tunnel_src, tunnel_dst, dscp=0, ecn=0): self.assertEqual(len(capture), len(sent)) tc = (dscp << 2) | ecn for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IPv6] rx_ip = rx[IPv6] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) self.assertEqual(rx_ip.tc, tc) rx_gre = GRE(scapy.compat.raw(rx_ip[IPv6].payload)) rx_ip = rx_gre[IPv6] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_tunneled_4o6(self, src_if, capture, sent, tunnel_src, tunnel_dst): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] rx_ip = rx[IPv6] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = GRE(scapy.compat.raw(rx_ip[IPv6].payload)) tx_ip = tx[IP] rx_ip = rx_gre[IP] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_tunneled_6o4(self, src_if, capture, sent, tunnel_src, tunnel_dst): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] rx_ip = rx[IP] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = GRE(scapy.compat.raw(rx_ip[IP].payload)) rx_ip = rx_gre[IPv6] tx_ip = tx[IPv6] self.assertEqual(rx_ip.src, tx_ip.src)
/*
 * Copyright (c) 2016 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 included_clib_cpu_h
#define included_clib_cpu_h

#include <vppinfra/format.h>

/*
 * multiarchitecture support. Adding new entry will produce
 * new graph node function variant optimized for specific cpu
 * microarchitecture.
 * Order is important for runtime selection, as 1st match wins...
 */

#if __x86_64__ && CLIB_DEBUG == 0
#define foreach_march_variant(macro, x) \
  macro(avx2,  x, "arch=core-avx2")
#else
#define foreach_march_variant(macro, x)
#endif


#if __GNUC__ > 4  && !__clang__
#define CLIB_CPU_OPTIMIZED __attribute__ ((optimize ("O3")))
#else
#define CLIB_CPU_OPTIMIZED
#endif


#define CLIB_MULTIARCH_ARCH_CHECK(arch, fn, tgt)			\
  if (clib_cpu_supports_ ## arch())					\
    return & fn ## _ ##arch;

#define CLIB_MULTIARCH_SELECT_FN(fn,...)                               \
  __VA_ARGS__ void * fn ## _multiarch_select(void)                     \
{                                                                      \
  foreach_march_variant(CLIB_MULTIARCH_ARCH_CHECK, fn)                 \
  return & fn;                                                         \
}

#ifdef CLIB_MARCH_VARIANT
#define __CLIB_MULTIARCH_FN(a,b) a##_##b
#define _CLIB_MULTIARCH_FN(a,b) __CLIB_MULTIARCH_FN(a,b)
#define CLIB_MULTIARCH_FN(fn) _CLIB_MULTIARCH_FN(fn,CLIB_MARCH_VARIANT)
#else
#define CLIB_MULTIARCH_FN(fn) fn
#endif

#define CLIB_MARCH_SFX CLIB_MULTIARCH_FN

#define foreach_x86_64_flags \
_ (sse3,     1, ecx, 0)   \
_ (ssse3,    1, ecx, 9)   \
_ (sse41,    1, ecx, 19)  \
_ (sse42,    1, ecx, 20)  \
_ (avx,      1, ecx, 28)  \
_ (avx2,     7, ebx, 5)   \
_ (avx512f,  7, ebx, 16)  \
_ (x86_aes,  1, ecx, 25)  \
_ (sha,      7, ebx, 29)  \
_ (invariant_tsc, 0x80000007, edx, 8)


#define foreach_aarch64_flags \
_ (fp,          0) \
_ (asimd,       1) \
_ (evtstrm,     2) \
_ (aarch64_aes, 3) \
_ (pmull,       4) \
_ (sha1,        5) \
_ (sha2,        6) \
_ (crc32,       7) \
_ (atomics,     8) \
_ (fphp,        9) \
_ (asimdhp,    10) \
_ (cpuid,      11) \
_ (asimdrdm,   12) \
_ (jscvt,      13) \
_ (fcma,       14) \
_ (lrcpc,      15) \
_ (dcpop,      16) \
_ (sha3,       17) \
_ (sm3,        18) \
_ (sm4,        19) \
_ (asimddp,    20) \
_ (sha512,     21) \
_ (sve,        22)

#if defined(__x86_64__)
#include "cpuid.h"

static inline int
clib_get_cpuid (const u32 lev, u32 * eax, u32 * ebx, u32 * ecx, u32 * edx)
{
  if ((u32) __get_cpuid_max (0x80000000 & lev, 0) < lev)
    return 0;
  if (lev == 7)
    __cpuid_count (lev, 0, *eax, *ebx, *ecx, *edx);
  else
    __cpuid (lev, *eax, *ebx, *ecx, *edx);
  return 1;
}


#define _(flag, func, reg, bit) \
static inline int							\
clib_cpu_supports_ ## flag()						\
{									\
  u32 __attribute__((unused)) eax, ebx = 0, ecx = 0, edx  = 0;		\
  clib_get_cpuid (func, &eax, &ebx, &ecx, &edx);			\
									\
  return ((reg & (1 << bit)) != 0);					\
}
foreach_x86_64_flags
#undef _
#else /* __x86_64__ */

#define _(flag, func, reg, bit) \
static inline int clib_cpu_supports_ ## flag() { return 0; }
foreach_x86_64_flags
#undef _
#endif /* __x86_64__ */
#if defined(__aarch64__)
#include <sys/auxv.h>
#define _(flag, bit) \
static inline int							\
clib_cpu_supports_ ## flag()						\
{									\
  unsigned long hwcap = getauxval(AT_HWCAP);				\
  return (hwcap & (1 << bit));						\
}
  foreach_aarch64_flags
#undef _
#else /* ! __x86_64__ && !__aarch64__ */
#define _(flag, bit) \
static inline int clib_cpu_supports_ ## flag() { return 0; }
  foreach_aarch64_flags
#undef _
#endif /* __x86_64__, __aarch64__ */
/*
 * aes is the only feature with the same name in both flag lists
 * handle this by prefixing it with the arch name, and handling it
 * with the custom function below
 */
  static inline int
clib_cpu_supports_aes ()
{
#if defined (__aarch64__)
  return clib_cpu_supports_x86_aes ();
#elif defined (__aarch64__)
  return clib_cpu_supports_aarch64_aes ();
#else
  return 0;
#endif
}

static inline int
clib_cpu_march_priority_avx512 ()
{
  if (clib_cpu_supports_avx512f ())
    return 20;
  return -1;
}

static inline int
clib_cpu_march_priority_avx2 ()
{
  if (clib_cpu_supports_avx2 ())
    return 10;
  return -1;
}

#ifdef CLIB_MARCH_VARIANT
#define CLIB_MARCH_FN_PRIORITY() CLIB_MARCH_SFX(clib_cpu_march_priority)()
#else
#define CLIB_MARCH_FN_PRIORITY() 0
#endif
#endif /* included_clib_cpu_h */

format_function_t format_cpu_uarch;
format_function_t format_cpu_model_name;
format_function_t format_cpu_flags;

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
# # Create an L3 GRE tunnel. # - set it admin up # - assign an IP Addres # gre_if = VppGreInterface(self, self.pg0.local_ip4, "1.1.1.2") gre_if.add_vpp_config() gre_if.admin_up() gre_if.config_ip4() # # add a route to the tunnel's destination that points # through the tunnel, hence forming a loop in the forwarding # graph # route_dst = VppIpRoute(self, "1.1.1.2", 32, [VppRoutePath("0.0.0.0", gre_if.sw_if_index)]) route_dst.add_vpp_config() # # packets to the tunnels destination should be dropped # tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "1.1.1.2") self.send_and_assert_no_replies(self.pg2, tx) self.logger.info(self.vapi.ppcli("sh adj 7")) # # break the loop # route_dst.modify([VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)]) route_dst.add_vpp_config() rx = self.send_and_expect(self.pg0, tx, self.pg1) # # a good route throught the tunnel to check it restacked # route_via_tun_2 = VppIpRoute(self, "2.2.2.2", 32, [VppRoutePath("0.0.0.0", gre_if.sw_if_index)]) route_via_tun_2.add_vpp_config() tx = self.create_stream_ip4(self.pg0, "2.2.2.3", "2.2.2.2") rx = self.send_and_expect(self.pg0, tx, self.pg1) self.verify_tunneled_4o4(self.pg1, rx, tx, self.pg0.local_ip4, "1.1.1.2") # # cleanup # route_via_tun_2.remove_vpp_config() gre_if.remove_vpp_config() def test_mgre(self): """ mGRE IPv4 tunnel Tests """ for itf in self.pg_interfaces[3:]: # # one underlay nh for each overlay/tunnel peer # itf.generate_remote_hosts(4) itf.configure_ipv4_neighbors() # # Create an L3 GRE tunnel. # - set it admin up # - assign an IP Addres # - Add a route via the tunnel # gre_if = VppGreInterface(self, itf.local_ip4, "0.0.0.0", mode=(VppEnum.vl_api_tunnel_mode_t. TUNNEL_API_MODE_MP)) gre_if.add_vpp_config() gre_if.admin_up() gre_if.config_ip4() gre_if.generate_remote_hosts(4) self.logger.info(self.vapi.cli("sh adj")) self.logger.info(self.vapi.cli("sh ip fib")) # # ensure we don't match to the tunnel if the source address # is all zeros # tx = self.create_tunnel_stream_4o4(self.pg0, "0.0.0.0", itf.local_ip4, self.pg0.local_ip4, self.pg0.remote_ip4) self.send_and_assert_no_replies(self.pg0, tx) # # for-each peer # for ii in range(1, 4): route_addr = "4.4.4.%d" % ii # # route traffic via the peer # route_via_tun = VppIpRoute( self, route_addr, 32, [VppRoutePath(gre_if._remote_hosts[ii].ip4, gre_if.sw_if_index)]) route_via_tun.add_vpp_config() # # Add a TEIB entry resolves the peer # teib = VppTeib(self, gre_if, gre_if._remote_hosts[ii].ip4, itf._remote_hosts[ii].ip4) teib.add_vpp_config() # # Send a packet stream that is routed into the tunnel # - packets are GRE encapped # tx_e = self.create_stream_ip4(self.pg0, "5.5.5.5", route_addr) rx = self.send_and_expect(self.pg0, tx_e, itf) self.verify_tunneled_4o4(self.pg0, rx, tx_e, itf.local_ip4, itf._remote_hosts[ii].ip4) tx_i = self.create_tunnel_stream_4o4(self.pg0, itf._remote_hosts[ii].ip4, itf.local_ip4, self.pg0.local_ip4, self.pg0.remote_ip4) rx = self.send_and_expect(self.pg0, tx_i, self.pg0) self.verify_decapped_4o4(self.pg0, rx, tx_i) # # delete and re-add the TEIB # teib.remove_vpp_config() self.send_and_assert_no_replies(self.pg0, tx_e) self.send_and_assert_no_replies(self.pg0, tx_i) teib.add_vpp_config() rx = self.send_and_expect(self.pg0, tx_e, itf) self.verify_tunneled_4o4(self.pg0, rx, tx_e, itf.local_ip4, itf._remote_hosts[ii].ip4) rx = self.send_and_expect(self.pg0, tx_i, self.pg0) self.verify_decapped_4o4(self.pg0, rx, tx_i) gre_if.admin_down() gre_if.unconfig_ip4() def test_mgre6(self): """ mGRE IPv6 tunnel Tests """ self.pg0.config_ip6() self.pg0.resolve_ndp() e = VppEnum.vl_api_tunnel_encap_decap_flags_t for itf in self.pg_interfaces[3:]: # # one underlay nh for each overlay/tunnel peer # itf.config_ip6() itf.generate_remote_hosts(4) itf.configure_ipv6_neighbors() # # Create an L3 GRE tunnel. # - set it admin up # - assign an IP Addres # - Add a route via the tunnel # gre_if = VppGreInterface( self, itf.local_ip6, "::", mode=(VppEnum.vl_api_tunnel_mode_t. TUNNEL_API_MODE_MP), flags=e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP) gre_if.add_vpp_config() gre_if.admin_up() gre_if.config_ip6() gre_if.generate_remote_hosts(4) # # for-each peer # for ii in range(1, 4): route_addr = "4::%d" % ii # # Add a TEIB entry resolves the peer # teib = VppTeib(self, gre_if, gre_if._remote_hosts[ii].ip6, itf._remote_hosts[ii].ip6) teib.add_vpp_config() # # route traffic via the peer # route_via_tun = VppIpRoute( self, route_addr, 128, [VppRoutePath(gre_if._remote_hosts[ii].ip6, gre_if.sw_if_index)]) route_via_tun.add_vpp_config() # # Send a packet stream that is routed into the tunnel # - packets are GRE encapped # tx_e = self.create_stream_ip6(self.pg0, "5::5", route_addr, dscp=2, ecn=1) rx = self.send_and_expect(self.pg0, tx_e, itf) self.verify_tunneled_6o6(self.pg0, rx, tx_e, itf.local_ip6, itf._remote_hosts[ii].ip6, dscp=2) tx_i = self.create_tunnel_stream_6o6(self.pg0, itf._remote_hosts[ii].ip6, itf.local_ip6, self.pg0.local_ip6, self.pg0.remote_ip6) rx = self.send_and_expect(self.pg0, tx_i, self.pg0) self.verify_decapped_6o6(self.pg0, rx, tx_i) # # delete and re-add the TEIB # teib.remove_vpp_config() self.send_and_assert_no_replies(self.pg0, tx_e) teib.add_vpp_config() rx = self.send_and_expect(self.pg0, tx_e, itf) self.verify_tunneled_6o6(self.pg0, rx, tx_e, itf.local_ip6, itf._remote_hosts[ii].ip6, dscp=2) rx = self.send_and_expect(self.pg0, tx_i, self.pg0) self.verify_decapped_6o6(self.pg0, rx, tx_i) gre_if.admin_down() gre_if.unconfig_ip4() itf.unconfig_ip6() self.pg0.unconfig_ip6() if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)