#!/usr/bin/env python3 from __future__ import print_function """ACL plugin - MACIP tests """ import binascii import ipaddress import random from socket import inet_ntop, inet_pton, AF_INET, AF_INET6 from struct import pack, unpack import re import unittest from ipaddress import ip_network, IPv4Network, IPv6Network import scapy.compat from scapy.packet import Raw from scapy.layers.l2 import Ether from scapy.layers.inet import IP, UDP from scapy.layers.inet6 import IPv6 from framework import VppTestCase, VppTestRunner, running_extended_tests from vpp_lo_interface import VppLoInterface from vpp_l2 import L2_PORT_TYPE from vpp_sub_interface import L2_VTR_OP, VppSubInterface, VppDot1QSubint, \ VppDot1ADSubint from vpp_acl import AclRule, VppAcl, VppAclInterface, VppEtypeWhitelist, \ VppMacipAclInterface, VppMacipAcl, MacipRule from vpp_papi import MACAddress class MethodHolder(VppTestCase): DEBUG = False BRIDGED = True ROUTED = False IS_IP4 = False IS_IP6 = True DOT1AD = "dot1ad" DOT1Q = "dot1q" PERMIT_TAGS = True DENY_TAGS = False # rule types DENY = 0 PERMIT = 1 # ACL types EXACT_IP = 1 SUBNET_IP = 2 WILD_IP = 3 EXACT_MAC = 1 WILD_MAC = 2 OUI_MAC = 3 ACLS = [] @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(MethodHolder, cls).setUpClass() cls.pg_if_packet_sizes = [64, 512, 1518, 9018] # packet sizes cls.bd_id = 111 cls.remote_hosts_count = 200 try: # create 4 pg interfaces, 1 loopback interface cls.create_pg_interfaces(range(4)) cls.create_loopback_interfaces(1) # create 2 subinterfaces cls.subifs = [ VppDot1QSubint(cls, cls.pg1, 10), VppDot1ADSubint(cls, cls.pg2, 20, 300, 400), VppDot1QSubint(cls, cls.pg3, 30), VppDot1ADSubint(cls, cls.pg3, 40, 600, 700)] cls.subifs[0].set_vtr(L2_VTR_OP.L2_POP_1, inner=10, push1q=1) cls.subifs[1].set_vtr(L2_VTR_OP.L2_POP_2, outer=300, inner=400, push1q=1) cls.subifs[2].set_vtr(L2_VTR_OP.L2_POP_1, inner=30, push1q=1) cls.subifs[3].set_vtr(L2_VTR_OP.L2_POP_2, outer=600, inner=700, push1q=1) cls.interfaces = list(cls.pg_interfaces) cls.interfaces.extend(cls.lo_interfaces) cls.interfaces.extend(cls.subifs) 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( rx_sw_if_index=cls.loop0.sw_if_index, bd_id=cls.bd_id, port_type=L2_PORT_TYPE.BVI) cls.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=cls.pg0.sw_if_index, bd_id=cls.bd_id) cls.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=cls.pg1.sw_if_index, bd_id=cls.bd_id) cls.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=cls.subifs[0].sw_if_index, bd_id=cls.bd_id) cls.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=cls.subifs[1].sw_if_index, bd_id=cls.bd_id) # Configure IPv4/6 addresses on loop interface and routed interface cls.loop0.config_ip4() cls.loop0.config_ip6() cls.pg2.config_ip4() cls.pg2.config_ip6() cls.pg3.config_ip4() cls.pg3.config_ip6() # Configure MAC address binding to IPv4 neighbors on loop0 cls.loop0.generate_remote_hosts(cls.remote_hosts_count) # Modify host mac addresses to have different OUI parts for i in range(2, cls.remote_hosts_count + 2): mac = cls.loop0.remote_hosts[i-2]._mac.split(':') mac[2] = format(int(mac[2], 16) + i, "02x") cls.loop0.remote_hosts[i - 2]._mac = ":".join(mac) cls.loop0.configure_ipv4_neighbors() cls.loop0.configure_ipv6_neighbors() # configure MAC address on pg3 cls.pg3.resolve_arp() cls.pg3.resolve_ndp() # configure MAC address on subifs for i in cls.subifs: i.config_ip4() i.resolve_arp() i.config_ip6() # configure MAC address on pg2 cls.pg2.resolve_arp() cls.pg2.resolve_ndp() # Loopback BVI interface has remote hosts # one half of hosts are behind pg0 second behind pg1,pg2,pg3 subifs cls.pg0.remote_hosts = cls.loop0.remote_hosts[:100] cls.subifs[0].remote_hosts = cls.loop0.remote_hosts[100:125] cls.subifs[1].remote_hosts = cls.loop0.remote_hosts[125:150] cls.subifs[2].remote_hosts = cls.loop0.remote_hosts[150:175] cls.subifs[3].remote_hosts = cls.loop0.remote_hosts[175:] except Exception: super(MethodHolder, cls).tearDownClass() raise @classmethod def tearDownClass(cls): super(MethodHolder, cls).tearDownClass() def setUp(self): super(MethodHolder, self).setUp() self.reset_packet_infos() def show_commands_at_teardown(self): self.logger.info(self.vapi.ppcli("show interface address")) self.logger.info(self.vapi.ppcli("show hardware")) self.logger.info(self.vapi.ppcli("sh acl-plugin macip acl")) self.logger.info(self.vapi.ppcli("sh acl-plugin macip interface")) self.logger.info(self.vapi.ppcli("sh classify tables verbose")) self.logger.info(self.vapi.ppcli("sh acl-plugin acl")) self.logger.info(self.vapi.ppcli("sh acl-plugin interface")) self.logger.info(self.vapi.ppcli("sh acl-plugin tables")) # print(self.vapi.ppcli("show interface address")) # print(self.vapi.ppcli("show hardware")) # print(self.vapi.ppcli("sh acl-plugin macip interface")) # print(self.vapi.ppcli("sh acl-plugin macip acl")) def macip_acl_dump_debug(self): acls = self.vapi.macip_acl_dump() if self.DEBUG: for acl in acls: # print("ACL #"+str(acl.acl_index)) for r in acl.r: rule = "ACTION" if r.is_permit == 1: rule = "PERMIT" elif r.is_permit == 0: rule = "DENY " """ print(" IP6" if r.is_ipv6 else " IP4", rule, binascii.hexlify(r.src_mac), binascii.hexlify(r.src_mac_mask), unpack('<16B', r.src_ip_addr), r.src_ip_prefix_len) """ return acls def create_rules(self, mac_type=EXACT_MAC, ip_type=EXACT_IP, acl_count=1, rules_count=None): acls = [] if rules_count is None: rules_count = [1] src_mac = int("220000dead00", 16) for acl in range(2, (acl_count+1) * 2): rules = [] host = random.choice(self.loop0.remote_hosts) is_ip6 = acl % 2 ip4 = host.ip4.split('.') ip6 = list(unpack('<16B', inet_pton(AF_INET6, host.ip6))) if ip_type == self.EXACT_IP: prefix_len4 = 32 prefix_len6 = 128 elif ip_type == self.WILD_IP: ip4 = [0, 0, 0, 0] ip6 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] prefix_len4 = 0 prefix_len6 = 0 rules_count[int((acl / 2) - 1)] = 1 else: prefix_len4 = 24 prefix_len6 = 64 if mac_type == self.EXACT_MAC: mask = "ff:ff:ff:ff:ff:ff" elif mac_type == self.WILD_MAC: mask = "00:00:00:00:00:00" elif mac_type == self.OUI_MAC: mask = "ff:ff:ff:00:00:00" else: mask = "ff:ff:ff:ff:ff:00" ip = ip6 if is_ip6 else ip4 ip_len = prefix_len6 if is_ip6 else prefix_len4 for i in range(0, (rules_count[int((acl / 2) - 1)])): src_mac += 16777217 if mac_type == self.WILD_MAC: mac = "00:00:00:00:00:00" elif mac_type == self.OUI_MAC: mac = ':'.join(re.findall('..', '{:02x}'.format( src_mac))[:3])+":00:00:00" else: mac = ':'.join(re.findall( '..', '{:02x}'.format(src_mac))) if ip_type == self.EXACT_IP: ip4[3] = random.randint(100, 200) ip6[15] = random.randint(100, 200) elif ip_type == self.SUBNET_IP: ip4[2] = random.randint(100, 200) ip4[3] = 0 ip6[7] = random.randint(100, 200) ip6[15] = 0 ip_pack = b'' for j in range(0, len(ip)): ip_pack += pack(' @media only all and (prefers-color-scheme: dark) { .highlight .hll { background-color: #49483e } .highlight .c { color: #75715e } /* Comment */ .highlight .err { color: #960050; background-color: #1e0010 } /* Error */ .highlight .k { color: #66d9ef } /* Keyword */ .highlight .l { color: #ae81ff } /* Literal */ .highlight .n { color: #f8f8f2 } /* Name */ .highlight .o { color: #f92672 } /* Operator */ .highlight .p { color: #f8f8f2 } /* Punctuation */ .highlight .ch { color: #75715e } /* Comment.Hashbang */ .highlight .cm { color: #75715e } /* Comment.Multiline */ .highlight .cp { color: #75715e } /* Comment.Preproc */ .highlight .cpf { color: #75715e } /* Comment.PreprocFile */ .highlight .c1 { color: #75715e } /* Comment.Single */ .highlight .cs { color: #75715e } /* Comment.Special */ .highlight .gd { color: #f92672 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gi { color: #a6e22e } /* Generic.Inserted */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #75715e } /* Generic.Subheading */ .highlight .kc { color: #66d9ef } /* Keyword.Constant */ .highlight .kd { color: #66d9ef } /* Keyword.Declaration */ .highlight .kn { color: #f92672 } /* Keyword.Namespace */ .highlight .kp { color: #66d9ef } /* Keyword.Pseudo */ .highlight .kr { color: #66d9ef } /* Keyword.Reserved */ .highlight .kt { color: #66d9ef } /* Keyword.Type */ .highlight .ld { color: #e6db74 } /* Literal.Date */ .highlight .m { color: #ae81ff } /* Literal.Number */ .highlight .s { color: #e6db74 } /* Literal.String */ .highlight .na { color: #a6e22e } /* Name.Attribute */ .highlight .nb { color: #f8f8f2 } /* Name.Builtin */ .highlight .nc { color: #a6e22e } /* Name.Class */ .highlight .no { color: #66d9ef } /* Name.Constant */ .highlight .nd { color: #a6e22e } /* Name.Decorator */ .highlight .ni { color: #f8f8f2 } /* Name.Entity */ .highlight .ne { color: #a6e22e } /* Name.Exception */ .highlight .nf { color: #a6e22e } /* Name.Function */ .highlight .nl { color: #f8f8f2 } /* Name.Label */ .highlight .nn { color: #f8f8f2 } /* Name.Namespace */ .highlight .nx { color: #a6e22e } /* Name.Other */ .highlight .py { color: #f8f8f2 } /* Name.Property */ .highlight .nt { color: #f92672 } /* Name.Tag */ .highlight .nv { color: #f8f8f2 } /* Name.Variable */ .highlight .ow { color: #f92672 } /* Operator.Word */ .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ .highlight .mb { color: #ae81ff } /* Literal.Number.Bin */ .highlight .mf { color: #ae81ff } /* Literal.Number.Float */ .highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ .highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ .highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ .highlight .sa { color: #e6db74 } /* Literal.String.Affix */ .highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ .highlight .sc { color: #e6db74 } /* Literal.String.Char */ .highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */ .highlight .sd { color: #e6db74 } /* Literal.String.Doc */ .highlight .s2 { color: #e6db74 } /* Literal.String.Double */ .highlight .se { color: #ae81ff } /* Literal.String.Escape */ .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ .highlight .si { color: #e6db74 } /* Literal.String.Interpol */ .highlight .sx { color: #e6db74 } /* Literal.String.Other */ .highlight .sr { color: #e6db74 } /* Literal.String.Regex */ .highlight .s1 { color: #e6db74 } /* Literal.String.Single */ .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #a6e22e } /* Name.Function.Magic */ .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ } @media (prefers-color-scheme: light) { .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }
/*
 * Copyright (c) 2018 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 <vlib/vlib.h>
#include <vnet/dpo/drop_dpo.h>
#include <vnet/fib/ip6_fib.h>

#include <vnet/ip/ip6_ll_table.h>

/**
 * There's only one IP6 link local table
 */
