#!/usr/bin/env python
-
-import unittest
+import random
import socket
+import unittest
from framework import VppTestCase, VppTestRunner
from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint
+from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpMRoute, \
+ VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind
from scapy.packet import Raw
-from scapy.layers.l2 import Ether, Dot1Q
-from scapy.layers.inet import IP, UDP
+from scapy.layers.l2 import Ether, Dot1Q, ARP
+from scapy.layers.inet import IP, UDP, ICMP, icmptypes, icmpcodes
from util import ppp
+from scapy.contrib.mpls import MPLS
class TestIPv4(VppTestCase):
pkts = []
for i in range(0, 257):
dst_if = self.flows[src_if][i % 2]
- info = self.create_packet_info(
- src_if.sw_if_index, dst_if.sw_if_index)
+ info = self.create_packet_info(src_if, dst_if)
payload = self.info_to_payload(info)
p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) /
payload_info = self.payload_to_info(str(packet[Raw]))
packet_index = payload_info.index
self.assertEqual(payload_info.dst, dst_sw_if_index)
- self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
- (dst_if.name, payload_info.src, packet_index))
+ self.logger.debug(
+ "Got packet on port %s: src=%u (id=%u)" %
+ (dst_if.name, payload_info.src, packet_index))
next_info = self.get_next_packet_info_for_interface2(
payload_info.src, dst_sw_if_index,
last_info[payload_info.src])
self.verify_capture(i, pkts)
+class TestIPv4FibCrud(VppTestCase):
+ """ FIB - add/update/delete - ip4 routes
+
+ Test scenario:
+ - add 1k,
+ - del 100,
+ - add new 1k,
+ - del 1.5k
+
+ ..note:: Python API is too slow to add many routes, needs replacement.
+ """
+
+ def config_fib_many_to_one(self, start_dest_addr, next_hop_addr, count):
+ """
+
+ :param start_dest_addr:
+ :param next_hop_addr:
+ :param count:
+ :return list: added ips with 32 prefix
+ """
+ added_ips = []
+ dest_addr = int(socket.inet_pton(socket.AF_INET,
+ start_dest_addr).encode('hex'),
+ 16)
+ dest_addr_len = 32
+ n_next_hop_addr = socket.inet_pton(socket.AF_INET, next_hop_addr)
+ for _ in range(count):
+ n_dest_addr = '{:08x}'.format(dest_addr).decode('hex')
+ self.vapi.ip_add_del_route(n_dest_addr, dest_addr_len,
+ n_next_hop_addr)
+ added_ips.append(socket.inet_ntoa(n_dest_addr))
+ dest_addr += 1
+ return added_ips
+
+ def unconfig_fib_many_to_one(self, start_dest_addr, next_hop_addr, count):
+
+ removed_ips = []
+ dest_addr = int(socket.inet_pton(socket.AF_INET,
+ start_dest_addr).encode('hex'),
+ 16)
+ dest_addr_len = 32
+ n_next_hop_addr = socket.inet_pton(socket.AF_INET, next_hop_addr)
+ for _ in range(count):
+ n_dest_addr = '{:08x}'.format(dest_addr).decode('hex')
+ self.vapi.ip_add_del_route(n_dest_addr, dest_addr_len,
+ n_next_hop_addr, is_add=0)
+ removed_ips.append(socket.inet_ntoa(n_dest_addr))
+ dest_addr += 1
+ return removed_ips
+
+ def create_stream(self, src_if, dst_if, dst_ips, count):
+ pkts = []
+
+ for _ in range(count):
+ dst_addr = random.choice(dst_ips)
+ info = self.create_packet_info(src_if, dst_if)
+ payload = self.info_to_payload(info)
+ p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
+ IP(src=src_if.remote_ip4, dst=dst_addr) /
+ UDP(sport=1234, dport=1234) /
+ Raw(payload))
+ info.data = p.copy()
+ self.extend_packet(p, random.choice(self.pg_if_packet_sizes))
+ pkts.append(p)
+
+ return pkts
+
+ def _find_ip_match(self, find_in, pkt):
+ for p in find_in:
+ if self.payload_to_info(str(p[Raw])) == \
+ self.payload_to_info(str(pkt[Raw])):
+ if p[IP].src != pkt[IP].src:
+ break
+ if p[IP].dst != pkt[IP].dst:
+ break
+ if p[UDP].sport != pkt[UDP].sport:
+ break
+ if p[UDP].dport != pkt[UDP].dport:
+ break
+ return p
+ return None
+
+ @staticmethod
+ def _match_route_detail(route_detail, ip, address_length=32, table_id=0):
+ if route_detail.address == socket.inet_pton(socket.AF_INET, ip):
+ if route_detail.table_id != table_id:
+ return False
+ elif route_detail.address_length != address_length:
+ return False
+ else:
+ return True
+ else:
+ return False
+
+ def verify_capture(self, dst_interface, received_pkts, expected_pkts):
+ self.assertEqual(len(received_pkts), len(expected_pkts))
+ to_verify = list(expected_pkts)
+ for p in received_pkts:
+ self.assertEqual(p.src, dst_interface.local_mac)
+ self.assertEqual(p.dst, dst_interface.remote_mac)
+ x = self._find_ip_match(to_verify, p)
+ to_verify.remove(x)
+ self.assertListEqual(to_verify, [])
+
+ def verify_route_dump(self, fib_dump, ips):
+
+ def _ip_in_route_dump(ip, fib_dump):
+ return next((route for route in fib_dump
+ if self._match_route_detail(route, ip)),
+ False)
+
+ for ip in ips:
+ self.assertTrue(_ip_in_route_dump(ip, fib_dump),
+ 'IP {} is not in fib dump.'.format(ip))
+
+ def verify_not_in_route_dump(self, fib_dump, ips):
+
+ def _ip_in_route_dump(ip, fib_dump):
+ return next((route for route in fib_dump
+ if self._match_route_detail(route, ip)),
+ False)
+
+ for ip in ips:
+ self.assertFalse(_ip_in_route_dump(ip, fib_dump),
+ 'IP {} is in fib dump.'.format(ip))
+
+ @classmethod
+ def setUpClass(cls):
+ """
+ #. Create and initialize 3 pg interfaces.
+ #. initialize class attributes configured_routes and deleted_routes
+ to store information between tests.
+ """
+ super(TestIPv4FibCrud, cls).setUpClass()
+
+ try:
+ # create 3 pg interfaces
+ cls.create_pg_interfaces(range(3))
+
+ cls.interfaces = list(cls.pg_interfaces)
+
+ # setup all interfaces
+ for i in cls.interfaces:
+ i.admin_up()
+ i.config_ip4()
+ i.resolve_arp()
+
+ cls.configured_routes = []
+ cls.deleted_routes = []
+ cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
+
+ except Exception:
+ super(TestIPv4FibCrud, cls).tearDownClass()
+ raise
+
+ def setUp(self):
+ super(TestIPv4FibCrud, self).setUp()
+ self.reset_packet_infos()
+
+ def test_1_add_routes(self):
+ """ Add 1k routes
+
+ - add 100 routes check with traffic script.
+ """
+ # config 1M FIB entries
+ self.configured_routes.extend(self.config_fib_many_to_one(
+ "10.0.0.0", self.pg0.remote_ip4, 100))
+
+ fib_dump = self.vapi.ip_fib_dump()
+ self.verify_route_dump(fib_dump, self.configured_routes)
+
+ self.stream_1 = self.create_stream(
+ self.pg1, self.pg0, self.configured_routes, 100)
+ self.stream_2 = self.create_stream(
+ self.pg2, self.pg0, self.configured_routes, 100)
+ self.pg1.add_stream(self.stream_1)
+ self.pg2.add_stream(self.stream_2)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
+ self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
+
+ def test_2_del_routes(self):
+ """ Delete 100 routes
+
+ - delete 10 routes check with traffic script.
+ """
+ self.deleted_routes.extend(self.unconfig_fib_many_to_one(
+ "10.0.0.10", self.pg0.remote_ip4, 10))
+ for x in self.deleted_routes:
+ self.configured_routes.remove(x)
+
+ fib_dump = self.vapi.ip_fib_dump()
+ self.verify_route_dump(fib_dump, self.configured_routes)
+
+ self.stream_1 = self.create_stream(
+ self.pg1, self.pg0, self.configured_routes, 100)
+ self.stream_2 = self.create_stream(
+ self.pg2, self.pg0, self.configured_routes, 100)
+ self.stream_3 = self.create_stream(
+ self.pg1, self.pg0, self.deleted_routes, 100)
+ self.stream_4 = self.create_stream(
+ self.pg2, self.pg0, self.deleted_routes, 100)
+ self.pg1.add_stream(self.stream_1 + self.stream_3)
+ self.pg2.add_stream(self.stream_2 + self.stream_4)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
+ self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
+
+ def test_3_add_new_routes(self):
+ """ Add 1k routes
+
+ - re-add 5 routes check with traffic script.
+ - add 100 routes check with traffic script.
+ """
+ tmp = self.config_fib_many_to_one(
+ "10.0.0.10", self.pg0.remote_ip4, 5)
+ self.configured_routes.extend(tmp)
+ for x in tmp:
+ self.deleted_routes.remove(x)
+
+ self.configured_routes.extend(self.config_fib_many_to_one(
+ "10.0.1.0", self.pg0.remote_ip4, 100))
+
+ fib_dump = self.vapi.ip_fib_dump()
+ self.verify_route_dump(fib_dump, self.configured_routes)
+
+ self.stream_1 = self.create_stream(
+ self.pg1, self.pg0, self.configured_routes, 300)
+ self.stream_2 = self.create_stream(
+ self.pg2, self.pg0, self.configured_routes, 300)
+ self.stream_3 = self.create_stream(
+ self.pg1, self.pg0, self.deleted_routes, 100)
+ self.stream_4 = self.create_stream(
+ self.pg2, self.pg0, self.deleted_routes, 100)
+
+ self.pg1.add_stream(self.stream_1 + self.stream_3)
+ self.pg2.add_stream(self.stream_2 + self.stream_4)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
+ self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
+
+ def test_4_del_routes(self):
+ """ Delete 1.5k routes
+
+ - delete 5 routes check with traffic script.
+ - add 100 routes check with traffic script.
+ """
+ self.deleted_routes.extend(self.unconfig_fib_many_to_one(
+ "10.0.0.0", self.pg0.remote_ip4, 15))
+ self.deleted_routes.extend(self.unconfig_fib_many_to_one(
+ "10.0.0.20", self.pg0.remote_ip4, 85))
+ self.deleted_routes.extend(self.unconfig_fib_many_to_one(
+ "10.0.1.0", self.pg0.remote_ip4, 100))
+ fib_dump = self.vapi.ip_fib_dump()
+ self.verify_not_in_route_dump(fib_dump, self.deleted_routes)
+
+
+class TestIPNull(VppTestCase):
+ """ IPv4 routes via NULL """
+
+ def setUp(self):
+ super(TestIPNull, self).setUp()
+
+ # create 2 pg interfaces
+ self.create_pg_interfaces(range(1))
+
+ for i in self.pg_interfaces:
+ i.admin_up()
+ i.config_ip4()
+ i.resolve_arp()
+
+ def tearDown(self):
+ super(TestIPNull, self).tearDown()
+ for i in self.pg_interfaces:
+ i.unconfig_ip4()
+ i.admin_down()
+
+ def test_ip_null(self):
+ """ IP NULL route """
+
+ #
+ # A route via IP NULL that will reply with ICMP unreachables
+ #
+ ip_unreach = VppIpRoute(self, "10.0.0.1", 32, [], is_unreach=1)
+ ip_unreach.add_vpp_config()
+
+ p_unreach = (Ether(src=self.pg0.remote_mac,
+ dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst="10.0.0.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ self.pg0.add_stream(p_unreach)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg0.get_capture(1)
+ rx = rx[0]
+ icmp = rx[ICMP]
+
+ self.assertEqual(icmptypes[icmp.type], "dest-unreach")
+ self.assertEqual(icmpcodes[icmp.type][icmp.code], "host-unreachable")
+ self.assertEqual(icmp.src, self.pg0.remote_ip4)
+ self.assertEqual(icmp.dst, "10.0.0.1")
+
+ #
+ # ICMP replies are rate limited. so sit and spin.
+ #
+ self.sleep(1)
+
+ #
+ # A route via IP NULL that will reply with ICMP prohibited
+ #
+ ip_prohibit = VppIpRoute(self, "10.0.0.2", 32, [], is_prohibit=1)
+ ip_prohibit.add_vpp_config()
+
+ p_prohibit = (Ether(src=self.pg0.remote_mac,
+ dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst="10.0.0.2") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ self.pg0.add_stream(p_prohibit)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg0.get_capture(1)
+
+ rx = rx[0]
+ icmp = rx[ICMP]
+
+ self.assertEqual(icmptypes[icmp.type], "dest-unreach")
+ self.assertEqual(icmpcodes[icmp.type][icmp.code], "host-prohibited")
+ self.assertEqual(icmp.src, self.pg0.remote_ip4)
+ self.assertEqual(icmp.dst, "10.0.0.2")
+
+
+class TestIPDisabled(VppTestCase):
+ """ IPv4 disabled """
+
+ def setUp(self):
+ super(TestIPDisabled, self).setUp()
+
+ # create 2 pg interfaces
+ self.create_pg_interfaces(range(2))
+
+ # PG0 is IP enalbed
+ self.pg0.admin_up()
+ self.pg0.config_ip4()
+ self.pg0.resolve_arp()
+
+ # PG 1 is not IP enabled
+ self.pg1.admin_up()
+
+ def tearDown(self):
+ super(TestIPDisabled, self).tearDown()
+ for i in self.pg_interfaces:
+ i.unconfig_ip4()
+ i.admin_down()
+
+ def send_and_assert_no_replies(self, intf, pkts, remark):
+ intf.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ for i in self.pg_interfaces:
+ i.get_capture(0)
+ i.assert_nothing_captured(remark=remark)
+
+ def test_ip_disabled(self):
+ """ IP Disabled """
+
+ #
+ # An (S,G).
+ # one accepting interface, pg0, 2 forwarding interfaces
+ #
+ route_232_1_1_1 = VppIpMRoute(
+ self,
+ "0.0.0.0",
+ "232.1.1.1", 32,
+ MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
+ [VppMRoutePath(self.pg1.sw_if_index,
+ MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
+ VppMRoutePath(self.pg0.sw_if_index,
+ MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
+ route_232_1_1_1.add_vpp_config()
+
+ pu = (Ether(src=self.pg1.remote_mac,
+ dst=self.pg1.local_mac) /
+ IP(src="10.10.10.10", dst=self.pg0.remote_ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+ pm = (Ether(src=self.pg1.remote_mac,
+ dst=self.pg1.local_mac) /
+ IP(src="10.10.10.10", dst="232.1.1.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ #
+ # PG1 does not forward IP traffic
+ #
+ self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
+ self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
+
+ #
+ # IP enable PG1
+ #
+ self.pg1.config_ip4()
+
+ #
+ # Now we get packets through
+ #
+ self.pg1.add_stream(pu)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ rx = self.pg0.get_capture(1)
+
+ self.pg1.add_stream(pm)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ rx = self.pg0.get_capture(1)
+
+ #
+ # Disable PG1
+ #
+ self.pg1.unconfig_ip4()
+
+ #
+ # PG1 does not forward IP traffic
+ #
+ self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
+ self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
+
+
+class TestIPSubNets(VppTestCase):
+ """ IPv4 Subnets """
+
+ def setUp(self):
+ super(TestIPSubNets, self).setUp()
+
+ # create a 2 pg interfaces
+ self.create_pg_interfaces(range(2))
+
+ # pg0 we will use to experiemnt
+ self.pg0.admin_up()
+
+ # pg1 is setup normally
+ self.pg1.admin_up()
+ self.pg1.config_ip4()
+ self.pg1.resolve_arp()
+
+ def tearDown(self):
+ super(TestIPSubNets, self).tearDown()
+ for i in self.pg_interfaces:
+ i.admin_down()
+
+ def send_and_assert_no_replies(self, intf, pkts, remark):
+ intf.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ for i in self.pg_interfaces:
+ i.get_capture(0)
+ i.assert_nothing_captured(remark=remark)
+
+ def test_ip_sub_nets(self):
+ """ IP Sub Nets """
+
+ #
+ # Configure a covering route to forward so we know
+ # when we are dropping
+ #
+ cover_route = VppIpRoute(self, "10.0.0.0", 8,
+ [VppRoutePath(self.pg1.remote_ip4,
+ self.pg1.sw_if_index)])
+ cover_route.add_vpp_config()
+
+ p = (Ether(src=self.pg1.remote_mac,
+ dst=self.pg1.local_mac) /
+ IP(dst="10.10.10.10", src=self.pg0.local_ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ self.pg1.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ rx = self.pg1.get_capture(1)
+
+ #
+ # Configure some non-/24 subnets on an IP interface
+ #
+ ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
+
+ self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
+ ip_addr_n,
+ 16)
+
+ pn = (Ether(src=self.pg1.remote_mac,
+ dst=self.pg1.local_mac) /
+ IP(dst="10.10.0.0", src=self.pg0.local_ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+ pb = (Ether(src=self.pg1.remote_mac,
+ dst=self.pg1.local_mac) /
+ IP(dst="10.10.255.255", src=self.pg0.local_ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ self.send_and_assert_no_replies(self.pg1, pn, "IP Network address")
+ self.send_and_assert_no_replies(self.pg1, pb, "IP Broadcast address")
+
+ # remove the sub-net and we are forwarding via the cover again
+ self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
+ ip_addr_n,
+ 16,
+ is_add=0)
+ self.pg1.add_stream(pn)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ rx = self.pg1.get_capture(1)
+ self.pg1.add_stream(pb)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ rx = self.pg1.get_capture(1)
+
+ #
+ # A /31 is a special case where the 'other-side' is an attached host
+ # packets to that peer generate ARP requests
+ #
+ ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
+
+ self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
+ ip_addr_n,
+ 31)
+
+ pn = (Ether(src=self.pg1.remote_mac,
+ dst=self.pg1.local_mac) /
+ IP(dst="10.10.10.11", src=self.pg0.local_ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ self.pg1.add_stream(pn)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ rx = self.pg0.get_capture(1)
+ rx[ARP]
+
+ # remove the sub-net and we are forwarding via the cover again
+ self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
+ ip_addr_n,
+ 31,
+ is_add=0)
+ self.pg1.add_stream(pn)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ rx = self.pg1.get_capture(1)
+
+
+class TestIPLoadBalance(VppTestCase):
+ """ IPv4 Load-Balancing """
+
+ def setUp(self):
+ super(TestIPLoadBalance, self).setUp()
+
+ self.create_pg_interfaces(range(5))
+
+ for i in self.pg_interfaces:
+ i.admin_up()
+ i.config_ip4()
+ i.resolve_arp()
+ i.enable_mpls()
+
+ def tearDown(self):
+ super(TestIPLoadBalance, self).tearDown()
+ for i in self.pg_interfaces:
+ i.disable_mpls()
+ i.unconfig_ip4()
+ i.admin_down()
+
+ def send_and_expect_load_balancing(self, input, pkts, outputs):
+ input.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ for oo in outputs:
+ rx = oo._get_capture(1)
+ self.assertNotEqual(0, len(rx))
+
+ def test_ip_load_balance(self):
+ """ IP Load-Balancing """
+
+ #
+ # An array of packets that differ only in the destination port
+ #
+ port_ip_pkts = []
+ port_mpls_pkts = []
+
+ #
+ # An array of packets that differ only in the source address
+ #
+ src_ip_pkts = []
+ src_mpls_pkts = []
+
+ for ii in range(65):
+ port_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.1") /
+ UDP(sport=1234, dport=1234 + ii) /
+ Raw('\xa5' * 100))
+ port_ip_pkts.append((Ether(src=self.pg0.remote_mac,
+ dst=self.pg0.local_mac) /
+ port_ip_hdr))
+ port_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
+ dst=self.pg0.local_mac) /
+ MPLS(label=66, ttl=2) /
+ port_ip_hdr))
+
+ src_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.%d" % ii) /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+ src_ip_pkts.append((Ether(src=self.pg0.remote_mac,
+ dst=self.pg0.local_mac) /
+ src_ip_hdr))
+ src_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
+ dst=self.pg0.local_mac) /
+ MPLS(label=66, ttl=2) /
+ src_ip_hdr))
+
+ route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
+ [VppRoutePath(self.pg1.remote_ip4,
+ self.pg1.sw_if_index),
+ VppRoutePath(self.pg2.remote_ip4,
+ self.pg2.sw_if_index)])
+ route_10_0_0_1.add_vpp_config()
+
+ binding = VppMplsIpBind(self, 66, "10.0.0.1", 32)
+ binding.add_vpp_config()
+
+ #
+ # inject the packet on pg0 - expect load-balancing across the 2 paths
+ # - since the default hash config is to use IP src,dst and port
+ # src,dst
+ # We are not going to ensure equal amounts of packets across each link,
+ # since the hash algorithm is statistical and therefore this can never
+ # be guaranteed. But wuth 64 different packets we do expect some
+ # balancing. So instead just ensure there is traffic on each link.
+ #
+ self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
+ [self.pg1, self.pg2])
+ self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
+ [self.pg1, self.pg2])
+ self.send_and_expect_load_balancing(self.pg0, port_mpls_pkts,
+ [self.pg1, self.pg2])
+ self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
+ [self.pg1, self.pg2])
+
+ #
+ # change the flow hash config so it's only IP src,dst
+ # - now only the stream with differing source address will
+ # load-balance
+ #
+ self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=0, dport=0)
+
+ self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
+ [self.pg1, self.pg2])
+ self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
+ [self.pg1, self.pg2])
+
+ self.pg0.add_stream(port_ip_pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg2.get_capture(len(port_ip_pkts))
+
+ #
+ # change the flow hash config back to defaults
+ #
+ self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=1, dport=1)
+
+ #
+ # Recursive prefixes
+ # - testing that 2 stages of load-balancing occurs and there is no
+ # polarisation (i.e. only 2 of 4 paths are used)
+ #
+ port_pkts = []
+ src_pkts = []
+
+ for ii in range(257):
+ port_pkts.append((Ether(src=self.pg0.remote_mac,
+ dst=self.pg0.local_mac) /
+ IP(dst="1.1.1.1", src="20.0.0.1") /
+ UDP(sport=1234, dport=1234 + ii) /
+ Raw('\xa5' * 100)))
+ src_pkts.append((Ether(src=self.pg0.remote_mac,
+ dst=self.pg0.local_mac) /
+ IP(dst="1.1.1.1", src="20.0.0.%d" % ii) /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100)))
+
+ route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
+ [VppRoutePath(self.pg3.remote_ip4,
+ self.pg3.sw_if_index),
+ VppRoutePath(self.pg4.remote_ip4,
+ self.pg4.sw_if_index)])
+ route_10_0_0_2.add_vpp_config()
+
+ route_1_1_1_1 = VppIpRoute(self, "1.1.1.1", 32,
+ [VppRoutePath("10.0.0.2", 0xffffffff),
+ VppRoutePath("10.0.0.1", 0xffffffff)])
+ route_1_1_1_1.add_vpp_config()
+
+ #
+ # inject the packet on pg0 - expect load-balancing across all 4 paths
+ #
+ self.vapi.cli("clear trace")
+ self.send_and_expect_load_balancing(self.pg0, port_pkts,
+ [self.pg1, self.pg2,
+ self.pg3, self.pg4])
+ self.send_and_expect_load_balancing(self.pg0, src_pkts,
+ [self.pg1, self.pg2,
+ self.pg3, self.pg4])
+
+
+class TestIPVlan0(VppTestCase):
+ """ IPv4 VLAN-0 """
+
+ def setUp(self):
+ super(TestIPVlan0, self).setUp()
+
+ self.create_pg_interfaces(range(2))
+
+ for i in self.pg_interfaces:
+ i.admin_up()
+ i.config_ip4()
+ i.resolve_arp()
+ i.enable_mpls()
+
+ def tearDown(self):
+ super(TestIPVlan0, self).tearDown()
+ for i in self.pg_interfaces:
+ i.disable_mpls()
+ i.unconfig_ip4()
+ i.admin_down()
+
+ def send_and_expect(self, input, pkts, output):
+ input.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ rx = output.get_capture(len(pkts))
+
+ def test_ip_vlan_0(self):
+ """ IP VLAN-0 """
+
+ pkts = (Ether(src=self.pg0.remote_mac,
+ dst=self.pg0.local_mac) /
+ Dot1Q(vlan=0) /
+ IP(dst=self.pg1.remote_ip4,
+ src=self.pg0.remote_ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100)) * 65
+
+ #
+ # Expect that packets sent on VLAN-0 are forwarded on the
+ # main interface.
+ #
+ self.send_and_expect(self.pg0, pkts, self.pg1)
+
+
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)