/* * l2_bd.c : layer 2 bridge domain * * Copyright (c) 2013 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * @file * @brief Ethernet Bridge Domain. * * Code in this file manages Layer 2 bridge domains. * */ bd_main_t bd_main; /** Init bridge domain if not done already. For feature bitmap, set all bits except ARP termination */ void bd_validate (l2_bridge_domain_t * bd_config) { if (bd_is_valid (bd_config)) return; bd_config->feature_bitmap = ~L2INPUT_FEAT_ARP_TERM; bd_config->bvi_sw_if_index = ~0; bd_config->members = 0; bd_config->flood_count = 0; bd_config->tun_master_count = 0; bd_config->tun_normal_count = 0; bd_config->mac_by_ip4 = 0; bd_config->mac_by_ip6 = hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword)); } u32 bd_find_index (bd_main_t * bdm, u32 bd_id) { u32 *p = (u32 *) hash_get (bdm->bd_index_by_bd_id, bd_id); if (!p) return ~0; return p[0]; } u32 bd_add_bd_index (bd_main_t * bdm, u32 bd_id) { ASSERT (!hash_get (bdm->bd_index_by_bd_id, bd_id)); u32 rv = clib_bitmap_first_clear (bdm->bd_index_bitmap); /* mark this index taken */ bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, rv, 1); hash_set (bdm->bd_index_by_bd_id, bd_id, rv); vec_validate (l2input_main.bd_configs, rv); l2input_main.bd_configs[rv].bd_id = bd_id; return rv; } static int bd_delete (bd_main_t * bdm, u32 bd_index) { l2_bridge_domain_t *bd = &l2input_main.bd_configs[bd_index]; u32 bd_id = bd->bd_id; u64 mac_addr; ip6_address_t *ip6_addr_key; /* flush non-static MACs in BD and removed bd_id from hash table */ l2fib_flush_bd_mac (vlib_get_main (), bd_index); hash_unset (bdm->bd_index_by_bd_id, bd_id); /* mark this index clear */ bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, bd_index, 0); /* clear BD config for reuse: bd_id to -1 and clear feature_bitmap */ bd->bd_id = ~0; bd->feature_bitmap = 0; /* free memory used by BD */ vec_free (bd->members); hash_free (bd->mac_by_ip4); /* *INDENT-OFF* */ hash_foreach_mem (ip6_addr_key, mac_addr, bd->mac_by_ip6, ({ clib_mem_free (ip6_addr_key); /* free memory used for ip6 addr key */ })); /* *INDENT-ON* */ hash_free (bd->mac_by_ip6); return 0; } static void update_flood_count (l2_bridge_domain_t * bd_config) { bd_config->flood_count = vec_len (bd_config->members) - (bd_config->tun_master_count ? bd_config->tun_normal_count : 0); } void bd_add_member (l2_bridge_domain_t * bd_config, l2_flood_member_t * member) { u32 ix; vnet_sw_interface_t *sw_if = vnet_get_sw_interface (vnet_get_main (), member->sw_if_index); /* * Add one element to the vector * vector is ordered [ bvi, normal/tun_masters..., tun_normals... ] * When flooding, the bvi interface (if present) must be the last member * processed due to how BVI processing can change the packet. To enable * this order, we make the bvi interface the first in the vector and * flooding walks the vector in reverse. */ switch (sw_if->flood_class) { case VNET_FLOOD_CLASS_TUNNEL_MASTER: bd_config->tun_master_count++; /* Fall through */ default: /* Fall through */ case VNET_FLOOD_CLASS_NORMAL: ix = (member->flags & L2_FLOOD_MEMBER_BVI) ? 0 : vec_len (bd_config->members) - bd_config->tun_normal_count; break; case VNET_FLOOD_CLASS_TUNNEL_NORMAL: ix = vec_len (bd_config->members); bd_config->tun_normal_count++; break; } vec_insert_elts (bd_config->members, member, 1, ix); update_flood_count (bd_config); } #define BD_REMOVE_ERROR_OK 0 #define BD_REMOVE_ERROR_NOT_FOUND 1 u32 bd_remove_member (l2_bridge_domain_t * bd_config, u32 sw_if_index) { u32 ix; /* Find and delete the member */ vec_foreach_index (ix, bd_config->members) { l2_flood_member_t *m = vec_elt_at_index (bd_config->members, ix); if (m->sw_if_index == sw_if_index) { vnet_sw_interface_t *sw_if = vnet_get_sw_interface (vnet_get_main (), sw_if_index); if (sw_if->flood_class != VNET_FLOOD_CLASS_NORMAL) { if (sw_if->flood_class == VNET_FLOOD_CLASS_TUNNEL_MASTER) bd_config->tun_master_count--; else if (sw_if->flood_class == VNET_FLOOD_CLASS_TUNNEL_NORMAL) bd_config->tun_normal_count--; } vec_delete (bd_config->members, 1, ix); update_flood_count (bd_config); return BD_REMOVE_ERROR_OK; } } return BD_REMOVE_ERROR_NOT_FOUND; } clib_error_t * l2bd_init (vlib_main_t * vm) { bd_main_t *bdm = &bd_main; bdm->bd_index_by_bd_id = hash_create (0, sizeof (uword)); /* * create a dummy bd with bd_id of 0 and bd_index of 0 with feature set * to packet drop only. Thus, packets received from any L2 interface with * uninitialized bd_index of 0 can be dropped safely. */ u32 bd_index = bd_add_bd_index (bdm, 0); ASSERT (bd_index == 0); l2input_main.bd_configs[0].feature_bitmap = L2INPUT_FEAT_DROP; bdm->vlib_main = vm; return 0; } VLIB_INIT_FUNCTION (l2bd_init); /** Set the learn/forward/flood flags for the bridge domain. Return 0 if ok, non-zero if for an error. */ u32 bd_set_flags (vlib_main_t * vm, u32 bd_index, u32 flags, u32 enable) { l2_bridge_domain_t *bd_config = l2input_bd_config (bd_index); bd_validate (bd_config); u32 feature_bitmap = 0; if (flags & L2_LEARN) { feature_bitmap |= L2INPUT_FEAT_LEARN; } if (flags & L2_FWD) { feature_bitmap |= L2INPUT_FEAT_FWD; } if (flags & L2_FLOOD) { feature_bitmap |= L2INPUT_FEAT_FLOOD; } if (flags & L2_UU_FLOOD) { feature_bitmap |= L2INPUT_FEAT_UU_FLOOD; } if (flags & L2_ARP_TERM) { feature_bitmap |= L2INPUT_FEAT_ARP_TERM; } if (enable) { bd_config->feature_bitmap |= feature_bitmap; } else { bd_config->feature_bitmap &= ~feature_bitmap; } return 0; } /** Set the mac age for the bridge domain. */ void bd_set_mac_age (vlib_main_t * vm, u32 bd_index, u8 age) { l2_bridge_domain_t *bd_config; int enable = 0; vec_validate (l2input_main.bd_configs, bd_index); bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index); bd_config->mac_age = age; /* check if there is at least one bd with mac aging enabled */ vec_foreach (bd_config, l2input_main.bd_configs) enable |= bd_config->bd_id != ~0 && bd_config->mac_age != 0; vlib_process_signal_event (vm, l2fib_mac_age_scanner_process_node.index, enable ? L2_MAC_AGE_PROCESS_EVENT_START : L2_MAC_AGE_PROCESS_EVENT_STOP, 0); } /** Set bridge-domain learn enable/disable. The CLI format is: set bridge-domain learn [disable] */ static clib_error_t * bd_learn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { bd_main_t *bdm = &bd_main; clib_error_t *error = 0; u32 bd_index, bd_id; u32 enable; uword *p; if (!unformat (input, "%d", &bd_id)) { error = clib_error_return (0, "expecting bridge-domain id but got `%U'", format_unformat_error, input); goto done; } if (bd_id == 0) return clib_error_return (0, "No operations on the default bridge domain are supported"); p = hash_get (bdm->bd_index_by_bd_id, bd_id); if (p == 0) return clib_error_return (0, "No such bridge domain %d", bd_id); bd_index = p[0]; enable = 1; if (unformat (input, "disable")) { enable = 0; } /* set the bridge domain flag */ if (bd_set_flags (vm, bd_index, L2_LEARN, enable)) { error = clib_error_return (0, "bridge-domain id %d out of range", bd_index); goto done; } done: return error; } /*? * Layer 2 learning can be enabled and disabled on each * interface and on each bridge-domain. Use this
#!/usr/bin/env python3
""" Container integration tests """