static ip6_ll_table_t ip6_ll_table;

u32
ip6_ll_fib_get (u32 sw_if_index)
{
  ASSERT (vec_len (ip6_ll_table.ilt_fibs) > sw_if_index);

  return (ip6_ll_table.ilt_fibs[sw_if_index]);
}

fib_node_index_t
ip6_ll_table_lookup (const ip6_ll_prefix_t * prefix)
{
  return (ip6_fib_table_lookup (ip6_ll_fib_get (prefix->ilp_sw_if_index),
				&prefix->ilp_addr, 128));
}

fib_node_index_t
ip6_ll_table_lookup_exact_match (const ip6_ll_prefix_t * prefix)
{
  return (ip6_fib_table_lookup_exact_match
	  (ip6_ll_fib_get (prefix->ilp_sw_if_index), &prefix->ilp_addr, 128));
}

static void
ip6_ll_fib_create (u32 sw_if_index)
{
  vnet_main_t *vnm = vnet_get_main ();
  u8 *desc;

  desc = format (NULL, "IP6-link-local:%U",
		 format_vnet_sw_interface_name,
		 vnm, vnet_get_sw_interface (vnm, sw_if_index));

  ip6_ll_table.ilt_fibs[sw_if_index] =
    ip6_fib_table_create_and_lock (FIB_SOURCE_IP6_ND,
				   FIB_TABLE_FLAG_IP6_LL, desc);

  /*
   * leave the default route as a drop, but fix fe::/10 to be a glean
   * via the interface.
   */
    /* *INDENT-OFF* */
    fib_prefix_t pfx = {
	.fp_proto = FIB_PROTOCOL_IP6,
	.fp_len = 10,
	.fp_addr = {
	    .ip6 = {
		.as_u8 = {
                    [0] = 0xFE,
                    [1] = 0x80,
                }
	    },
	}
    };
    fib_table_entry_update_one_path(
        ip6_ll_table.ilt_fibs[sw_if_index],
        &pfx,
        FIB_SOURCE_SPECIAL,
        (FIB_ENTRY_FLAG_ATTACHED |
         FIB_ENTRY_FLAG_NO_ATTACHED_EXPORT),
        DPO_PROTO_IP6,
        NULL,
        sw_if_index,
        ~0,
        1,
        NULL,
        FIB_ROUTE_PATH_FLAG_NONE);
    /* *INDENT-ON* */
}

