/* *------------------------------------------------------------------ * ip_api.c - vnet ip api * * 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. *------------------------------------------------------------------ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define vl_typedefs /* define message structures */ #include #undef vl_typedefs #define vl_endianfun /* define message structures */ #include #undef vl_endianfun /* instantiate all the print functions we know about */ #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) #define vl_printfun #include #undef vl_printfun #include #define foreach_ip_api_msg \ _(IP_FIB_DUMP, ip_fib_dump) \ _(IP6_FIB_DUMP, ip6_fib_dump) \ _(IP_MFIB_DUMP, ip_mfib_dump) \ _(IP6_MFIB_DUMP, ip6_mfib_dump) \ _(IP_NEIGHBOR_DUMP, ip_neighbor_dump) \ _(IP_MROUTE_ADD_DEL, ip_mroute_add_del) \ _(MFIB_SIGNAL_DUMP, mfib_signal_dump) \ _(IP_ADDRESS_DUMP, ip_address_dump) \ _(IP_UNNUMBERED_DUMP, ip_unnumbered_dump) \ _(IP_DUMP, ip_dump) \ _(IP_NEIGHBOR_ADD_DEL, ip_neighbor_add_del) \ _(SET_ARP_NEIGHBOR_LIMIT, set_arp_neighbor_limit) \ _(IP_PROBE_NEIGHBOR, ip_probe_neighbor) \ _(IP_SCAN_NEIGHBOR_ENABLE_DISABLE, ip_scan_neighbor_enable_disable) \ _(WANT_IP4_ARP_EVENTS, want_ip4_arp_events) \ _(WANT_IP6_ND_EVENTS, want_ip6_nd_events) \ _(WANT_IP6_RA_EVENTS, want_ip6_ra_events) \ _(PROXY_ARP_ADD_DEL, proxy_arp_add_del) \ _(PROXY_ARP_DUMP, proxy_arp_dump) \ _(PROXY_ARP_INTFC_ENABLE_DISABLE, proxy_arp_intfc_enable_disable) \ _(PROXY_ARP_INTFC_DUMP, proxy_arp_intfc_dump) \ _(RESET_FIB, reset_fib) \ _(IP_ADD_DEL_ROUTE, ip_add_del_route) \ _(IP_TABLE_ADD_DEL, ip_table_add_del) \ _(IP_PUNT_POLICE, ip_punt_police) \ _(IP_PUNT_REDIRECT, ip_punt_redirect) \ _(SET_IP_FLOW_HASH,set_ip_flow_hash) \ _(SW_INTERFACE_IP6ND_RA_CONFIG, sw_interface_ip6nd_ra_config) \ _(SW_INTERFACE_IP6ND_RA_PREFIX, sw_interface_ip6nd_ra_prefix) \ _(IP6ND_PROXY_ADD_DEL, ip6nd_proxy_add_del) \ _(IP6ND_PROXY_DUMP, ip6nd_proxy_dump) \ _(IP6ND_SEND_ROUTER_SOLICITATION, ip6nd_send_router_solicitation) \ _(SW_INTERFACE_IP6_ENABLE_DISABLE, sw_interface_ip6_enable_disable ) \ _(SW_INTERFACE_IP6_SET_LINK_LOCAL_ADDRESS, \ sw_interface_ip6_set_link_local_address) \ _(IP_CONTAINER_PROXY_ADD_DEL, ip_container_proxy_add_del) \ _(IOAM_ENABLE, ioam_enable) \ _(IOAM_DISABLE, ioam_disable) \ _(IP_SOURCE_AND_PORT_RANGE_CHECK_ADD_DEL, \ ip_source_and_port_range_check_add_del) \ _(IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL, \ ip_source_and_port_range_check_interface_add_del) \ _(IP_REASSEMBLY_SET, ip_reassembly_set) \ _(IP_REASSEMBLY_GET, ip_reassembly_get) \ _(IP_REASSEMBLY_ENABLE_DISABLE, ip_reassembly_enable_disable) extern void stats_dslock_with_hint (int hint, int tag); extern void stats_dsunlock (void); static void send_ip_neighbor_details (u32 sw_if_index, u8 is_ipv6, u8 is_static, u8 * mac_address, u8 * ip_address, vl_api_registration_t * reg, u32 context) { vl_api_ip_neighbor_details_t *mp; mp = vl_msg_api_alloc (sizeof (*mp)); memset (mp, 0, sizeof (*mp)); mp->_vl_msg_id = ntohs (VL_API_IP_NEIGHBOR_DETAILS); mp->context = context; mp->sw_if_index = htonl (sw_if_index); mp->is_ipv6 = is_ipv6; mp->is_static = is_static; memcpy (mp->mac_address, mac_address, 6); memcpy (mp->ip_address, ip_address, (is_ipv6) ? 16 : 4); vl_api_send_msg (reg, (u8 *) mp); } static void vl_api_ip_neighbor_dump_t_handler (vl_api_ip_neighbor_dump_t * mp) { vl_api_registration_t *reg; reg = vl_api_client_index_to_registration (mp->client_index); if (!reg) return; u32 sw_if_index = ntohl (mp->sw_if_index); if (mp->is_ipv6) { ip6_neighbor_t *n, *ns; ns = ip6_neighbors_entries (sw_if_index); /* *INDENT-OFF* */ vec_foreach (n, ns) { send_ip_neighbor_details (n->key.sw_if_index, mp->is_ipv6, ((n->flags & IP6_NEIGHBOR_FLAG_STATIC) ? 1 : 0), (u8 *) n->link_layer_address, (u8 *) & (n->key.ip6_address.as_u8), reg, mp->context); } /* *INDENT-ON* */ vec_free (ns); } else { ethernet_arp_ip4_entry_t *n, *ns; ns = ip4_neighbor_entries (sw_if_index); /* *INDENT-OFF* */ vec_foreach (n, ns) { send_ip_neighbor_details (n->sw_if_index, mp->is_ipv6, ((n->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC) ? 1 : 0), (u8*) n->ethernet_address, (u8*) & (n->ip4_address.as_u8), reg, mp->context); } /* *INDENT-ON* */ vec_free (ns); } } static void send_ip_fib_details (vpe_api_main_t * am, vl_api_registration_t * reg, const fib_table_t * table, const fib_prefix_t * pfx, fib_route_path_encode_t * api_rpaths, u32 context) { vl_api_ip_fib_details_t *mp; fib_route_path_encode_t *api_rpath; vl_api_fib_path_t *fp; int path_count; path_count = vec_len (api_rpaths); mp = vl_msg_api_alloc (sizeof (*mp) + path_count * sizeof (*fp)); if (!mp) return; memset (mp, 0, sizeof (*mp)); mp->_vl_msg_id = ntohs (VL_API_IP_FIB_DETAILS); mp->context = context; mp->table_id = htonl (table->ft_table_id); memcpy (mp->table_name, table->ft_desc, clib_min (vec_len (table->ft_desc), sizeof (mp->table_name))); mp->address_length = pfx->fp_len; memcpy (mp->address, &pfx->fp_addr.ip4, sizeof (pfx->fp_addr.ip4)); mp->stats_index = htonl (fib_table_entry_get_stats_index (table->ft_index, pfx)); mp->count = htonl (path_count); fp = mp->path; vec_foreach (api_rpath, api_rpaths) { fib_api_path_encode (api_rpath, fp); fp++; } vl_api_send_msg (reg, (u8 *) mp); } typedef struct vl_api_ip_fib_dump_walk_ctx_t_ { fib_node_index_t *feis; } vl_api_ip_fib_dump_walk_ctx_t; static fib_table_walk_rc_t vl_api_ip_fib_dump_walk (fib_node_index_t fei, void *arg) { vl_api_ip_fib_dump_walk_ctx_t *ctx = arg; vec_add1 (ctx->feis, fei); return (FIB_TABLE_WALK_CONTINUE); } static void vl_api_ip_fib_dump_t_handler (vl_api_ip_fib_dump_t * mp) { vpe_api_main_t *am = &vpe_api_main; vl_api_registration_t *reg; ip4_main_t *im = &ip4_main; fib_table_t *fib_table; fib_node_index_t *lfeip; const fib_prefix_t *pfx; u32 fib_index; fib_route_path_encode_t *api_rpaths; vl_api_ip_fib_dump_walk_ctx_t ctx = { .feis = NULL, }; reg = vl_api_client_index_to_registration (mp->client_index); if (!reg) return; /* *INDENT-OFF* */ pool_foreach (fib_table, im->fibs, ({ fib_table_walk(fib_table->ft_index, FIB_PROTOCOL_IP4, vl_api_ip_fib_dump_walk, &ctx); })); /* *INDENT-ON* */ vec_sort_with_function (ctx.feis, fib_entry_cmp_for_sort); vec_foreach (lfeip, ctx.feis) { pfx = fib_entry_get_prefix (*lfeip); fib_index = fib_entry_get_fib_index (*lfeip); fib_table = fib_table_get (fib_index, pfx->fp_proto); api_rpaths = NULL; fib_entry_encode (*lfeip, &api_rpaths); send_ip_fib_details (am, reg, fib_table, pfx, api_rpaths, mp->context); vec_free (api_rpaths); } vec_free (ctx.feis); } static void send_ip6_fib_details (vpe_api_main_t * am, vl_api_registration_t * reg, const fib_table_t * table, const fib_prefix_t * pfx, fib_route_path_encode_t * api_rpaths, u32 context) { vl_api_ip6_fib_details_t *mp; fib_route_path_encode_t *api_rpath; vl_api_fib_path_t *fp; int path_count; path_count = vec_len (api_rpaths); mp = vl_msg_api_alloc (sizeof (*mp) + path_count * sizeof (*fp)); if (!mp) return; memset (mp, 0, sizeof (*mp)); mp->_vl_msg_id = ntohs (VL_API_IP6_FIB_DETAILS); mp->context = context; mp->table_id = htonl (table->ft_table_id); mp->address_length = pfx->fp_len; memcpy (mp->address, &pfx->fp_addr.ip6, sizeof (pfx->fp_addr.ip6)); memcpy (mp->table_name, table->ft_desc, clib_min (vec_len (table->ft_desc), sizeof (mp->table_name))); mp->stats_index = htonl (fib_table_entry_get_stats_index (table->ft_index, pfx)); mp->count = htonl (path_count); fp = mp->path; vec_foreach (api_rpath, api_rpaths) { fib_api_path_encode (api_rpath, fp); fp++; } vl_api_send_msg (reg, (u8 *) mp); } typedef struct apt_ip6_fib_show_ctx_t_ { u32 fib_index; fib_node_index_t *entries; } api_ip6_fib_show_ctx_t; static void api_ip6_fib_table_put_entries (clib_bihash_kv_24_8_t * kvp, void *arg) { api_ip6_fib_show_ctx_t *ctx = arg; if ((kvp->key[2] >> 32) == ctx->fib_index) { vec_add1 (ctx->entries, kvp->value); } } static void api_ip6_fib_table_get_all (vl_api_registration_t * reg, vl_api_ip6_fib_dump_t * mp, fib_table_t * fib_table) { vpe_api_main_t *am = &vpe_api_main; ip6_main_t *im6 = &ip6_main; fib_node_index_t *fib_entry_index; api_ip6_fib_show_ctx_t ctx = { .fib_index = fib_table->ft_index, .entries = NULL, }; fib_route_path_encode_t *api_rpaths; const fib_prefix_t *pfx; BV (clib_bihash_foreach_key_value_pair) ((BVT (clib_bihash) *) & im6->ip6_table[IP6_FIB_TABLE_NON_FWDING]. ip6_hash, api_ip6_fib_table_put_entries, &ctx); vec_sort_with_function (ctx.entries, fib_entry_cmp_for_sort); vec_foreach (fib_entry_index, ctx.entries) { pfx = fib_entry_get_prefix (*fib_entry_index); api_rpaths = NULL; fib_entry_encode (*fib_entry_index, &api_rpaths); send_ip6_fib_details (am, reg, fib_table, pfx, api_rpaths, mp->context); vec_free (api_rpaths); } vec_free (ctx.entries); } static void vl_api_ip6_fib_dump_t_handler (vl_api_ip6_fib_dump_t * mp) { vl_api_registration_t *reg; ip6_main_t *im6 = &ip6_main; fib_table_t *fib_table; reg = vl_api_client_index_to_registration (mp->client_index); if (!reg) return; /* *INDENT-OFF* */ pool_foreach (fib_table, im6->fibs, ({ /* don't send link locals */ if (fib_table->ft_flags & FIB_TABLE_FLAG_IP6_LL) continue; api_ip6_fib_table_get_all(reg, mp, fib_table); })); /* *INDENT-ON* */ } static void send_ip_mfib_details (vl_api_registration_t * reg, u32 context, u32 table_id, fib_node_index_t mfei) { fib_route_path_encode_t *api_rpath, *api_rpaths = NULL; vl_api_ip_mfib_details_t *mp; mfib_entry_t *mfib_entry; vl_api_fib_path_t *fp; mfib_prefix_t pfx; int path_count; mfib_entry = mfib_entry_get (mfei); mfib_entry_get_prefix (mfei, &pfx); mfib_entry_encode (mfei, &api_rpaths); path_count = vec_len (api_rpaths); mp = vl_msg_api_alloc (sizeof (*mp) + path_count * sizeof (*fp)); if (!mp) return; memset (mp, 0, sizeof (*mp)); mp->_vl_msg_id = ntohs (VL_API_IP_MFIB_DETAILS); mp->context = context; mp->rpf_id = mfib_entry->mfe_rpf_id; mp->entry_flags = mfib_entry->mfe_flags; mp->table_id = htonl (table_id); mp->address_length = pfx.fp_len; memcpy (mp->grp_address, &pfx.fp_grp_addr.ip4, sizeof (pfx.fp_grp_addr.ip4)); memcpy (mp->src_address, &pfx.fp_src_addr.ip4, sizeof (pfx.fp_src_addr.ip4)); mp->count = htonl (path_count); fp = mp->path; vec_foreach (api_rpath, api_rpaths) { fib_api_path_encode (api_rpath, fp); fp++; } vec_free (api_rpaths); vl_api_send_msg (reg, (u8 *) mp); } typedef struct vl_api_ip_mfib_dump_ctc_t_ { fib_node_index_t *entries; } vl_api_ip_mfib_dump_ctc_t; static int vl_api_ip_mfib_table_dump_walk (fib_node_index_t fei, void *arg) { vl_api_ip_mfib_dump_ctc_t *ctx = arg; vec_add1 (ctx->entries, fei); return (0); } static void vl_api_ip_mfib_dump_t_handler (vl_api_ip_mfib_dump_t * mp) { vl_api_registration_t *reg; ip4_main_t *im = &ip4_main; mfib_table_t *mfib_table; fib_node_index_t *mfeip; vl_api_ip_mfib_dump_ctc_t ctx = { .entries = NULL, }; reg = vl_api_client_index_to_registration (mp->client_index); if (!reg) return; /* *INDENT-OFF* */ pool_foreach (mfib_table, im->mfibs, ({ ip4_mfib_table_walk(&mfib_table->v4, vl_api_ip_mfib_table_dump_walk, &ctx); vec_sort_with_function (ctx.entries, mfib_entry_cmp_for_sort); vec_foreach (mfeip, ctx.entries) { send_ip_mfib_details (reg, mp->context, mfib_table->mft_table_id, *mfeip); } vec_reset_length (ctx.entries); })); /* *INDENT-ON* */ vec_free (ctx.entries); } static void send_ip6_mfib_details (vpe_api_main_t * am, vl_api_registration_t * reg, u32 table_id, mfib_prefix_t * pfx, fib_route_path_encode_t * api_rpaths, u32 context) { vl_api_ip6_mfib_details_t *mp; fib_route_path_encode_t *api_rpath; vl_api_fib_path_t *fp; int path_count; path_count = vec_len (api_rpaths); mp = vl_msg_api_alloc (sizeof (*mp) + path_count * sizeof (*fp)); if (!mp) return; memset (mp, 0, sizeof (*mp)); mp->_vl_msg_id = ntohs (VL_API_IP6_MFIB_DETAILS); mp->context = context; mp->table_id = htonl (table_id); mp->address_length = pfx->fp_len; memcpy (mp->grp_address, &pfx->fp_grp_addr.ip6, sizeof (pfx->fp_grp_addr.ip6)); memcpy (mp->src_address, &pfx->fp_src_addr.ip6, sizeof (pfx->fp_src_addr.ip6)); mp->count = htonl (path_count); fp = mp->path; vec_foreach (api_rpath, api_rpaths) { fib_api_path_encode (api_rpath, fp); fp++; } vl_api_send_msg (reg, (u8 *) mp); } typedef struct vl_api_ip6_mfib_dump_ctc_t_ { fib_node_index_t *entries; } vl_api_ip6_mfib_dump_ctc_t; static int vl_api_ip6_mfib_table_dump_walk (fib_node_index_t fei, void *arg) { vl_api_ip6_mfib_dump_ctc_t *ctx = arg; vec_add1 (ctx->entries, fei); return (0); } static void vl_api_ip6_mfib_dump_t_handler (vl_api_ip6_mfib_dump_t * mp) { vpe_api_main_t *am = &vpe_api_main; vl_api_registration_t *reg; ip6_main_t *im = &ip6_main; mfib_table_t *mfib_table; fib_node_index_t *mfeip; mfib_prefix_t pfx; fib_route_path_encode_t *api_rpaths = NULL; vl_api_ip6_mfib_dump_ctc_t ctx = { .entries = NULL, }; reg = vl_api_client_index_to_registration (mp->client_index); if (!reg) return; /* *INDENT-OFF* */ pool_foreach (mfib_table, im->mfibs, ({ ip6_mfib_table_walk(&mfib_table->v6, vl_api_ip6_mfib_table_dump_walk, &ctx); vec_sort_with_function (ctx.entries, mfib_entry_cmp_for_sort); vec_foreach(mfeip, ctx.entries) { mfib_entry_get_prefix (*mfeip, &pfx); mfib_entry_encode (*mfeip, &api_rpaths); send_ip6_mfib_details (am, reg, mfib_table->mft_table_id, &pfx, api_rpaths, mp->context); } vec_reset_length (api_rpaths); vec_reset_length (ctx.entries); })); /* *INDENT-ON* */ vec_free (ctx.entries); vec_free (api_rpaths); } static void vl_api_ip_punt_police_t_handler (vl_api_ip_punt_police_t * mp, vlib_main_t * vm) { vl_api_ip_punt_police_reply_t *rmp; int rv = 0; if (mp->is_ip6) ip6_punt_policer_add_del (mp->is_add, ntohl (mp->policer_index)); else ip4_punt_policer_add_del (mp->is_add, ntohl (mp->policer_index)); REPLY_MACRO (VL_API_IP_PUNT_POLICE_REPLY); } static void vl_api_ip_punt_redirect_t_handler (vl_api_ip_punt_redirect_t * mp, vlib_main_t * vm) { vl_api_ip_punt_redirect_reply_t *rmp; int rv = 0; if (mp->is_add) { ip46_address_t nh; memset (&nh, 0, sizeof (nh)); if (mp->is_ip6) { memcpy (&nh.ip6, mp->nh, sizeof (nh.ip6)); ip6_punt_redirect_add (ntohl (mp->rx_sw_if_index), ntohl (mp->tx_sw_if_index), &nh); } else { memcpy (&nh.ip4, mp->nh, sizeof (nh.ip4)); ip4_punt_redirect_add (ntohl (mp->rx_sw_if_index), ntohl (mp->tx_sw_if_index), &nh); } } else { if (mp->is_ip6) { ip6_punt_redirect_del (ntohl (mp->rx_sw_if_index)); } else { ip4_punt_redirect_del (ntohl (mp->rx_sw_if_index)); } } REPLY_MACRO (VL_API_IP_PUNT_REDIRECT_REPLY); } static void vl_api_ip_neighbor_add_del_t_handler (vl_api_ip_neighbor_add_del_t * mp, vlib_main_t * vm) { ip46_address_t ip = ip46_address_in
#!/usr/bin/env python