import unittest
from framework import VppTestCase, VppTestRunner, running_extended_tests
from scapy.layers.l2 import Ether
from scapy.packet import Raw
from scapy.layers.inet import IP, UDP, TCP
from scapy.packet import Packet
from socket import inet_pton, AF_INET, AF_INET6
from scapy.layers.inet6 import IPv6, ICMPv6Unknown, ICMPv6EchoRequest
from scapy.layers.inet6 import ICMPv6EchoReply, IPv6ExtHdrRouting
from scapy.layers.inet6 import IPv6ExtHdrFragment
from pprint import pprint
from random import randint
from util import L4_Conn


class Conn(L4_Conn):
    # for now same as L4_Conn
    pass


@unittest.skipUnless(running_extended_tests, "part of extended tests")
class ContainerIntegrationTestCase(VppTestCase):
    """ Container integration extended testcases """

    @classmethod
    def setUpClass(cls):
        super(ContainerIntegrationTestCase, cls).setUpClass()
        # create pg0 and pg1
        cls.create_pg_interfaces(range(2))
        for i in cls.pg_interfaces:
            i.admin_up()
            i.config_ip4()
            i.config_ip6()
            i.resolve_arp()
            i.resolve_ndp()

    @classmethod
    def tearDownClass(cls):
        super(ContainerIntegrationTestCase, cls).tearDownClass()

    def tearDown(self):
        """Run standard test teardown and log various show commands
        """
        super(ContainerIntegrationTestCase, self).tearDown()

    def show_commands_at_teardown(self):
        self.logger.info(self.vapi.cli("show ip neighbors"))

    def run_basic_conn_test(self, af, acl_side):
        """ Basic connectivity test """
        conn1 = Conn(self, self.pg0, self.pg1, af, UDP, 42001, 4242)
        conn1.send_through(0)
        # the return packets should pass
        conn1.send_through(1)

    def run_negative_conn_test(self, af, acl_side):
        """ Packets with local spoofed address """
        conn1 = Conn(self, self.pg0, self.pg1, af, UDP, 42001, 4242)
        try:
            p2 = conn1.send_through(0).command()
        except:
            # If we asserted while waiting, it's good.
            # the conn should have timed out.
            p2 = None
        self.assert_equal(p2, None, ": packet should have been dropped")

    def test_0010_basic_conn_test(self):
        """ IPv4 basic connectivity test """
        self.run_basic_conn_test(AF_INET, 0)

    def test_0011_basic_conn_test(self):
        """ IPv6 basic connectivity test """
        self.run_basic_conn_test(AF_INET6, 0)

    def test_0050_loopback_prepare_test(self):
        """ Create loopbacks overlapping with remote addresses """
        self.create_loopback_interfaces(2)
        for i in range(2):
            intf = self.lo_interfaces[i]
            intf.admin_up()
            intf.local_ip4 = self.pg_interfaces[i].remote_ip4
            intf.local_ip4_prefix_len = 32
            intf.config_ip4()
            intf.local_ip6 = self.pg_interfaces[i].remote_ip6
            intf.local_ip6_prefix_len = 128
            intf.config_ip6()

    def test_0110_basic_conn_test(self):
        """ IPv4 local-spoof connectivity test """
        self.run_negative_conn_test(AF_INET, 0)

    def test_0111_basic_conn_test(self):
        """ IPv6 local-spoof connectivity test """
        self.run_negative_conn_test(AF_INET, 1)

    def test_0200_basic_conn_test(self):
        """ Configure container commands """
        for i in range(2):
            for addr in [self.pg_interfaces[i].remote_ip4,
                         self.pg_interfaces[i].remote_ip6]:
                self.vapi.ppcli("ip container " + addr + " " +
                                self.pg_interfaces[i].name)
                self.vapi.ppcli("stn rule address " + addr +
                                " interface " + self.pg_interfaces[i].name)

    def test_0210_basic_conn_test(self):
        """ IPv4 test after configuring container """
        self.run_basic_conn_test(AF_INET, 0)

    def test_0211_basic_conn_test(self):
        """ IPv6 test after configuring container """
        self.run_basic_conn_test(AF_INET, 1)

    def test_0300_unconfigure_commands(self):
        """ Unconfigure container commands """
        for i in range(2):
            for addr in [self.pg_interfaces[i].remote_ip4,
                         self.pg_interfaces[i].