static void
ip6_ll_prefix_to_fib (const ip6_ll_prefix_t * ilp, fib_prefix_t * fp)
{
  fp->fp_proto = FIB_PROTOCOL_IP6;
  fp->fp_len = 128;
  fp->fp_addr.ip6 = ilp->ilp_addr;
}

fib_node_index_t
ip6_ll_table_entry_update (const ip6_ll_prefix_t * ilp,
			   fib_route_path_flags_t flags)
{
  fib_node_index_t ip6_ll_entry_index;
  fib_route_path_t *rpaths, rpath = {
    .frp_flags = flags,
    .frp_sw_if_index = ilp->ilp_sw_if_index,
    .frp_proto = DPO_PROTO_IP6,
  };
  fib_prefix_t fp;

  vec_validate (ip6_ll_table.ilt_fibs, ilp->ilp_sw_if_index);

  if (0 == ip6_ll_fib_get (ilp->ilp_sw_if_index))
    {
      ip6_ll_fib_create (ilp->ilp_sw_if_index);
    }

  rpaths = NULL;
  vec_add1 (rpaths, rpath);

  ip6_ll_prefix_to_fib (ilp, &fp);
  ip6_ll_entry_index =
    fib_table_entry_update (ip6_ll_fib_get (ilp->ilp_sw_if_index), &fp,
			    FIB_SOURCE_IP6_ND,
			    (flags & FIB_ROUTE_PATH_LOCAL ?
			     FIB_ENTRY_FLAG_LOCAL : FIB_ENTRY_FLAG_NONE),
			    rpaths);
  vec_free (rpaths);

  return (ip6_ll_entry_index);
}

