summaryrefslogtreecommitdiffstats
path: root/src/vlib/format_funcs.h
blob: 30e919d7e96cf0be021e88a3972be31240c1a5f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/*
 * Copyright (c) 2015 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.
 */
/*
 * format_funcs.h: VLIB formatting/unformating
 *
 * Copyright (c) 2008 Eliot Dresselhaus
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef included_vlib_format_h
#define included_vlib_format_h

/* Format vlib_rx_or_tx_t/vlib_read_or_write_t enum as string. */
u8 *format_vlib_rx_tx (u8 * s, va_list * args);
u8 *format_vlib_read_write (u8 * s, va_list * args);

/* Formats buffer data as printable ascii or as hex. */
u8 *format_vlib_buffer_data (u8 * s, va_list * args);

/* Enable/on => 1; disable/off => 0. */
uword unformat_vlib_enable_disable (unformat_input_t * input, va_list * args);

/* rx/tx => VLIB_RX/VLIB_TX. */
uword unformat_vlib_rx_tx (unformat_input_t * input, va_list * args);

/* Parse a-zA-Z0-9_ token and hash to value. */
uword unformat_vlib_number_by_name (unformat_input_t * input, va_list * args);

/* Parse an int either %d or 0x%x. */
uword unformat_vlib_number (unformat_input_t * input, va_list * args);

/* Parse a filename to dump debug info */
uword unformat_vlib_tmpfile (unformat_input_t * input, va_list * args);

/* Flag to format_vlib_*_header functions to tell them not to recurse
   into the next layer's header.  For example, tells format_vlib_ethernet_header
   not to format ip header. */
#define FORMAT_VLIB_HEADER_NO_RECURSION (~0)

