summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatej Klotton <mklotton@cisco.com>2016-11-04 11:11:44 +0100
committerDamjan Marion <dmarion.lists@gmail.com>2016-11-11 10:29:04 +0000
commit0178d52384e0428368f1acf3163e664ecda7b64c (patch)
tree8f6e09a23ca9b332df92f0ac16cc7d2526d7e4b6
parent9db551cf50da13bf82d3a735ea6b56912a97d5c6 (diff)
Add IRB test
- JIRA: CSIT-255 - create loopback interfaces - move pg-interface specific arp and neighbor discovery from vpp_interface to vpp_pg_interface - base configuration of IRB tests - IP test scenario Change-Id: I9945a188163652a4e22325877aef008c4d029557 Signed-off-by: Matej Klotton <mklotton@cisco.com>
-rw-r--r--test/framework.py19
-rw-r--r--test/test_ip4_irb.py247
-rw-r--r--test/test_l2bd.py4
-rw-r--r--test/test_l2xc.py4
-rw-r--r--test/util.py12
-rw-r--r--test/vpp_interface.py155
-rw-r--r--test/vpp_lo_interface.py16
-rw-r--r--test/vpp_papi_provider.py39
-rw-r--r--test/vpp_pg_interface.py91
-rw-r--r--test/vpp_sub_interface.py15
10 files changed, 483 insertions, 119 deletions
diff --git a/test/framework.py b/test/framework.py
index b10592cf1e7..8dbc18fda4f 100644
--- a/test/framework.py
+++ b/test/framework.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-from abc import *
-import os
import subprocess
import unittest
import tempfile
@@ -13,6 +11,7 @@ from threading import Thread
from inspect import getdoc
from hook import StepHook, PollHook
from vpp_pg_interface import VppPGInterface
+from vpp_lo_interface import VppLoInterface
from vpp_papi_provider import VppPapiProvider
from scapy.packet import Raw
from log import *
@@ -330,6 +329,22 @@ class VppTestCase(unittest.TestCase):
cls.pg_interfaces = result
return result
+ @classmethod
+ def create_loopback_interfaces(cls, interfaces):
+ """
+ Create loopback interfaces
+
+ :param interfaces: iterable indexes of the interfaces
+
+ """
+ result = []
+ for i in interfaces:
+ intf = VppLoInterface(cls, i)
+ setattr(cls, intf.name, intf)
+ result.append(intf)
+ cls.lo_interfaces = result
+ return result
+
@staticmethod
def extend_packet(packet, size):
"""
diff --git a/test/test_ip4_irb.py b/test/test_ip4_irb.py
new file mode 100644
index 00000000000..412575dbd3c
--- /dev/null
+++ b/test/test_ip4_irb.py
@@ -0,0 +1,247 @@
+#!/usr/bin/env python
+import unittest
+from random import choice, randint
+
+from scapy.packet import Raw
+from scapy.layers.l2 import Ether
+from scapy.layers.inet import IP, UDP
+from logging import *
+
+from framework import VppTestCase, VppTestRunner
+
+""" IRB Test Case
+
+config
+ L2 MAC learning enabled in l2bd
+ 2 routed interfaces untagged, bvi
+ 2 bridged interfaces in l2bd with bvi
+test
+ sending ip4 eth pkts between routed interfaces
+ 2 routed interfaces
+ 2 bridged interfaces
+ 64B, 512B, 1518B, 9200B (ether_size)
+ burst of pkts per interface
+ 257pkts per burst
+ routed pkts hitting different FIB entries
+ bridged pkts hitting different MAC entries
+verify
+ all packets received correctly
+"""
+
+
+class TestIpIrb(VppTestCase):
+ """ IRB Test Case """
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestIpIrb, cls).setUpClass()
+
+ cls.pg_if_packet_sizes = [64, 512, 1518, 9018] # packet sizes
+ cls.bd_id = 10
+ cls.remote_hosts_count = 250
+
+ # create 3 pg interfaces, 1 loopback interface
+ cls.create_pg_interfaces(range(3))
+ cls.create_loopback_interfaces(range(1))
+
+ cls.interfaces = list(cls.pg_interfaces)
+ cls.interfaces.extend(cls.lo_interfaces)
+
+ for i in cls.interfaces:
+ i.admin_up()
+
+ # Create BD with MAC learning enabled and put interfaces to this BD
+ cls.vapi.sw_interface_set_l2_bridge(
+ cls.loop0.sw_if_index, bd_id=cls.bd_id, bvi=1)
+ cls.vapi.sw_interface_set_l2_bridge(
+ cls.pg0.sw_if_index, bd_id=cls.bd_id)
+ cls.vapi.sw_interface_set_l2_bridge(
+ cls.pg1.sw_if_index, bd_id=cls.bd_id)
+
+ cls.loop0.config_ip4()
+ cls.pg2.config_ip4()
+
+ # configure MAC address binding to IPv4 neighbors on loop0
+ cls.loop0.generate_remote_hosts(cls.remote_hosts_count)
+ cls.loop0.configure_extend_ipv4_mac_binding()
+ # configure MAC address on pg2
+ cls.pg2.resolve_arp()
+
+ # one half of hosts are behind pg0 second behind pg1
+ half = cls.remote_hosts_count // 2
+ cls.pg0.remote_hosts = cls.loop0.remote_hosts[:half]
+ cls.pg1.remote_hosts = cls.loop0.remote_hosts[half:]
+
+ def tearDown(self):
+ super(TestIpIrb, self).tearDown()
+ if not self.vpp_dead:
+ info(self.vapi.cli("show l2patch"))
+ info(self.vapi.cli("show l2fib verbose"))
+ info(self.vapi.cli("show bridge-domain %s detail" % self.bd_id))
+ info(self.vapi.cli("show ip arp"))
+
+ def create_stream(self, src_ip_if, dst_ip_if, packet_sizes):
+ pkts = []
+ for i in range(0, 257):
+ remote_dst_host = choice(dst_ip_if.remote_hosts)
+ info = self.create_packet_info(
+ src_ip_if.sw_if_index, dst_ip_if.sw_if_index)
+ payload = self.info_to_payload(info)
+ p = (Ether(dst=src_ip_if.local_mac, src=src_ip_if.remote_mac) /
+ IP(src=src_ip_if.remote_ip4,
+ dst=remote_dst_host.ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw(payload))
+ info.data = p.copy()
+ size = packet_sizes[(i // 2) % len(packet_sizes)]
+ self.extend_packet(p, size)
+ pkts.append(p)
+ return pkts
+
+ def create_stream_l2_to_ip(self, src_l2_if, src_ip_if, dst_ip_if,
+ packet_sizes):
+ pkts = []
+ for i in range(0, 257):
+ info = self.create_packet_info(
+ src_ip_if.sw_if_index, dst_ip_if.sw_if_index)
+ payload = self.info_to_payload(info)
+
+ host = choice(src_l2_if.remote_hosts)
+
+ p = (Ether(src=host.mac,
+ dst = src_ip_if.local_mac) /
+ IP(src=host.ip4,
+ dst=dst_ip_if.remote_ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw(payload))
+
+ info.data = p.copy()
+ size = packet_sizes[(i // 2) % len(packet_sizes)]
+ self.extend_packet(p, size)
+
+ pkts.append(p)
+ return pkts
+
+ def verify_capture_l2_to_ip(self, dst_ip_if, src_ip_if, capture):
+ last_info = dict()
+ for i in self.interfaces:
+ last_info[i.sw_if_index] = None
+
+ dst_ip_sw_if_index = dst_ip_if.sw_if_index
+
+ for packet in capture:
+ ip = packet[IP]
+ udp = packet[IP][UDP]
+ payload_info = self.payload_to_info(str(packet[IP][UDP][Raw]))
+ packet_index = payload_info.index
+
+ self.assertEqual(payload_info.dst, dst_ip_sw_if_index)
+
+ next_info = self.get_next_packet_info_for_interface2(
+ payload_info.src, dst_ip_sw_if_index,
+ last_info[payload_info.src])
+ last_info[payload_info.src] = next_info
+ self.assertTrue(next_info is not None)
+ saved_packet = next_info.data
+ self.assertTrue(next_info is not None)
+
+ # MAC: src, dst
+ self.assertEqual(packet.src, dst_ip_if.local_mac)
+ self.assertEqual(packet.dst, dst_ip_if.remote_mac)
+
+ # IP: src, dst
+ host = src_ip_if.host_by_ip4(ip.src)
+ self.assertIsNotNone(host)
+ self.assertEqual(ip.dst, saved_packet[IP].dst)
+ self.assertEqual(ip.dst, dst_ip_if.remote_ip4)
+
+ # UDP:
+ self.assertEqual(udp.sport, saved_packet[UDP].sport)
+ self.assertEqual(udp.dport, saved_packet[UDP].dport)
+
+ def verify_capture(self, dst_ip_if, src_ip_if, capture):
+ last_info = dict()
+ for i in self.interfaces:
+ last_info[i.sw_if_index] = None
+
+ dst_ip_sw_if_index = dst_ip_if.sw_if_index
+
+ for packet in capture:
+ ip = packet[IP]
+ udp = packet[IP][UDP]
+ payload_info = self.payload_to_info(str(packet[IP][UDP][Raw]))
+ packet_index = payload_info.index
+
+ self.assertEqual(payload_info.dst, dst_ip_sw_if_index)
+
+ next_info = self.get_next_packet_info_for_interface2(
+ payload_info.src, dst_ip_sw_if_index,
+ last_info[payload_info.src])
+ last_info[payload_info.src] = next_info
+ self.assertTrue(next_info is not None)
+ self.assertEqual(packet_index, next_info.index)
+ saved_packet = next_info.data
+ self.assertTrue(next_info is not None)
+
+ # MAC: src, dst
+ self.assertEqual(packet.src, dst_ip_if.local_mac)
+ host = dst_ip_if.host_by_mac(packet.dst)
+
+ # IP: src, dst
+ self.assertEqual(ip.src, src_ip_if.remote_ip4)
+ self.assertEqual(ip.dst, saved_packet[IP].dst)
+ self.assertEqual(ip.dst, host.ip4)
+
+ # UDP:
+ self.assertEqual(udp.sport, saved_packet[UDP].sport)
+ self.assertEqual(udp.dport, saved_packet[UDP].dport)
+
+ def test_ip4_irb_1(self):
+ """ IPv4 IRB test 1
+
+ Test scenario:
+ ip traffic from pg2 interface must ends in both pg0 and pg1
+ - arp entry present in loop0 interface for dst IP
+ - no l2 entree configured, pg0 and pg1 are same
+ """
+
+ stream = self.create_stream(
+ self.pg2, self.loop0, self.pg_if_packet_sizes)
+ self.pg2.add_stream(stream)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rcvd1 = self.pg0.get_capture()
+ rcvd2 = self.pg1.get_capture()
+
+ self.verify_capture(self.loop0, self.pg2, rcvd1)
+ self.verify_capture(self.loop0, self.pg2, rcvd2)
+
+ self.assertListEqual(rcvd1.res, rcvd2.res)
+
+ def test_ip4_irb_2(self):
+ """ IPv4 IRB test 2
+
+ Test scenario:
+ ip traffic from pg0 and pg1 ends on pg2
+ """
+
+ stream1 = self.create_stream_l2_to_ip(
+ self.pg0, self.loop0, self.pg2, self.pg_if_packet_sizes)
+ stream2 = self.create_stream_l2_to_ip(
+ self.pg1, self.loop0, self.pg2, self.pg_if_packet_sizes)
+ self.pg0.add_stream(stream1)
+ self.pg1.add_stream(stream2)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rcvd = self.pg2.get_capture()
+ self.verify_capture_l2_to_ip(self.pg2, self.loop0, rcvd)
+
+ self.assertEqual(len(stream1) + len(stream2), len(rcvd.res))
+
+
+if __name__ == '__main__':
+ unittest.main(testRunner=VppTestRunner)
diff --git a/test/test_l2bd.py b/test/test_l2bd.py
index 53e1ee053ab..3c65cc1e0a8 100644
--- a/test/test_l2bd.py
+++ b/test/test_l2bd.py
@@ -10,7 +10,7 @@ from scapy.layers.inet import IP, UDP
from framework import VppTestCase, VppTestRunner
from vpp_sub_interface import VppDot1QSubint
-from util import TestHost
+from util import Host
class TestL2bd(VppTestCase):
@@ -100,7 +100,7 @@ class TestL2bd(VppTestCase):
hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
packets = []
for j in range(start_nr, end_nr):
- host = TestHost(
+ host = Host(
"00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
"172.17.1%02x.%u" % (pg_if.sw_if_index, j))
packet = (Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac))
diff --git a/test/test_l2xc.py b/test/test_l2xc.py
index 35c7a818414..23fd757760f 100644
--- a/test/test_l2xc.py
+++ b/test/test_l2xc.py
@@ -9,7 +9,7 @@ from scapy.layers.inet import IP, UDP
from logging import *
from framework import VppTestCase, VppTestRunner
-from util import TestHost
+from util import Host
class TestL2xc(VppTestCase):
@@ -85,7 +85,7 @@ class TestL2xc(VppTestCase):
self.hosts_by_pg_idx[pg_if.sw_if_index] = []
hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
for j in range(0, count):
- host = TestHost(
+ host = Host(
"00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
"172.17.1%02x.%u" % (pg_if.sw_if_index, j))
hosts.append(host)
diff --git a/test/util.py b/test/util.py
index 8d7c9202a41..6e7e275cddc 100644
--- a/test/util.py
+++ b/test/util.py
@@ -1,8 +1,7 @@
-from logging import *
+import socket
-
-class TestHost(object):
- """ Generic test host "connected" to VPP. """
+class Host(object):
+ """ Generic test host "connected" to VPPs interface. """
@property
def mac(self):
@@ -15,6 +14,11 @@ class TestHost(object):
return self._ip4
@property
+ def ip4n(self):
+ """ IPv4 address """
+ return socket.inet_pton(socket.AF_INET, self._ip4)
+
+ @property
def ip6(self):
""" IPv6 address """
return self._ip6
diff --git a/test/vpp_interface.py b/test/vpp_interface.py
index 509ab952236..d74248a39b3 100644
--- a/test/vpp_interface.py
+++ b/test/vpp_interface.py
@@ -1,9 +1,8 @@
from abc import abstractmethod, ABCMeta
import socket
-from logging import info, error
-from scapy.layers.l2 import Ether, ARP
+from logging import info
-from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6ND_NA, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptDstLLAddr
+from util import Host
class VppInterface(object):
@@ -20,7 +19,7 @@ class VppInterface(object):
@property
def remote_mac(self):
"""MAC-address of the remote interface "connected" to this interface"""
- return self._remote_mac
+ return self._remote_hosts[0].mac
@property
def local_mac(self):
@@ -35,17 +34,17 @@ class VppInterface(object):
@property
def local_ip4n(self):
"""Local IPv4 address - raw, suitable as API parameter"""
- return self._local_ip4n
+ return socket.inet_pton(socket.AF_INET, self._local_ip4)
@property
def remote_ip4(self):
"""IPv4 address of remote peer "connected" to this interface"""
- return self._remote_ip4
+ return self._remote_hosts[0].ip4
@property
def remote_ip4n(self):
"""IPv4 address of remote peer - raw, suitable as API parameter"""
- return self._remote_ip4n
+ return socket.inet_pton(socket.AF_INET, self.remote_ip4)
@property
def local_ip6(self):
@@ -55,17 +54,17 @@ class VppInterface(object):
@property
def local_ip6n(self):
"""Local IPv6 address - raw, suitable as API parameter"""
- return self._local_ip6n
+ return socket.inet_pton(socket.AF_INET6, self.local_ip6)
@property
def remote_ip6(self):
"""IPv6 address of remote peer "connected" to this interface"""
- return self._remote_ip6
+ return self._remote_hosts[0].ip6
@property
def remote_ip6n(self):
"""IPv6 address of remote peer - raw, suitable as API parameter"""
- return self._remote_ip6n
+ return socket.inet_pton(socket.AF_INET6, self.remote_ip6)
@property
def name(self):
@@ -82,19 +81,51 @@ class VppInterface(object):
"""Test case creating this interface"""
return self._test
+ @property
+ def remote_hosts(self):
+ """Remote hosts list"""
+ return self._remote_hosts
+
+ @remote_hosts.setter
+ def remote_hosts(self, value):
+ self._remote_hosts = value
+ #TODO: set hosts_by dicts
+
+ def host_by_mac(self, mac):
+ return self._hosts_by_mac[mac]
+
+ def host_by_ip4(self, ip):
+ return self._hosts_by_ip4[ip]
+
+ def host_by_ip6(self, ip):
+ return self._hosts_by_ip6[ip]
+
+ def generate_remote_hosts(self, count=1):
+ """Generate and add remote hosts for the interface."""
+ self._remote_hosts = []
+ self._hosts_by_mac = {}
+ self._hosts_by_ip4 = {}
+ self._hosts_by_ip6 = {}
+ for i in range(2, count+2): # 0: network address, 1: local vpp address
+ mac = "02:%02x:00:00:ff:%02x" % (self.sw_if_index, i)
+ ip4 = "172.16.%u.%u" % (self.sw_if_index, i)
+ ip6 = "fd01:%04x::%04x" % (self.sw_if_index, i)
+ host = Host(mac, ip4, ip6)
+ self._remote_hosts.append(host)
+ self._hosts_by_mac[mac] = host
+ self._hosts_by_ip4[ip4] = host
+ self._hosts_by_ip6[ip6] = host
+
def post_init_setup(self):
"""Additional setup run after creating an interface object"""
- self._remote_mac = "02:00:00:00:ff:%02x" % self.sw_if_index
+
+ self.generate_remote_hosts()
self._local_ip4 = "172.16.%u.1" % self.sw_if_index
self._local_ip4n = socket.inet_pton(socket.AF_INET, self.local_ip4)
- self._remote_ip4 = "172.16.%u.2" % self.sw_if_index
- self._remote_ip4n = socket.inet_pton(socket.AF_INET, self.remote_ip4)
- self._local_ip6 = "fd01:%u::1" % self.sw_if_index
+ self._local_ip6 = "fd01:%04x::1" % self.sw_if_index
self._local_ip6n = socket.inet_pton(socket.AF_INET6, self.local_ip6)
- self._remote_ip6 = "fd01:%u::2" % self.sw_if_index
- self._remote_ip6n = socket.inet_pton(socket.AF_INET6, self.remote_ip6)
r = self.test.vapi.sw_interface_dump()
for intf in r:
@@ -124,6 +155,13 @@ class VppInterface(object):
self.test.vapi.sw_interface_add_del_address(
self.sw_if_index, addr, addr_len)
+ def configure_extend_ipv4_mac_binding(self):
+ """Configure neighbor MAC to IPv4 addresses."""
+ for host in self._remote_hosts:
+ macn = host.mac.replace(":", "").decode('hex')
+ ipn = host.ip4n
+ self.test.vapi.ip_neighbor_add_del(self.sw_if_index, macn, ipn)
+
def config_ip6(self):
"""Configure IPv6 address on the VPP interface"""
addr = self._local_ip6n
@@ -147,91 +185,6 @@ class VppInterface(object):
"""Configure IPv6 RA suppress on the VPP interface"""
self.test.vapi.sw_interface_ra_suppress(self.sw_if_index)
- def create_arp_req(self):
- """Create ARP request applicable for this interface"""
- return (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.remote_mac) /
- ARP(op=ARP.who_has, pdst=self.local_ip4,
- psrc=self.remote_ip4, hwsrc=self.remote_mac))
-
- def create_ndp_req(self):
- return (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.remote_mac) /
- IPv6(src=self.remote_ip6, dst=self.local_ip6) /
- ICMPv6ND_NS(tgt=self.local_ip6) /
- ICMPv6NDOptSrcLLAddr(lladdr=self.remote_mac))
-
- def resolve_arp(self, pg_interface=None):
- """Resolve ARP using provided packet-generator interface
-
- :param pg_interface: interface used to resolve, if None then this
- interface is used
-
- """
- if pg_interface is None:
- pg_interface = self
- info("Sending ARP request for %s on port %s" %
- (self.local_ip4, pg_interface.name))
- arp_req = self.create_arp_req()
- pg_interface.add_stream(arp_req)
- pg_interface.enable_capture()
- self.test.pg_start()
- info(self.test.vapi.cli("show trace"))
- arp_reply = pg_interface.get_capture()
- if arp_reply is None or len(arp_reply) == 0:
- info("No ARP received on port %s" % pg_interface.name)
- return
- arp_reply = arp_reply[0]
- # Make Dot1AD packet content recognizable to scapy
- if arp_reply.type == 0x88a8:
- arp_reply.type = 0x8100
- arp_reply = Ether(str(arp_reply))
- try:
- if arp_reply[ARP].op == ARP.is_at:
- info("VPP %s MAC address is %s " %
- (self.name, arp_reply[ARP].hwsrc))
- self._local_mac = arp_reply[ARP].hwsrc
- else:
- info("No ARP received on port %s" % pg_interface.name)
- except:
- error("Unexpected response to ARP request:")
- error(arp_reply.show())
- raise
-
- def resolve_ndp(self, pg_interface=None):
- """Resolve NDP using provided packet-generator interface
-
- :param pg_interface: interface used to resolve, if None then this
- interface is used
-
- """
- if pg_interface is None:
- pg_interface = self
- info("Sending NDP request for %s on port %s" %
- (self.local_ip6, pg_interface.name))
- ndp_req = self.create_ndp_req()
- pg_interface.add_stream(ndp_req)
- pg_interface.enable_capture()
- self.test.pg_start()
- info(self.test.vapi.cli("show trace"))
- ndp_reply = pg_interface.get_capture()
- if ndp_reply is None or len(ndp_reply) == 0:
- info("No NDP received on port %s" % pg_interface.name)
- return
- ndp_reply = ndp_reply[0]
- # Make Dot1AD packet content recognizable to scapy
- if ndp_reply.type == 0x88a8:
- ndp_reply.type = 0x8100
- ndp_reply = Ether(str(ndp_reply))
- try:
- ndp_na = ndp_reply[ICMPv6ND_NA]
- opt = ndp_na[ICMPv6NDOptDstLLAddr]
- info("VPP %s MAC address is %s " %
- (self.name, opt.lladdr))
- self._local_mac = opt.lladdr
- except:
- error("Unexpected response to NDP request:")
- error(ndp_reply.show())
- raise
-
def admin_up(self):
""" Put interface ADMIN-UP """
self.test.vapi.sw_interface_set_flags(self.sw_if_index, admin_up_down=1)
diff --git a/test/vpp_lo_interface.py b/test/vpp_lo_interface.py
new file mode 100644
index 00000000000..ed9ac725db5
--- /dev/null
+++ b/test/vpp_lo_interface.py
@@ -0,0 +1,16 @@
+
+from vpp_interface import VppInterface
+
+
+class VppLoInterface(VppInterface):
+ """
+ VPP loopback interface
+ """
+
+ def __init__(self, test, lo_index):
+ """ Create VPP loopback interface """
+ self._lo_index = lo_index
+ self._test = test
+ r = self.test.vapi.create_loopback()
+ self._sw_if_index = r.sw_if_index
+ self.post_init_setup()
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index f0eb410bf30..10445de625c 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -277,6 +277,13 @@ class VppPapiProvider(object):
"""
return self.api(vpp_papi.create_vlan_subif, (sw_if_index, vlan))
+ def create_loopback(self, mac=''):
+ """
+
+ :param mac: (Optional)
+ """
+ return self.api(vpp_papi.create_loopback, (mac,))
+
def ip_add_del_route(
self,
dst_address,
@@ -352,3 +359,35 @@ class VppPapiProvider(object):
dst_address_length,
dst_address,
next_hop_address))
+
+ def ip_neighbor_add_del(self,
+ sw_if_index,
+ mac_address,
+ dst_address,
+ vrf_id=0,
+ is_add=1,
+ is_ipv6=0,
+ is_static=0,
+ ):
+ """ Add neighbor MAC to IPv4 or IPv6 address.
+
+ :param sw_if_index:
+ :param mac_address:
+ :param dst_address:
+ :param vrf_id: (Default value = 0)
+ :param is_add: (Default value = 1)
+ :param is_ipv6: (Default value = 0)
+ :param is_static: (Default value = 0)
+ """
+
+ return self.api(
+ vpp_papi.ip_neighbor_add_del,
+ (vrf_id,
+ sw_if_index,
+ is_add,
+ is_ipv6,
+ is_static,
+ mac_address,
+ dst_address
+ )
+ )
diff --git a/test/vpp_pg_interface.py b/test/vpp_pg_interface.py
index c9e4076d19a..81c7192e07e 100644
--- a/test/vpp_pg_interface.py
+++ b/test/vpp_pg_interface.py
@@ -1,9 +1,12 @@
import os
import time
-from logging import error
+from logging import error, info
from scapy.utils import wrpcap, rdpcap
from vpp_interface import VppInterface
+from scapy.layers.l2 import Ether, ARP
+from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6ND_NA, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptDstLLAddr
+
class VppPGInterface(VppInterface):
"""
@@ -130,3 +133,89 @@ class VppPGInterface(VppInterface):
" packets arrived" % self.out_path)
return []
return output
+
+ def create_arp_req(self):
+ """Create ARP request applicable for this interface"""
+ return (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.remote_mac) /
+ ARP(op=ARP.who_has, pdst=self.local_ip4,
+ psrc=self.remote_ip4, hwsrc=self.remote_mac))
+
+ def create_ndp_req(self):
+ """Create NDP - NS applicable for this interface"""
+ return (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.remote_mac) /
+ IPv6(src=self.remote_ip6, dst=self.local_ip6) /
+ ICMPv6ND_NS(tgt=self.local_ip6) /
+ ICMPv6NDOptSrcLLAddr(lladdr=self.remote_mac))
+
+ def resolve_arp(self, pg_interface=None):
+ """Resolve ARP using provided packet-generator interface
+
+ :param pg_interface: interface used to resolve, if None then this
+ interface is used
+
+ """
+ if pg_interface is None:
+ pg_interface = self
+ info("Sending ARP request for %s on port %s" %
+ (self.local_ip4, pg_interface.name))
+ arp_req = self.create_arp_req()
+ pg_interface.add_stream(arp_req)
+ pg_interface.enable_capture()
+ self.test.pg_start()
+ info(self.test.vapi.cli("show trace"))
+ arp_reply = pg_interface.get_capture()
+ if arp_reply is None or len(arp_reply) == 0:
+ info("No ARP received on port %s" % pg_interface.name)
+ return
+ arp_reply = arp_reply[0]
+ # Make Dot1AD packet content recognizable to scapy
+ if arp_reply.type == 0x88a8:
+ arp_reply.type = 0x8100
+ arp_reply = Ether(str(arp_reply))
+ try:
+ if arp_reply[ARP].op == ARP.is_at:
+ info("VPP %s MAC address is %s " %
+ (self.name, arp_reply[ARP].hwsrc))
+ self._local_mac = arp_reply[ARP].hwsrc
+ else:
+ info("No ARP received on port %s" % pg_interface.name)
+ except:
+ error("Unexpected response to ARP request:")
+ error(arp_reply.show())
+ raise
+
+ def resolve_ndp(self, pg_interface=None):
+ """Resolve NDP using provided packet-generator interface
+
+ :param pg_interface: interface used to resolve, if None then this
+ interface is used
+
+ """
+ if pg_interface is None:
+ pg_interface = self
+ info("Sending NDP request for %s on port %s" %
+ (self.local_ip6, pg_interface.name))
+ ndp_req = self.create_ndp_req()
+ pg_interface.add_stream(ndp_req)
+ pg_interface.enable_capture()
+ self.test.pg_start()
+ info(self.test.vapi.cli("show trace"))
+ ndp_reply = pg_interface.get_capture()
+ if ndp_reply is None or len(ndp_reply) == 0:
+ info("No NDP received on port %s" % pg_interface.name)
+ return
+ ndp_reply = ndp_reply[0]
+ # Make Dot1AD packet content recognizable to scapy
+ if ndp_reply.type == 0x88a8:
+ ndp_reply.type = 0x8100
+ ndp_reply = Ether(str(ndp_reply))
+ try:
+ ndp_na = ndp_reply[ICMPv6ND_NA]
+ opt = ndp_na[ICMPv6NDOptDstLLAddr]
+ info("VPP %s MAC address is %s " %
+ (self.name, opt.lladdr))
+ self._local_mac = opt.lladdr
+ except:
+ error("Unexpected response to NDP request:")
+ error(ndp_reply.show())
+ raise
diff --git a/test/vpp_sub_interface.py b/test/vpp_sub_interface.py
index cd98a68c59d..b387d27b49d 100644
--- a/test/vpp_sub_interface.py
+++ b/test/vpp_sub_interface.py
@@ -1,9 +1,10 @@
from scapy.layers.l2 import Ether, Dot1Q
from abc import abstractmethod, ABCMeta
from vpp_interface import VppInterface
+from vpp_pg_interface import VppPGInterface
-class VppSubInterface(VppInterface):
+class VppSubInterface(VppPGInterface):
__metaclass__ = ABCMeta
@property
@@ -55,14 +56,14 @@ class VppDot1QSubint(VppSubInterface):
self._vlan = vlan
r = self.test.vapi.create_vlan_subif(parent.sw_if_index, self.vlan)
self._sw_if_index = r.sw_if_index
- self.post_init_setup()
+ VppInterface.post_init_setup(self)
def create_arp_req(self):
- packet = VppInterface.create_arp_req(self)
+ packet = VppPGInterface.create_arp_req(self)
return self.add_dot1_layer(packet)
def create_ndp_req(self):
- packet = VppInterface.create_ndp_req(self)
+ packet = VppPGInterface.create_ndp_req(self)
return self.add_dot1_layer(packet)
def add_dot1_layer(self, packet):
@@ -108,14 +109,14 @@ class VppDot1ADSubint(VppSubInterface):
two_tags=1,
exact_match=1)
self._sw_if_index = r.sw_if_index
- self.post_init_setup()
+ VppInterface.post_init_setup(self)
def create_arp_req(self):
- packet = VppInterface.create_arp_req(self)
+ packet = VppPGInterface.create_arp_req(self)
return self.add_dot1_layer(packet)
def create_ndp_req(self):
- packet = VppInterface.create_ndp_req(self)
+ packet = VppPGInterface.create_ndp_req(self)
return self.add_dot1_layer(packet)
def add_dot1_layer(self, packet):