void
ip6_ll_table_entry_delete (const ip6_ll_prefix_t * ilp)
{
  fib_node_index_t ip6_ll_entry_index;
  u32 fib_index;

  ip6_ll_entry_index = ip6_ll_table_lookup_exact_match (ilp);

  if (FIB_NODE_INDEX_INVALID != ip6_ll_entry_index)
    fib_table_entry_delete_index (ip6_ll_entry_index, FIB_SOURCE_IP6_ND);

  /*
   * if there are no ND sourced prefixes left, then we can clean up this FIB
   */
  fib_index = ip6_ll_fib_get (ilp->ilp_sw_if_index);
  if (0 == fib_table_get_num_entries (fib_index,
				      FIB_PROTOCOL_IP6, FIB_SOURCE_IP6_ND))
    {
      fib_table_unlock (fib_index, FIB_PROTOCOL_IP6, FIB_SOURCE_IP6_ND);
      ip6_ll_table.ilt_fibs[ilp->ilp_sw_if_index] = 0;
    }
}

static void
ip6_ll_table_show_one (vlib_main_t * vm, ip6_ll_prefix_t * ilp, int detail)
{
  vlib_cli_output (vm, "%U",
		   format_fib_entry,
		   ip6_ll_table_lookup (ilp),
		   (detail ?
		    FIB_ENTRY_FORMAT_DETAIL2 : FIB_ENTRY_FORMAT_DETAIL));
}