#endif /* included_vlib_format_h */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
, UDP from scapy.layers.inet6 import IPv6 from vpp_ip_route import VppIpRoute, VppRoutePath try: text_type = unicode except NameError: text_type = str # # The number of packets to sent. # N_PKTS_IN_STREAM = 300 class TestECMP(VppTestCase): """Equal-cost multi-path routing Test Case""" @classmethod def setUpClass(cls): """ Perform standard class setup (defined by class method setUpClass in class VppTestCase) before running the test case, set test case related variables and configure VPP. """ super(TestECMP, cls).setUpClass() # create 4 pg interfaces cls.create_pg_interfaces(range(4)) # packet sizes to test cls.pg_if_packet_sizes = [64, 1500, 9018] # setup interfaces for i in cls.pg_interfaces: i.admin_up() i.generate_remote_hosts(5) i.config_ip4() i.resolve_arp() i.configure_ipv4_neighbors() i.config_ip6() i.resolve_ndp() i.configure_ipv6_neighbors() @classmethod def tearDownClass(cls): if not cls.vpp_dead: for i in cls.pg_interfaces: i.unconfig_ip4() i.unconfig_ip6() i.admin_down() super(TestECMP, cls).tearDownClass() def setUp(self): super(TestECMP, self).setUp() self.reset_packet_infos() def tearDown(self): """ Show various debug prints after each test. """ super(TestECMP, self).tearDown() def show_commands_at_teardown(self): self.logger.info(self.vapi.ppcli("show ip4 neighbors")) self.logger.info(self.vapi.ppcli("show ip6 neighbors")) def get_ip_address(self, ip_addr_start, ip_prefix_len): """ :param str ip_addr_start: Starting IPv4 or IPv6 address. :param int ip_prefix_len: IP address prefix length. :return: Random IPv4 or IPv6 address from required range. """ try: ip_addr = IPv4Address(text_type(ip_addr_start)) ip_max_len = 32 except (AttributeError, AddressValueError): ip_addr = IPv6Address(text_type(ip_addr_start)) ip_max_len = 128 return str(ip_addr + random.randint(0, 2 ** (ip_max_len - ip_prefix_len) - 2)) def create_stream( self, src_if, src_ip_start, dst_ip_start, ip_prefix_len, packet_sizes, ip_l=IP ): """Create input packet stream for defined interfaces. :param VppInterface src_if: Source Interface for packet stream. :param str src_ip_start: Starting source IPv4 or IPv6 address. :param str dst_ip_start: Starting destination IPv4 or IPv6 address. :param int ip_prefix_len: IP address prefix length. :param list packet_sizes: packet size to test. :param Scapy ip_l: Required IP layer - IP or IPv6. (Default is IP.) """ pkts = [] for i in range(0, N_PKTS_IN_STREAM): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) src_ip = self.get_ip_address(src_ip_start, ip_prefix_len) dst_ip = self.get_ip_address(dst_ip_start, ip_prefix_len) p = ( Ether(dst=src_if.local_mac, src=src_if.remote_mac) / ip_l(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload) ) info.data = p.copy() size = random.choice(packet_sizes) self.extend_packet(p, size) pkts.append(p) return pkts def verify_capture(self, rx_if, capture, ip_l=IP): """Verify captured input packet stream for defined interface. :param VppInterface rx_if: Interface to verify captured packet stream. :param list capture: Captured packet stream. :param Scapy ip_l: Required IP layer - IP or IPv6. (Default is IP.) """ self.logger.info("Verifying capture on interface %s" % rx_if.name) count = 0 host_counters = {} for host_mac in rx_if._hosts_by_mac: host_counters[host_mac] = 0 for packet in capture: try: ip_received = packet[ip_l] payload_info = self.payload_to_info(packet[Raw]) packet_index = payload_info.index ip_sent = self._packet_infos[packet_index].data[ip_l] self.logger.debug( "Got packet on port %s: src=%u (id=%u)" % (rx_if.name, payload_info.src, packet_index) ) # Check standard fields self.assertIn( packet.dst, rx_if._hosts_by_mac, "Destination MAC address %s shouldn't be routed " "via interface %s" % (packet.dst, rx_if.name), ) self.assertEqual(packet.src, rx_if.local_mac) self.assertEqual(ip_received.src, ip_sent.src) self.assertEqual(ip_received.dst, ip_sent.dst) host_counters[packet.dst] += 1 self._packet_infos.pop(packet_index) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise # We expect packet routed via all host of pg interface for host_mac in host_counters: nr = host_counters[host_mac] self.assertNotEqual(nr, 0, "No packet routed via host %s" % host_mac) self.logger.info( "%u packets routed via host %s of %s interface" % (nr, host_mac, rx_if.name) ) count += nr self.logger.info( "Total amount of %u packets routed via %s interface" % (count, rx_if.name) ) return count def create_ip_routes(self, dst_ip_net, dst_prefix_len, is_ipv6=0): """ Create IP routes for defined destination IP network. :param str dst_ip_net: Destination IP network. :param int dst_prefix_len: IP address prefix length. :param int is_ipv6: 0 if an ip4 route, else ip6 """ paths = [] for pg_if in self.pg_interfaces[1:]: for nh_host in pg_if.remote_hosts: nh_host_ip = nh_host.ip4 if is_ipv6 == 0 else nh_host.ip6 paths.append(VppRoutePath(nh_host_ip, pg_if.sw_if_index)) rip = VppIpRoute(self, dst_ip_net, dst_prefix_len, paths) rip.add_vpp_config() self.logger.info("Route via %s on %s created" % (nh_host_ip, pg_if.name)) self.logger.debug(self.vapi.ppcli("show ip fib")) self.logger.debug(self.vapi.ppcli("show ip6 fib")) def test_ip_ecmp(self): """IP equal-cost multi-path routing test""" src_ip_net = "16.0.0.1" dst_ip_net = "32.0.0.1" ip_prefix_len = 24 self.create_ip_routes(dst_ip_net, ip_prefix_len) pkts = self.create_stream( self.pg0, src_ip_net, dst_ip_net, ip_prefix_len, self.pg_if_packet_sizes ) self.pg0.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() # We expect packets on pg1, pg2 and pg3, but not on pg0 rx_count = 0 for pg_if in self.pg_interfaces[1:]: capture = pg_if._get_capture(timeout=1) self.assertNotEqual( len(capture), 0, msg="No packets captured on %s" % pg_if.name ) rx_count += self.verify_capture(pg_if, capture) self.pg0.assert_nothing_captured(remark="IP packets forwarded on pg0") # Check that all packets were forwarded via pg1, pg2 and pg3 self.assertEqual(rx_count, len(pkts)) def test_ip6_ecmp(self): """IPv6 equal-cost multi-path routing test""" src_ip_net = "3ffe:51::1" dst_ip_net = "3ffe:71::1" ip_prefix_len = 64 self.create_ip_routes(dst_ip_net, ip_prefix_len, is_ipv6=1) pkts = self.create_stream( self.pg0, src_ip_net, dst_ip_net, ip_prefix_len, self.pg_if_packet_sizes, ip_l=IPv6, ) self.pg0.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() # We expect packets on pg1, pg2 and pg3, but not on pg0 rx_count = 0 for pg_if in self.pg_interfaces[1:]: capture = pg_if._get_capture(timeout=1) self.assertNotEqual( len(capture), 0, msg="No packets captured on %s" % pg_if.name ) rx_count += self.verify_capture(pg_if, capture, ip_l=IPv6) self.pg0.assert_nothing_captured(remark="IP packets forwarded on pg0") # Check that all packets were forwarded via pg1, pg2 and pg3 self.assertEqual(rx_count, len(pkts)) if __name__ == "__main__": unittest.main(testRunner=VppTestRunner)