import binascii
import socket
from socket import AF_INET, AF_INET6
import unittest
import sys

from framework import VppTestCase

from scapy.packet import Raw
from scapy.layers.l2 import Ether
from scapy.layers.inet import IP, UDP, TCP
from scapy.layers.inet6 import IPv6
from util import ppp


class TestClassifier(VppTestCase):

    @staticmethod
    def _resolve_mask_match(mask_match):
        mask_match = binascii.unhexlify(mask_match)
        mask_match_len = ((len(mask_match) - 1) // 16 + 1) * 16
        mask_match = mask_match + b'\0' * \
            (mask_match_len - len(mask_match))
        return mask_match, mask_match_len

    @classmethod
    def setUpClass(cls):
        """
        Perform standard class setup (defined by class method setUpClass in
        class VppTestCase) before running the test case, set test case related
        variables and configure VPP.
        """
        super(TestClassifier, cls).setUpClass()
        cls.acl_active_table = ''
        cls.af = AF_INET

    def setUp(self):
        """
        Perform test setup before test case.

        **Config:**
            - create 4 pg interfaces
                - untagged pg0/pg1/pg2 interface
                    pg0 -------> pg1 (IP ACL)
                           \
                            ---> pg2 (MAC ACL))
                             \
                              -> pg3 (PBR)
            - setup interfaces:
                - put it into UP state
                - set IPv4/6 addresses
                - resolve neighbor address using ARP

        :ivar list interfaces: pg interfaces.
        :ivar list pg_if_packet_sizes: packet sizes in test.
        :ivar dict acl_tbl_idx: ACL table index.
        :ivar int pbr_vrfid: VRF id for PBR test.
        """
        self.reset_packet_infos()
        super(TestClassifier, self).setUp()
        if self.af is None:  # l2_acl test case
            return

        # create 4 pg interfaces
        self.create_pg_interfaces(range(4))

        # packet sizes to test
        self.pg_if_packet_sizes = [64, 9018]

        self.interfaces = list(self.pg_interfaces)

        # ACL & PBR vars
        self.acl_tbl_idx = {}
        self.pbr_vrfid = 200

        # setup all interfaces
        for intf in self.interfaces:
            intf.admin_up()
            if self.af == AF_INET:
                intf.config_ip4()
                intf.resolve_arp()
            elif self.af == AF_INET6:
                intf.config_ip6()
                intf.resolve_ndp()

    def tearDown(self):
        """Run standard test teardown and acl related log."""
        if self.af is not None and not self.vpp_dead:
            if self.af == AF_INET:
                self.logger.info(self.vapi.ppcli("show inacl type ip4"))
                self.logger.info(self.vapi.ppcli("show outacl type ip4"))
            elif self.af == AF_INET6:
                self.logger.info(self.vapi.ppcli("show inacl type ip6"))
                self.logger.info(self.vapi.ppcli("show outacl type ip6"))

            self.logger.info(self.vapi.cli("show classify table verbose"))
            self.logger.info(self.vapi.cli("show ip fib"))

            acl_active_table = 'ip_out'
            if self.af == AF_INET6:
                acl_active_table = 'ip6_out'

            if self.acl_active_table == acl_active_table:
                self.output_acl_set_interface(
                    self.pg0, self.acl_tbl_idx.get(self.acl_active_table), 0)
                self.acl_active_table = ''
            elif self.acl_active_table != '':
                self.input_acl_set_interface(
                    self.pg0, self.acl_tbl_idx.get(self.acl_active_table), 0)
                self.acl_active_table = ''

            for intf in self.interfaces:
                if self.af == AF_INET:
                    intf.unconfig_ip4()
                elif self.af == AF_INET6:
                    intf.unconfig_ip6()
                intf.admin_down()

        super(TestClassifier, self).tearDown()

    @staticmethod
    def build_mac_match(dst_mac='', src_mac='', ether_type=''):
        """Build MAC ACL match data with hexstring format.

        :param str dst_mac: source MAC address <x:x:x:x:x:x>
        :param str src_mac: destination MAC address <x:x:x:x:x:x>
        :param str ether_type: ethernet type <0-ffff>
        """
        if dst_mac:
            dst_mac = dst_mac.replace(':', '')
        if src_mac:
            src_mac = src_mac.replace(':', '')

        return ('{!s:0>12}{!s:0>12}{!s:0>4}'.format(
            dst_mac, src_mac, ether_type)).rstrip('0')

    @staticmethod
    def build_mac_mask(dst_mac='', src_mac='', ether_type=''):
        """Build MAC ACL mask data with hexstring format.

        :param str dst_mac: source MAC address <0-ffffffffffff>
        :param str src_mac: destination MAC address <0-ffffffffffff>
        :param str ether_type: ethernet type <0-ffff>
        """

        return ('{!s:0>12}{!s:0>12}{!s:0>4}'.format(
            dst_mac, src_mac, ether_type)).rstrip('0')

    @staticmethod
    def build_ip_mask(proto='', src_ip='', dst_ip='',
                      src_port='', dst_port=''):
        """Build IP ACL mask data with hexstring format.

        :param str proto: protocol number <0-ff>
        :param str src_ip: source ip address <0-ffffffff>
        :param str dst_ip: destination ip address <0-ffffffff>
        :param str src_port: source port number <0-ffff>
        :param str dst_port: destination port number <0-ffff>
        """

        return ('{!s:0>20}{!s:0>12}{!s:0>8}{!s:0>4}{!s:0>4}'.format(
            proto, src_ip, dst_ip, src_port, dst_port)).rstrip('0')

    @staticmethod
    def build_ip6_mask(nh='', src_ip='', dst_ip='',
                       src_port='', dst_port=''):
        """Build IPv6 ACL mask data with hexstring format.

        :param str nh: next header number <0-ff>
        :param str src_ip: source ip address <0-ffffffff>
        :param str dst_ip: destination ip address <0-ffffffff>
        :param str src_port: source port number <0-ffff>
        :param str dst_port: destination port number <0-ffff>
        """

        return ('{!s:0>14}{!s:0>34}{!s:0>32}{!s:0>4}{!s:0>4}'.format(
            nh, src_ip, dst_ip, src_port, dst_port)).rstrip('0')

    @staticmethod
    def build_ip_match(proto=0, src_ip='', dst_ip='',
                       src_port=0, dst_port=0):
        """Build IP ACL match data with hexstring format.

        :param int proto: protocol number with valid option "x"
        :param str src_ip: source ip address with format of "x.x.x.x"
        :param str dst_ip: destination ip address with format of "x.x.x.x"
        :param int src_port: source port number "x"
        :param int dst_port: destination port number "x"
        """
        if src_ip:
            src_ip = binascii.hexlify(socket.inet_aton(src_ip)).decode('ascii')
        if dst_ip:
            dst_ip = binascii.hexlify(socket.inet_aton(dst_ip)).decode('ascii')

        return ('{!s:0>20}{!s:0>12}{!s:0>8}{!s:0>4}{!s:0>4}'.format(
            hex(proto)[2:], src_ip, dst_ip, hex(src_port)[2:],
            hex(dst_port)[2:])).rstrip('0')

    @staticmethod
    def build_ip6_match(nh=0, src_ip='', dst_ip='',
                        src_port=0, dst_port=0):
        """Build IPv6 ACL match data with hexstring format.

        :param int nh: next header number with valid option "x"
        :param str src_ip: source ip6 address with format of "xxx:xxxx::xxxx"
        :param str dst_ip: destination ip6 address with format of
            "xxx:xxxx::xxxx"
        :param int src_port: source port number "x"
        :param int dst_port: destination port number "x"
        """
        if src_ip:
            if sys.version_info[0] == 2:
                src_ip = binascii.hexlify(socket.inet_pton(
                    socket.AF_INET6, src_ip))
            else:
                src_ip = socket.inet_pton(socket.AF_INET6, src_ip).hex()

        if dst_ip:
            if sys.version_info[0] == 2:
                dst_ip = binascii.hexlify(socket.inet_pton(
                    socket.AF_INET6, dst_ip))
            else:
                dst_ip = socket.inet_pton(socket.AF_INET6, dst_ip).hex()

        return ('{!s:0>14}{!s:0>34}{!s:0>32}{!s:0>4}{!s:0>4}'.format(
            hex(nh)[2:], src_ip, dst_ip, hex(src_port)[2:],
            hex(dst_port)[2:])).rstrip('0')

    def create_stream(self, src_if, dst_if, packet_sizes,
                      proto_l=UDP(sport=1234, dport=5678)):
        """Create input packet stream for defined interfaces.

        :param VppInterface src_if: Source Interface for packet stream.
        :param VppInterface dst_if: Destination Interface for packet stream.
        :param list packet_sizes: packet size to test.
        :param Scapy proto_l: Required IP protocol. Default protocol is UDP.
        """
        pkts = []

        for size in packet_sizes:
            info = self.create_packet_info(src_if, dst_if)
            payload = self.info_to_payload(info)
            if self.af == AF_INET:
                p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
                     IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) /
                     proto_l /
                     Raw(payload))
            elif self.af == AF_INET6:
                p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
                     IPv6(src=src_if.remote_ip6, dst=dst_if.remote_ip6) /
                     proto_l /
                     Raw(payload))
            info.data = p.copy()
            self.extend_packet(p, size)
            pkts.append(p)
        return pkts

    def verify_capture(self, dst_if, capture, proto_l=UDP):
        """Verify captured input packet stream for defined interface.

        :param VppInterface dst_if: Interface to verify captured packet stream.
        :param list capture: Captured packet stream.
        :param Scapy proto_l: Required IP protocol. Default protocol is UDP.
        """
        ip_proto = IP
        if self.af == AF_INET6:
            ip_proto = IPv6
        self.logger.info("Verifying capture on interface %s" % dst_if.name)
        last_info = dict()
        for i in self.interfaces:
            last_info[i.sw_if_index] = None
        dst_sw_if_index = dst_if.sw_if_index
        for packet in capture:
            try:
                ip_received = packet[ip_proto]
                proto_received = packet[proto_l]
                payload_info = self.payload_to_info(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))
                next_info = self.get_next_packet_info_for_interface2(
                    payload_info.src, dst_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
                ip_saved = saved_packet[ip_proto]
                proto_saved = saved_packet[proto_l]
                # Check standard fields
                self.assertEqual(ip_received.src, ip_saved.src)
                self.assertEqual(ip_received.dst, ip_saved.dst)
                self.assertEqual(proto_received.sport, proto_saved.sport)
                self.assertEqual(proto_received.dport, proto_saved.dport)
            except BaseException:
                self.logger.error(ppp("Unexpected or invalid packet:", packet))
                raise
        for i in self.interfaces:
            remaining_packet = self.get_next_packet_info_for_interface2(
                i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index])
            self.assertTrue(remaining_packet is None,
                            "Interface %s: Packet expected from interface %s "
                            "didn't arrive" % (dst_if.name, i.name))

    def create_classify_table(self, key, mask, data_offset=0):
        """Create Classify Table

        :param str key: key for classify table (ex, ACL name).
        :param str mask: mask value for interested traffic.
        :param int data_offset:
        """
        mask_match, mask_match_len = self._resolve_mask_match(mask)
        r = self.vapi.classify_add_del_table(
            is_add=1,
            mask=mask_match,
            mask_len=mask_match_len,
            match_n_vectors=(len(mask) - 1) // 32 + 1,
            miss_next_index=0,
            current_data_flag=1,
            current_data_offset=data_offset)
        self.assertIsNotNone(r, 'No response msg for add_del_table')
        self.acl_tbl_idx[key] = r.new_table_index

    def create_classify_session(self, table_index, match, pbr_option=0,
                                vrfid=0, is_add=1):
        """Create Classify Session

        :param int table_index: table index to identify classify table.
        :param str match: matched value for interested traffic.
        :param int pbr_option: enable/disable PBR feature.
        :param int vrfid: VRF id.
        :param int is_add: option to configure classify session.
            - create(1) or delete(0)
        """
        mask_match, mask_match_len = self._resolve_mask_match(match)
        r = self.vapi.classify_add_del_session(
            is_add=is_add,
            table_index=table_index,
            match=mask_match,
            match_len=mask_match_len,
            opaque_index=0,
            action=pbr_option,
            metadata=vrfid)
        self.assertIsNotNone(r, 'No response msg for add_del_session')

    def input_acl_set_interface(self, intf, table_index, is_add=1):
        """Configure Input ACL interface

        :param VppInterface intf: Interface to apply Input ACL feature.
        :param int table_index: table index to identify classify table.
        :param int is_add: option to configure classify session.
            - enable(1) or disable(0)
        """
        r = None
        if self.af == AF_INET:
            r = self.vapi.input_acl_set_interface(
                is_add,
                intf.sw_if_index,
                ip4_table_index=table_index)
        elif self.af == AF_INET6:
            r = self.vapi.input_acl_set_interface(
                is_add,
                intf.sw_if_index,
                ip6_table_index=table_index)
        else:
            r = self.vapi.input_acl_set_interface(
                is_add,
                intf.sw_if_index,
                l2_table_index=table_index)
        self.assertIsNotNone(r, 'No response msg for acl_set_interface')

    def output_acl_set_interface(self, intf, table_index, is_add=1):
        """Configure Output ACL interface

        :param VppInterface intf: Interface to apply Output ACL feature.
        :param int table_index: table index to identify classify table.
        :param int is_add: option to configure classify session.
            - enable(1) or disable(0)
        """
        r = None
        if self.af == AF_INET:
            r = self.vapi.output_acl_set_interface(
                is_add,
                intf.sw_if_index,
                ip4_table_index=table_index)
        elif self.af == AF_INET6:
            r = self.vapi.output_acl_set_interface(
                is_add,
                intf.sw_if_index,
                ip6_table_index=table_index)
        else:
            r = self.vapi.output_acl_set_interface(
                is_add,
                intf.sw_if_index,
                l2_table_index=table_index)
        self.assertIsNotNone(r, 'No response msg for acl_set_interface')

    def config_pbr_fib_entry(self, intf, is_add=1):
        """Configure fib entry to route traffic toward PBR VRF table

        :param VppInterface intf: destination interface to be routed for PBR.

        """
        addr_len = 24
        self.vapi.ip_add_del_route(dst_address=intf.local_ip4,
                                   dst_address_length=</