typedef struct ip6_ll_show_ctx_t_
{
  fib_node_index_t *entries;
} ip6_ll_show_ctx_t;

static fib_table_walk_rc_t
ip6_ll_table_show_walk (fib_node_index_t fib_entry_index, void *arg)
{
  ip6_ll_show_ctx_t *ctx = arg;

  vec_add1 (ctx->entries, fib_entry_index);

  return (FIB_TABLE_WALK_CONTINUE);
}

static void
ip6_ll_table_show_all (vlib_main_t * vm, u32 fib_index)
{
  fib_node_index_t *fib_entry_index;
  ip6_ll_show_ctx_t ctx = {
    .entries = NULL,
  };

  fib_table_walk (fib_index, FIB_PROTOCOL_IP6, ip6_ll_table_show_walk, &ctx);
  vec_sort_with_function (ctx.entries, fib_entry_cmp_for_sort);

  vec_foreach (fib_entry_index, ctx.entries)
  {
    vlib_cli_output (vm, "%U",
		     format_fib_entry,
		     *fib_entry_index, FIB_ENTRY_FORMAT_BRIEF);
  }

  vec_free (ctx.entries);
}

typedef struct
{
  u32 fib_index;
  u64 count_by_prefix_length[129];
} count_routes_in_fib_at_prefix_length_arg_t;

static void
count_routes_in_fib_at_prefix_length (BVT (clib_bihash_kv) * kvp, void *arg)
{
  count_routes_in_fib_at_prefix_length_arg_t *ap = arg;
  int mask_width;

  if ((kvp->key[2] >> 32) != ap->fib_index)
    return;

  mask_width = kvp->key[2] & 0xFF;

  ap->count_by_prefix_length[mask_width