# 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.
export WS_ROOT=$(CURDIR)
export BR=$(WS_ROOT)/build-root
CCACHE_DIR?=$(BR)/.ccache
GDB?=gdb
PLATFORM?=vpp
SAMPLE_PLUGIN?=no
MACHINE=$(shell uname -m)
,:=,
define disable_plugins
$(if $(1), \
"plugins {" \
$(patsubst %,"plugin %_plugin.so { disable }",$(subst $(,), ,$(1))) \
" }" \
,)
endef
MINIMAL_STARTUP_CONF=" \
unix { \
interactive \
cli-listen /run/vpp/cli.sock \
gid $(shell id -g) \
$(if $(wildcard startup.vpp),"exec startup.vpp",) \
} \
$(if $(DPDK_CONFIG), "dpdk { $(DPDK_CONFIG) }",) \
$(call disable_plugins,$(DISABLED_PLUGINS)) \
"
GDB_ARGS= -ex "handle SIGUSR1 noprint nostop"
#
# OS Detection
#
# We allow Darwin (MacOS) for docs generation; VPP build will still fail.
ifneq ($(shell uname),Darwin)
OS_ID = $(shell grep '^ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g')
OS_VERSION_ID= $(shell grep '^VERSION_ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g')
endif
ifeq ($(filter ubuntu debian,$(OS_ID)),$(OS_ID))
PKG=deb
else ifeq ($(filter rhel centos fedora opensuse,$(OS_ID)),$(OS_ID))
PKG=rpm
endif
# +libganglia1-dev if building the gmond plugin
DEB_DEPENDS = curl build-essential autoconf automake ccache
DEB_DEPENDS += debhelper dkms git libtool libapr1-dev dh-systemd
DEB_DEPENDS += libconfuse-dev git-review exuberant-ctags cscope pkg-config
DEB_DEPENDS += lcov chrpath autoconf indent clang-format libnuma-dev
DEB_DEPENDS += python-all python-dev python-virtualenv python-pip libffi6 check
DEB_DEPENDS += libboost-all-dev libffi-dev python-ply
ifeq ($(OS_VERSION_ID),14.04)
DEB_DEPENDS += openjdk-8-jdk-headless
DEB_DEPENDS += libssl-dev
else ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-8)
DEB_DEPENDS += openjdk-8-jdk-headless
DEB_DEPENDS += libssl-dev
APT_ARGS = -t jessie-backports
else ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-9)
DEB_DEPENDS += default-jdk-headless
DEB_DEPENDS += libssl1.0-dev
else
DEB_DEPENDS += default-jdk-headless
DEB_DEPENDS += libssl-dev
endif
RPM_DEPENDS = redhat-lsb glibc-static java-1.8.0-openjdk-devel yum-utils
RPM_DEPENDS += apr-devel
RPM_DEPENDS += numactl-devel
RPM_DEPENDS += check check-devel
RPM_DEPENDS += boost boost-devel
RPM_DEPENDS += subunit subunit-devel
ifeq ($(OS_ID)-$(OS_VERSION_ID),fedora-25)
RPM_DEPENDS += openssl-devel
RPM_DEPENDS += python-devel python-ply
RPM_DEPENDS += python2-virtualenv
RPM_DEPENDS_GROUPS = 'C Development Tools and Libraries'
else ifeq ($(shell if [ "$(OS_ID)" = "fedora" ]; then test $(OS_VERSION_ID) -gt 25; echo $$?; fi),0)
RPM_DEPENDS += compat-openssl10-devel
RPM_DEPENDS += python2-devel python-ply
RPM_DEPENDS += python2-virtualenv
RPM_DEPENDS_GROUPS = 'C Development Tools and Libraries'
else
RPM_DEPENDS += openssl-devel
RPM_DEPENDS += python-devel python-ply
RPM_DEPENDS += python-virtualenv
RPM_DEPENDS_GROUPS = 'Development Tools'
endif
# +ganglia-devel if building the ganglia plugin
RPM_DEPENDS += chrpath libffi-devel rpm-build
SUSE_NAME= $(shell grep '^NAME=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g' | cut -d' ' -f2)
RPM_SUSE_BUILDTOOLS_DEPS = autoconf automake ccache check-devel chrpath
RPM_SUSE_BUILDTOOLS_DEPS += clang indent libtool make python-ply
RPM_SUSE_DEVEL_DEPS = glibc-devel-static java-1_8_0-openjdk-devel libnuma-devel
RPM_SUSE_DEVEL_DEPS += libopenssl-devel openssl-devel
RPM_SUSE_PYTHON_DEPS = python-devel python3-devel python-pip python3-pip
RPM_SUSE_PYTHON_DEPS += python-rpm-macros python3-rpm-macros
RPM_SUSE_PLATFORM_DEPS = distribution-release shadow rpm-build
ifeq ($(OS_ID),opensuse)
ifneq ($(SUSE_NAME),Tumbleweed)
RPM_SUSE_DEVEL_DEPS += boost_1_61-devel gcc6
RPM_SUSE_PYTHON_DEPS += python-virtualenv
else
RPM_SUSE_DEVEL_DEPS = libboost_headers-devel libboost_thread-devel gcc
RPM_SUSE_PYTHON_DEPS += python2-virtualenv
endif
endif
RPM_SUSE_DEPENDS += $(RPM_SUSE_BUILDTOOLS_DEPS) $(RPM_SUSE_DEVEL_DEPS) $(RPM_SUSE_PYTHON_DEPS) $(RPM_SUSE_PLATFORM_DEPS)
ifneq ($(wildcard $(STARTUP_DIR)/startup.conf),)
STARTUP_CONF ?= $(STARTUP_DIR)/startup.conf
endif
ifeq ($(findstring y,$(UNATTENDED)),y)
CONFIRM=-y
FORCE=--force-yes
endif
TARGETS = vpp
ifneq ($(SAMPLE_PLUGIN),no)
TARGETS += sample-plugin
endif
.PHONY: help bootstrap wipe wipe-release build build-release rebuild rebuild-release
.PHONY: run run-release debug debug-release build-vat run-vat pkg-deb pkg-rpm
.PHONY: ctags cscope
.PHONY: test test-debug retest retest-debug test-doc test-wipe-doc test-help test-wipe
.PHONY: test-cov test-wipe-cov
help:
@echo "Make Targets:"
@echo " bootstrap - prepare tree for build"
@echo " install-dep - install software dependencies"
@echo " wipe - wipe all products of debug build "
@echo " wipe-release - wipe all products of release build "
@echo " build - build debug binaries"
@echo " build-release - build release binaries"
@echo " build-coverity - build coverity artifacts"
@echo " rebuild - wipe and build debug binares"
@echo " rebuild-release - wipe and build release binares"
@echo " run - run debug binary"
@echo " run-release - run release binary"
@echo " debug - run debug binary with debugger"
@echo " debug-release - run release binary with debugger"
@echo " test - build and run (basic) functional tests"
@echo " test-debug - build and run (basic) functional tests (debug build)"
@echo " test-all - build and run (all) functional tests"
@echo " test-all-debug - build and run (all) functional tests (debug build)"
@echo " test-shell - enter shell with test environment"
@echo " test-shell-debug - enter shell with test environment (debug build)"
@echo " test-wipe - wipe files generated by unit tests"
@echo " retest - run functional tests"
@echo " retest-debug - run functional tests (debug build)"
@echo " test-help - show help on test framework"
@echo " run-vat - run vpp-api-test tool"
@echo " pkg-deb - build DEB packages"
@echo " pkg-rpm - build RPM packages"
@echo " dpdk-install-dev - install DPDK development packages"
@echo " ctags - (re)generate ctags database"
@echo " gtags - (re)generate gtags database"
@echo " cscope - (re)generate cscope database"
@echo " checkstyle - check coding style"
@echo " fixstyle - fix coding style"
@echo " doxygen - (re)generate documentation"
@echo " bootstrap-doxygen - setup Doxygen dependencies"
@echo " wipe-doxygen - wipe all generated documentation"
@echo " test-doc - generate documentation for test framework"
@echo " test-wipe-doc - wipe documentation for test framework"
@echo " test-cov - generate code coverage report for test framework"
@echo " test-wipe-cov - wipe code coverage report for test framework"
@echo " test-checkstyle - check PEP8 compliance for test framework"
@echo ""
@echo "Make Arguments:"
@echo " V=[0|1] - set build verbosity level"
@echo " STARTUP_CONF=<path> - startup configuration file"
@echo " #!/usr/bin/env python
import unittest
from framework import VppTestCase, VppTestRunner
from vpp_object import VppObject
from vpp_neighbor import VppNeighbor
from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable
from vpp_ip import *
from vpp_mac import *
from scapy.packet import Raw
from scapy.layers.l2 import Ether, ARP
from scapy.layers.inet import IP, UDP
from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6NDOptSrcLLAddr, \
ICMPv6ND_NA
from scapy.utils6 import in6_getnsma, in6_getnsmac
from socket import AF_INET, AF_INET6
from scapy.utils import inet_pton, inet_ntop
from util import mactobinary
def find_gbp_endpoint(test, sw_if_index, ip=None, mac=None):
vip = VppIpAddress(ip)
eps = test.vapi.gbp_endpoint_dump()
for ep in eps:
if ep.endpoint.sw_if_index != sw_if_index:
continue
for eip in ep.endpoint.ips:
if vip == eip:
return True
return False
class VppGbpEndpoint(VppObject):
"""
GBP Endpoint
"""
@property
def bin_mac(self):
return mactobinary(self.itf.remote_mac)
@property
def mac(self):
return self.itf.remote_mac
@property
def ip4(self):
return self._ip4
@property
def fip4(self):
return self._fip4
@property
def ip6(self):
return self._ip6
@property
def fip6(self):
return self._fip6
@property
def ips(self):
return [self.ip4, self.ip6]
@property
def fips(self):
return [self.fip4, self.fip6]
def __init__(self, test, itf, epg, recirc, ip4, fip4, ip6, fip6):
self._test = test
self.itf = itf
self.epg = epg
self.recirc = recirc
self._ip4 = VppIpAddress(ip4)
self._fip4 = VppIpAddress(fip4)
self._ip6 = VppIpAddress(ip6)
self._fip6 = VppIpAddress(fip6)
self.vmac = VppMacAddress(self.itf.remote_mac)
def add_vpp_config(self):
res = self._test.vapi.gbp_endpoint_add(
self.itf.sw_if_index,
[self.ip4.encode(), self.ip6.encode()],
self.vmac.encode(),
self.epg.epg)
self.handle = res.handle
self._test.registry.register(self, self._test.logger)
def remove_vpp_config(self):
self._test.vapi.gbp_endpoint_del(self.handle)
def __str__(self):
return self.object_id()
def object_id(self):
return "gbp-endpoint;[%d:%s:%d]" % (self.itf.sw_if_index,
self.ip4.address,
self.epg.epg)
def query_vpp_config(self):
return find_gbp_endpoint(self._test,
self.itf.sw_if_index,
self.ip4.address)
class VppGbpRecirc(VppObject):
"""
GBP Recirculation Interface
"""
def __init__(self, test, epg, recirc, is_ext=False):
self._test = test
self.recirc = recirc
self.epg = epg
self.is_ext = is_ext
def add_vpp_config(self):
self._test.vapi.gbp_recirc_add_del(
1,
self.recirc.sw_if_index,
self.epg.epg,
self.is_ext)
self._test.registry.register(self, self._test.logger)
def remove_vpp_config(self):
self._test.vapi.gbp_recirc_add_del(
0,
self.recirc.sw_if_index,
self.epg.epg,
self.is_ext)
def __str__(self):
return self.object_id()
def object_id(self):
return "gbp-recirc;[%d]" % (self.recirc.sw_if_index)
def query_vpp_config(self):
rs = self._test.vapi.gbp_recirc_dump()
for r in rs:
if r.recirc.sw_if_index == self.recirc.sw_if_index:
return True
return False
class VppGbpSubnet(VppObject):
"""
GBP Subnet
"""
def __init__(self, test, table_id, address, address_len,
is_internal=True,
sw_if_index=None, epg=None):
self._test = test
self.table_id = table_id
self.prefix = VppIpPrefix(address, address_len)
self.is_internal = is_internal
self.sw_if_index = sw_if_index
self.epg = epg
def add_vpp_config(self):
self._test.vapi.gbp_subnet_add_del(
1,
self.table_id,
self.is_internal,
self.prefix.encode(),
sw_if_index=self.sw_if_index if self.sw_if_index else 0xffffffff,
epg_id=self.epg if self.epg else 0xffff)
self._test.registry.register(self, self._test.logger)
def remove_vpp_config(self):
self._test.vapi.gbp_subnet_add_del(
0,
self.table_id,
self.is_internal,
self.prefix.encode())
def __str__(self):
return self.object_id()
def object_id(self):
return "gbp-subnet;[%d-%s]" % (self.table_id,
self.prefix)
def query_vpp_config(self):
ss = self._test.vapi.gbp_subnet_dump()
for s in ss:
if s.subnet.table_id == self.table_id and \
s.subnet.prefix == self.prefix:
return True
return False
class VppGbpEndpointGroup(VppObject):
"""
GBP Endpoint Group
"""
def __init__(self, test, epg, rd, bd, uplink,
bvi, bvi_ip4, bvi_ip6=None):
self._test = test
self.uplink = uplink
self.bvi = bvi
self.bvi_ip4 = bvi_ip4
self.bvi_ip4_n = inet_pton(AF_INET, bvi_ip4)
self.bvi_ip6 = bvi_ip6
self.bvi_ip6_n = inet_pton(AF_INET6, bvi_ip6)
self.epg = epg
self.bd = bd
self.rd = rd
def add_vpp_config(self):
self._test.vapi.gbp_endpoint_group_add_del(
1,
self.epg,
self.bd,
self.rd,
self.rd,
self.uplink.sw_if_index)
self._test.registry.register(self, self._test.logger)
def remove_vpp_config(self):
self._test.vapi.gbp_endpoint_group_add_del(
0,
self.epg,
self.bd,
self.rd,
self.rd,
self.uplink.sw_if_index)
def __str__(self):
return self.object_id()
def object_id(self):
return "gbp-endpoint-group;[%d]" % (self.epg)
def query_vpp_config(self):
epgs = self._test.vapi.gbp_endpoint_group_dump()
for epg in epgs:
if epg.epg.epg_id == self.epg:
return True
return False
class VppGbpContract(VppObject):
"""
GBP Contract
"""
def __init__(self, test, src_epg, dst_epg, acl_index):
self._test = test
self.acl_index = acl_index
self.src_epg = src_epg
self.dst_epg = dst_epg
def add_vpp_config(self):
self._test.vapi.gbp_contract_add_del(
1,
self.src_epg,
self.dst_epg,
self.acl_index)
self._test.registry.register(self, self._test.logger)
def remove_vpp_config(self):
self._test.vapi.gbp_contract_add_del(
0,
self.src_epg,
self.dst_epg,
self.acl_index)
def __str__(self):
return self.object_id()
def object_id(self):
return "gbp-contract;[%d:%s:%d]" % (self.src_epg,
self.dst_epg,
self.acl_index)
def query_vpp_config(self):
cs = self._test.vapi.gbp_contract_dump()
for c in cs:
if c.contract.src_epg == self.src_epg \
and c.contract.dst_epg == self.dst_epg:
return True
return False
class VppGbpAcl(VppObject):
"""
GBP Acl
"""
def __init__(self, test):
self._test = test
self.acl_index = 4294967295
def create_rule(self, is_ipv6=0, permit_deny=0, proto=-1,
s_prefix=0, s_ip='\x00\x00\x00\x00', sport_from=0,
sport_to=65535, d_prefix=0, d_ip='\x00\x00\x00\x00',
dport_from=0, dport_to=65535):
if proto == -1 or proto == 0:
sport_to = 0
dport_to = sport_to
elif proto == 1 or proto == 58:
sport_to = 255
dport_to = sport_to
rule = ({'is_permit': permit_deny, 'is_ipv6': is_ipv6, 'proto': proto,
'srcport_or_icmptype_first': sport_from,
'srcport_or_icmptype_last': sport_to,
'src_ip_prefix_len': s_prefix,
'src_ip_addr': s_ip,
'dstport_or_icmpcode_first': dport_from,
'dstport_or_icmpcode_last': dport_to,
'dst_ip_prefix_len': d_prefix,
'dst_ip_addr': d_ip})
return rule
def add_vpp_config(self, rules):
reply = self._test.vapi.acl_add_replace(self.acl_index,
r=rules,
tag='GBPTest')
self.acl_index = reply.acl_index
return self.acl_index
def remove_vpp_config(self):
self._test.vapi.acl_del(self.acl_index)
def __str__(self):
return self.object_id()
def object_id(self):
return "gbp-acl;[%d]" % (self.acl_index)
def query_vpp_config(self):
cs = self._test.vapi.acl_dump()
for c in cs:
if c.acl_index == self.acl_index:
return True
return False
class TestGBP(VppTestCase):
""" GBP Test Case """
def setUp(self):
super(TestGBP, self).setUp()
self.create_pg_interfaces(range(9))
self.create_loopback_interfaces(9)
self.router_mac = "00:11:22:33:44:55"
for i in self.pg_interfaces:
i.admin_up()
for i in self.lo_interfaces:
i.admin_up()
self.vapi.sw_interface_set_mac_address(
i.sw_if_index,
mactobinary(self.router_mac))
def tearDown(self):
for i in self.pg_interfaces:
i.admin_down()
super(TestGBP, self).tearDown()
def send_and_expect_bridged(self, src, tx, dst):
rx = self.send_and_expect(src, tx, dst)
for r in rx:
self.assertEqual(r[Ether].src, tx[0][Ether].src)
self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
self.assertEqual(r[IP].src, tx[0][IP].src)
self.assertEqual(r[IP].dst, tx[0][IP].dst)
return rx
def send_and_expect_bridged6(self, src, tx, dst):
rx = self.send_and_expect(src, tx, dst)
for r in rx:
self.assertEqual(r[Ether].src, tx[0][Ether].src)
self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
return rx
def send_and_expect_routed(self, src, tx, dst, src_mac):
rx = self.send_and_expect(src, tx, dst)
for r in rx:
self.assertEqual(r[Ether].src, src_mac)
self.assertEqual(r[Ether].dst, dst.remote_mac)
self.assertEqual(r[IP].src, tx[0][IP].src)
self.assertEqual(r[IP].dst, tx[0][IP].dst)
return rx
def send_and_expect_natted(self, src, tx, dst, src_ip):
rx = self.send_and_expect(src, tx, dst)
for r in rx:
self.assertEqual(r[Ether].src, tx[0][Ether].src)
self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
self.assertEqual(r[IP].src, src_ip)
self.assertEqual(r[IP].dst, tx[0][IP].dst)
return rx
def send_and_expect_natted6(self, src, tx, dst, src_ip):
rx = self.send_and_expect(src, tx, dst)
for r in rx:
self.assertEqual(r[Ether].src, tx[0][Ether].src)
self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
self.assertEqual(r[IPv6].src, src_ip)
self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
return rx
def send_and_expect_unnatted(self, src, tx, dst, dst_ip):
rx = self.send_and_expect(src, tx, dst)
for r in rx:
self.assertEqual(r[Ether].src, tx[0][Ether].src)
self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
self.assertEqual(r[IP].dst, dst_ip)
self.assertEqual(r[IP].src, tx[0][IP].src)
return rx
def send_and_expect_unnatted6(self, src, tx, dst, dst_ip):
rx = self.send_and_expect(src, tx, dst)
for r in rx:
self.assertEqual(r[Ether].src, tx[0][Ether].src)
self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
self.assertEqual(r[IPv6].dst, dst_ip)
self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
return rx
def send_and_expect_double_natted(self, src, tx, dst, src_ip, dst_ip):
rx = self.send_and_expect(src, tx, dst)
for r in rx:
self.assertEqual(r[Ether].src, self.router_mac)
self.assertEqual(r[Ether].dst, dst.remote_mac)
self.assertEqual(r[IP].dst, dst_ip)
self.assertEqual(r[IP].src, src_ip)
return rx
def send_and_expect_double_natted6(self, src, tx, dst, src_ip, dst_ip):
rx = self.send_and_expect(src, tx, dst)
for r in rx:
self.assertEqual(r[Ether].src, self.router_mac)
self.assertEqual(r[Ether].dst, dst.remote_mac)
self.assertEqual(r[IPv6].dst, dst_ip)
self.assertEqual(r[IPv6].src, src_ip)
return rx
def test_gbp(self):
""" Group Based Policy """
nat_table = VppIpTable(self, 20)
nat_table.add_vpp_config()
nat_table = VppIpTable(self, 20, is_ip6=True)
nat_table.add_vpp_config()
#
# Bridge Domains
#
self.vapi.bridge_domain_add_del(1, flood=1, uu_flood=1, forward=1,
learn=0, arp_term=1, is_add=1)
self.vapi.bridge_domain_add_del(2, flood=1, uu_flood=1, forward=1,
learn=0, arp_term=1, is_add=1)
self.vapi.bridge_domain_add_del(20, flood=1, uu_flood=1, forward=1,
learn=0, arp_term=1, is_add=1)
#
# 3 EPGs, 2 of which share a BD.
# 2 NAT EPGs, one for floating-IP subnets, the other for internet
#
epgs = [VppGbpEndpointGroup(self, 220, 0, 1, self.pg4,
self.loop0,
"10.0.0.128",
"2001:10::128"),
VppGbpEndpointGroup(self, 221, 0, 1, self.pg5,
self.loop0,
"10.0.1.128",
"2001:10:1::128"),
VppGbpEndpointGroup(self, 222, 0, 2, self.pg6,
self.loop1,
"10.0.2.128",
"2001:10:2::128"),
VppGbpEndpointGroup(self, 333, 20, 20, self.pg7,
self.loop2,
"11.0.0.128",
"3001::128"),
VppGbpEndpointGroup(self, 444, 20, 20, self.pg8,
self.loop2,
"11.0.0.129",
"3001::129")]
recircs = [VppGbpRecirc(self, epgs[0],
self.loop3),
VppGbpRecirc(self, epgs[1],
self.loop4),
VppGbpRecirc(self, epgs[2],
self.loop5),
VppGbpRecirc(self, epgs[3],
self.loop6, is_ext=True),
VppGbpRecirc(self, epgs[4],
self.loop8, is_ext=True)]
epg_nat = epgs[3]
recirc_nat = recircs[3]
#
# 4 end-points, 2 in the same subnet, 3 in the same BD
#
eps = [VppGbpEndpoint(self, self.pg0,
epgs[0], recircs[0],
"10.0.0.1", "11.0.0.1",
"2001:10::1", "3001::1"),
VppGbpEndpoint(self, self.pg1,
epgs[0], recircs[0],
"10.0.0.2", "11.0.0.2",
"2001:10::2", "3001::2"),
VppGbpEndpoint(self, self.pg2,
epgs[1], recircs[1],
"10.0.1.1", "11.0.0.3",
"2001:10:1::1", "3001::3"),
VppGbpEndpoint(self, self.pg3,
epgs[2], recircs[2],
"10.0.2.1", "11.0.0.4",
"2001:10:2::1", "3001::4")]
#
# Config related to each of the EPGs
#
for epg in epgs:
# IP config on the BVI interfaces
if epg != epgs[1] and epg != epgs[4]:
epg.bvi.set_table_ip4(epg.rd)
epg.bvi.set_table_ip6(epg.rd)
# The BVIs are NAT inside interfaces
self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index,
is_inside=1,
is_add=1)
self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index,
is_inside=1,
is_add=1)
self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
epg.bvi_ip4_n,
32)
self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
epg.bvi_ip6_n,
128,
is_ipv6=True)
# EPG uplink interfaces in the BD
epg.uplink.set_table_ip4(epg.rd)
epg.uplink.set_table_ip6(epg.rd)
self.vapi.sw_interface_set_l2_bridge(epg.uplink.sw_if_index,
epg.bd)
# add the BD ARP termination entry for BVI IP
self.vapi.bd_ip_mac_add_del(bd_id=epg.bd,
mac=mactobinary(self.router_mac),
ip=epg.bvi_ip4_n,
is_ipv6=0,
is_add=1)
self.vapi.bd_ip_mac_add_del(bd_id=epg.bd,
mac=mactobinary(self.router_mac),
ip=epg.bvi_ip6_n,
is_ipv6=1,
is_add=1)
# epg[1] shares the same BVI to epg[0]
if epg != epgs[1] and epg != epgs[4]:
# BVI in BD
self.vapi.sw_interface_set_l2_bridge(epg.bvi.sw_if_index,
epg.bd,
bvi=1)
# BVI L2 FIB entry
self.vapi.l2fib_add_del(self.router_mac,
epg.bd,
epg.bvi.sw_if_index,
is_add=1, bvi_mac=1)
# EPG in VPP
epg.add_vpp_config()
for recirc in recircs:
# EPG's ingress recirculation interface maps to its RD
recirc.recirc.set_table_ip4(recirc.epg.rd)
recirc.recirc.set_table_ip6(recirc.epg.rd)
# in the bridge to allow DVR. L2 emulation to punt to L3
self.vapi.sw_interface_set_l2_bridge(recirc.recirc.sw_if_index,
recirc.epg.bd)
self.vapi.sw_interface_set_l2_emulation(
recirc.recirc.sw_if_index)
self.vapi.nat44_interface_add_del_feature(
recirc.recirc.sw_if_index,
is_inside=0,
is_add=1)
self.vapi.nat66_add_del_interface(
recirc.recirc.sw_if_index,
is_inside=0,
is_add=1)
recirc.add_vpp_config()
ep_routes = []
ep_arps = []
for ep in eps:
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
#
# routes to the endpoints. We need these since there are no
# adj-fibs due to the fact the the BVI address has /32 and
# the subnet is not attached.
#
for (ip, fip) in zip(ep.ips, ep.fips):
r = VppIpRoute(self, ip.address, ip.length,
[VppRoutePath(ip.address,
ep.epg.bvi.sw_if_index,
proto=ip.dpo_proto)],
is_ip6=ip.is_ip6)
r.add_vpp_config()
ep_routes.append(r)
#
# ARP entries for the endpoints
#
a = VppNeighbor(self,
ep.epg.bvi.sw_if_index,
ep.itf.remote_mac,
ip.address,
af=ip.af)
a.add_vpp_config()
ep_arps.append(a)
# add the BD ARP termination entry
self.vapi.bd_ip_mac_add_del(bd_id=ep.epg.bd,
mac=ep.bin_mac,
ip=ip.bytes,
is_ipv6=ip.is_ip6,
is_add=1)
# Add static mappings for each EP from the 10/8 to 11/8 network
if ip.af == AF_INET:
self.vapi.nat44_add_del_static_mapping(ip.bytes,
fip.bytes,
vrf_id=0,
addr_only=1)
else:
self.vapi.nat66_add_del_static_mapping(ip.bytes,
fip.bytes,
vrf_id=0)
# add each EP itf to the its BD
self.vapi.sw_interface_set_l2_bridge(ep.itf.sw_if_index,
ep.epg.bd)
# L2 FIB entry
self.vapi.l2fib_add_del(ep.mac,
ep.epg.bd,
ep.itf.sw_if_index,
is_add=1)
# VPP EP create ...
ep.add_vpp_config()
self.logger.info(self.vapi.cli("sh gbp endpoint"))
# ... results in a Gratuitous ARP/ND on the EPG's uplink
rx = ep.epg.uplink.get_capture(len(ep.ips), timeout=0.2)
for ii, ip in enumerate(ep.ips):
p = rx[ii]
if ip.is_ip6:
self.assertTrue(p.haslayer(ICMPv6ND_NA))
self.assertEqual(p[ICMPv6ND_NA].tgt, ip.address)
else:
self.assertTrue(p.haslayer(ARP))
self.assertEqual(p[ARP].psrc, ip.address)
self.assertEqual(p[ARP].pdst, ip.address)
# add the BD ARP termination entry for floating IP
for fip in ep.fips:
self.vapi.bd_ip_mac_add_del(bd_id=epg_nat.bd,
mac=ep.bin_mac,
ip=fip.bytes,
is_ipv6=fip.is_ip6,
is_add=1)
# floating IPs route via EPG recirc
r = VppIpRoute(self, fip.address, fip.length,
[VppRoutePath(fip.address,
ep.recirc.recirc.sw_if_index,
is_dvr=1,
proto=fip.dpo_proto)],
table_id=20,
is_ip6=fip.is_ip6)
r.add_vpp_config()
ep_routes.append(r)
# L2 FIB entries in the NAT EPG BD to bridge the packets from
# the outside direct to the internal EPG
self.vapi.l2fib_add_del(ep.mac,
epg_nat.bd,
ep.recirc.recirc.sw_if_index,
is_add=1)
#
# ARP packets for unknown IP are flooded
#
pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff",
src=self.pg0.remote_mac) /
ARP(op="who-has",
hwdst="ff:ff:ff:ff:ff:ff",
hwsrc=self.pg0.remote_mac,
pdst=epgs[0].bvi_ip4,
psrc="10.0.0.88"))
self.send_and_expect(self.pg0, [pkt_arp], self.pg0)
#
# ARP/ND packets get a response
#
pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff",
src=self.pg0.remote_mac) /
ARP(op="who-has",
hwdst="ff:ff:ff:ff:ff:ff",
hwsrc=self.pg0.remote_mac,
pdst=epgs[0].bvi_ip4,
psrc=eps[0].ip4.address))
self.send_and_expect(self.pg0, [pkt_arp], self.pg0)
nsma = in6_getnsma(inet_pton(AF_INET6, eps[0].ip6.address))
d = inet_ntop(AF_INET6, nsma)
pkt_nd = (Ether(dst=in6_getnsmac(nsma)) /
IPv6(dst=d, src=eps[0].ip6.address) /
ICMPv6ND_NS(tgt=epgs[0].bvi_ip6) /
ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
self.send_and_expect(self.pg0, [pkt_nd], self.pg0)
#
# broadcast packets are flooded
#
pkt_bcast = (Ether(dst="ff:ff:ff:ff:ff:ff",
src=self.pg0.remote_mac) /
IP(src=eps[0].ip4.address, dst="232.1.1.1") /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.vapi.cli("clear trace")
self.pg0.add_stream(pkt_bcast)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rxd = eps[1].itf.get_capture(1)
self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
rxd = epgs[0].uplink.get_capture(1)
self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
#
# packets to non-local L3 destinations dropped
#
pkt_intra_epg_220_ip4 = (Ether(src=self.pg0.remote_mac,
dst=self.router_mac) /
IP(src=eps[0].ip4.address,
dst="10.0.0.99") /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
pkt_inter_epg_222_ip4 = (Ether(src=self.pg0.remote_mac,
dst=self.router_mac) /
IP(src=eps[0].ip4.address,
dst="10.0.1.99") /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_assert_no_replies(self.pg0, pkt_intra_epg_220_ip4 * 65)
pkt_inter_epg_222_ip6 = (Ether(src=self.pg0.remote_mac,
dst=self.router_mac) /
IPv6(src=eps[0].ip6.address,
dst="2001:10::99") /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_222_ip6 * 65)
#
# Add the subnet routes
#
s41 = VppGbpSubnet(self, 0, "10.0.0.0", 24)
s42 = VppGbpSubnet(self, 0, "10.0.1.0", 24)
s43 = VppGbpSubnet(self, 0, "10.0.2.0", 24)
s41.add_vpp_config()
s42.add_vpp_config()
s43.add_vpp_config()
s61 = VppGbpSubnet(self, 0, "2001:10::1", 64)
s62 = VppGbpSubnet(self, 0, "2001:10:1::1", 64)
s63 = VppGbpSubnet(self, 0, "2001:10:2::1", 64)
s61.add_vpp_config()
s62.add_vpp_config()
s63.add_vpp_config()
self.send_and_expect_bridged(self.pg0,
pkt_intra_epg_220_ip4 * 65,
self.pg4)
self.send_and_expect_bridged(self.pg3,
pkt_inter_epg_222_ip4 * 65,
self.pg6)
self.send_and_expect_bridged6(self.pg3,
pkt_inter_epg_222_ip6 * 65,
self.pg6)
self.logger.info(self.vapi.cli("sh ip fib 11.0.0.2"))
self.logger.info(self.vapi.cli("sh gbp endpoint-group"))
self.logger.info(self.vapi.cli("sh gbp endpoint"))
self.logger.info(self.vapi.cli("sh gbp recirc"))
self.logger.info(self.vapi.cli("sh int"))
self.logger.info(self.vapi.cli("sh int addr"))
self.logger.info(self.vapi.cli("sh int feat loop6"))
self.logger.info(self.vapi.cli("sh vlib graph ip4-gbp-src-classify"))
self.logger.info(self.vapi.cli("sh int feat loop3"))
#
# Packet destined to unknown unicast is sent on the epg uplink ...
#
pkt_intra_epg_220_to_uplink = (Ether(src=self.pg0.remote_mac,
dst="00:00:00:33:44:55") /
IP(src=eps[0].ip4.address,
dst="10.0.0.99") /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_expect_bridged(self.pg0,
pkt_intra_epg_220_to_uplink * 65,
self.pg4)
# ... and nowhere else
self.pg1.get_capture(0, timeout=0.1)
self.pg1.assert_nothing_captured(remark="Flood onto other VMS")
pkt_intra_epg_221_to_uplink = (Ether(src=self.pg2.remote_mac,
dst="00:00:00:33:44:66") /
IP(src=eps[0].ip4.address,
dst="10.0.0.99") /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_expect_bridged(self.pg2,
pkt_intra_epg_221_to_uplink * 65,
self.pg5)
#
# Packets from the uplink are forwarded in the absence of a contract
#
pkt_intra_epg_220_from_uplink = (Ether(src="00:00:00:33:44:55",
dst=self.pg0.remote_mac) /
IP(src=eps[0].ip4.address,
dst="10.0.0.99") /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_expect_bridged(self.pg4,
pkt_intra_epg_220_from_uplink * 65,
self.pg0)
#
# in the absence of policy, endpoints in the same EPG
# can communicate
#
pkt_intra_epg = (Ether(src=self.pg0.remote_mac,
dst=self.pg1.remote_mac) /
IP(src=eps[0].ip4.address,
dst=eps[1].ip4.address) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1)
#
# in the abscense of policy, endpoints in the different EPG
# cannot communicate
#
pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac,
dst=self.pg2.remote_mac) /
IP(src=eps[0].ip4.address,
dst=eps[2].ip4.address) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac,
dst=self.pg0.remote_mac) /
IP(src=eps[2].ip4.address,
dst=eps[0].ip4.address) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac,
dst=self.router_mac) /
IP(src=eps[0].ip4.address,
dst=eps[3].ip4.address) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_assert_no_replies(self.pg0,
pkt_inter_epg_220_to_221 * 65)
self.send_and_assert_no_replies(self.pg0,
pkt_inter_epg_220_to_222 * 65)
#
# A uni-directional contract from EPG 220 -> 221
#
acl = VppGbpAcl(self)
rule = acl.create_rule(permit_deny=1, proto=17)
rule2 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17)
acl_index = acl.add_vpp_config([rule, rule2])
c1 = VppGbpContract(self, 220, 221, acl_index)
c1.add_vpp_config()
self.send_and_expect_bridged(self.pg0,
pkt_inter_epg_220_to_221 * 65,
self.pg2)
self.send_and_assert_no_replies(self.pg0,
pkt_inter_epg_220_to_222 * 65)
#
# contract for the return direction
#
c2 = VppGbpContract(self, 221, 220, acl_index)
c2.add_vpp_config()
self.send_and_expect_bridged(self.pg0,
pkt_inter_epg_220_to_221 * 65,
self.pg2)
self.send_and_expect_bridged(self.pg2,
pkt_inter_epg_221_to_220 * 65,
self.pg0)
#
# check that inter group is still disabled for the groups
# not in the contract.
#
self.send_and_assert_no_replies(self.pg0,
pkt_inter_epg_220_to_222 * 65)
#
# A uni-directional contract from EPG 220 -> 222 'L3 routed'
#
c3 = VppGbpContract(self, 220, 222, acl_index)
c3.add_vpp_config()
self.logger.info(self.vapi.cli("sh gbp contract"))
self.send_and_expect_routed(self.pg0,
pkt_inter_epg_220_to_222 * 65,
self.pg3,
self.router_mac)
#
# remove both contracts, traffic stops in both directions
#
c2.remove_vpp_config()
c1.remove_vpp_config()
c3.remove_vpp_config()
acl.remove_vpp_config()
self.send_and_assert_no_replies(self.pg2,
pkt_inter_epg_221_to_220 * 65)
self.send_and_assert_no_replies(self.pg0,
pkt_inter_epg_220_to_221 * 65)
self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1)
#
# EPs to the outside world
#
# in the EP's RD an external subnet via the NAT EPG's recirc
se1 = VppGbpSubnet(self, 0, "0.0.0.0", 0,
is_internal=False,
sw_if_index=recirc_nat.recirc.sw_if_index,
epg=epg_nat.epg)
se1.add_vpp_config()
se2 = VppGbpSubnet(self, 0, "11.0.0.0", 8,
is_internal=False,
sw_if_index=recirc_nat.recirc.sw_if_index,
epg=epg_nat.epg)
se2.add_vpp_config()
se16 = VppGbpSubnet(self, 0, "::", 0,
is_internal=False,
sw_if_index=recirc_nat.recirc.sw_if_index,
epg=epg_nat.epg)
se16.add_vpp_config()
# in the NAT RD an external subnet via the NAT EPG's uplink
se3 = VppGbpSubnet(self, 20, "0.0.0.0", 0,
is_internal=False,
sw_if_index=epg_nat.uplink.sw_if_index,
epg=epg_nat.epg)
se36 = VppGbpSubnet(self, 20, "::", 0,
is_internal=False,
sw_if_index=epg_nat.uplink.sw_if_index,
epg=epg_nat.epg)
se4 = VppGbpSubnet(self, 20, "11.0.0.0", 8,
is_internal=False,
sw_if_index=epg_nat.uplink.sw_if_index,
epg=epg_nat.epg)
se3.add_vpp_config()
se36.add_vpp_config()
se4.add_vpp_config()
self.logger.info(self.vapi.cli("sh ip fib 0.0.0.0/0"))
self.logger.info(self.vapi.cli("sh ip fib 11.0.0.1"))
self.logger.info(self.vapi.cli("sh ip6 fib ::/0"))
self.logger.info(self.vapi.cli("sh ip6 fib %s" %
eps[0].fip6))
#
# From an EP to an outside addess: IN2OUT
#
pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac,
dst=self.router_mac) /
IP(src=eps[0].ip4.address,
dst="1.1.1.1") /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
# no policy yet
self.send_and_assert_no_replies(self.pg0,
pkt_inter_epg_220_to_global * 65)
acl2 = VppGbpAcl(self)
rule = acl2.create_rule(permit_deny=1, proto=17, sport_from=1234,
sport_to=1234, dport_from=1234, dport_to=1234)
rule2 = acl2.create_rule(is_ipv6=1, permit_deny=1, proto=17,
sport_from=1234, sport_to=1234,
dport_from=1234, dport_to=1234)
acl_index2 = acl2.add_vpp_config([rule, rule2])
c4 = VppGbpContract(self, 220, 333, acl_index2)
c4.add_vpp_config()
self.send_and_expect_natted(self.pg0,
pkt_inter_epg_220_to_global * 65,
self.pg7,
eps[0].fip4.address)
pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac,
dst=self.router_mac) /
IPv6(src=eps[0].ip6.address,
dst="6001::1") /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_expect_natted6(self.pg0,
pkt_inter_epg_220_to_global * 65,
self.pg7,
eps[0].fip6.address)
#
# From a global address to an EP: OUT2IN
#
pkt_inter_epg_220_from_global = (Ether(src=self.router_mac,
dst=self.pg0.remote_mac) /
IP(dst=eps[0].fip4.address,
src="1.1.1.1") /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_assert_no_replies(self.pg7,
pkt_inter_epg_220_from_global * 65)
c5 = VppGbpContract(self, 333, 220, acl_index2)
c5.add_vpp_config()
self.send_and_expect_unnatted(self.pg7,
pkt_inter_epg_220_from_global * 65,
eps[0].itf,
eps[0].ip4.address)
pkt_inter_epg_220_from_global = (Ether(src=self.router_mac,
dst=self.pg0.remote_mac) /
IPv6(dst=eps[0].fip6.address,
src="6001::1") /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_expect_unnatted6(self.pg7,
pkt_inter_epg_220_from_global * 65,
eps[0].itf,
eps[0].ip6.address)
#
# From a local VM to another local VM using resp. public addresses:
# IN2OUT2IN
#
pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac,
dst=self.router_mac) /
IP(src=eps[0].ip4.address,
dst=eps[1].fip4.address) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_expect_double_natted(eps[0].itf,
pkt_intra_epg_220_global * 65,
eps[1].itf,
eps[0].fip4.address,
eps[1].ip4.address)
pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac,
dst=self.router_mac) /
IPv6(src=eps[0].ip6.address,
dst=eps[1].fip6.address) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_expect_double_natted6(eps[0].itf,
pkt_intra_epg_220_global * 65,
eps[1].itf,
eps[0].fip6.address,
eps[1].ip6.address)
#
# cleanup
#
for ep in eps:
# del static mappings for each EP from the 10/8 to 11/8 network
self.vapi.nat44_add_del_static_mapping(ep.ip4.bytes,
ep.fip4.bytes,
vrf_id=0,
addr_only=1,
is_add=0)
self.vapi.nat66_add_del_static_mapping(ep.ip6.bytes,
ep.fip6.bytes,
vrf_id=0,
is_add=0)
for epg in epgs:
# IP config on the BVI interfaces
self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
epg.bvi_ip4_n,
32,
is_add=0)
self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
epg.bvi_ip6_n,
128,
is_add=0,
is_ipv6=True)
self.logger.info(self.vapi.cli("sh int addr"))
epg.uplink.set_table_ip4(0)
epg.uplink.set_table_ip6(0)
if epg != epgs[0] and epg != epgs[3]:
epg.bvi.set_table_ip4(0)
epg.bvi.set_table_ip6(0)
self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index,
is_inside=1,
is_add=0)
self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index,
is_inside=1,
is_add=0)
for recirc in recircs:
recirc.recirc.set_table_ip4(0)
recirc.recirc.set_table_ip6(0)
self.vapi.nat44_interface_add_del_feature(
recirc.recirc.sw_if_index,
is_inside=0,
is_add=0)
self.vapi.nat66_add_del_interface(
recirc.recirc.sw_if_index,
is_inside=0,
is_add=0)
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)