summaryrefslogtreecommitdiffstats
path: root/src/plugins/pppoe
AgeCommit message (Collapse)AuthorFilesLines
2018-01-30Allow the provider of a midchain adjacency to pass context data that is ↵Neale Ranns1-5/+12
returned in the fixup function Change-Id: I458e6e03b03e27775df33a2fd302743126d6ac44 Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-01-23VPPAPIGEN: vppapigen replacement in Python PLY.Ole Troan1-1/+1
This is a version of the VPP API generator in Python PLY. It supports the existing language, and has a plugin architecture for generators. Currently C and JSON are supported. Changes: - vl_api_version to option version = "major.minor.patch" - enum support - Added error checking and reporting - import support (removed the C pre-processor) - services (tying request/reply together) Version: option version = "1.0.0"; Enum: enum colours { RED, BLUE = 50, }; define foo { vl_api_colours_t colours; }; Services: service { rpc foo returns foo_reply; rpc foo_dump returns stream foo_details; rpc want_stats returns want_stats_reply events ip4_counters, ip6_counters; }; Future planned features: - unions - bool, text - array support (including length) - proto3 output plugin - Refactor C/C++ generator as a plugin - Refactor Java generator as a plugin Change-Id: Ifa289966c790e1b1a8e2938a91e69331e3a58bdf Signed-off-by: Ole Troan <ot@cisco.com>
2018-01-18VPP-1119: PPPoE's destination MAC was overwrittenHongjun Ni1-1/+2
Change-Id: I6ae99c00e76058654f2c5e71377e9fd1bd13b47b Signed-off-by: Hongjun Ni <hongjun.ni@intel.com> (cherry picked from commit c287cd550b91be7a93bae76d0086db87f575e3d3)
2018-01-11api: remove transport specific code from handlersFlorin Coras1-10/+8
This does not update api client code. In other words, if the client assumes the transport is shmem based, this patch does not change that. Furthermore, code that checks queue size, for tail dropping, is not updated. Done for the following apis: Plugins - acl - gtpu - memif - nat - pppoe VNET - bfd - bier - tapv2 - vhost user - dhcp - flow - geneve - ip - punt - ipsec/ipsec-gre - l2 - l2tp - lisp-cp/one-cp - lisp-gpe - map - mpls - policer - session - span - udp - tap - vxlan/vxlan-gpe - interface VPP - api/api.c OAM - oam_api.c Stats - stats.c Change-Id: I0e33ecefb2bdab0295698c0add948068a5a83345 Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-01-09api: refactor vlibmemoryFlorin Coras1-2/+2
- separate client/server code for both memory and socket apis - separate memory api code from generic vlib api code - move unix_shared_memory_fifo to svm and rename to svm_fifo_t - overall declutter Change-Id: I90cdd98ff74d0787d58825b914b0f1eafcfa4dc2 Signed-off-by: Florin Coras <fcoras@cisco.com>
2017-11-17Replace tap interface using general interfaceHongjun Ni4-50/+48
Change-Id: Icd73f00162fb6aabe296c8bb6f2174ad4f6a17b7 Signed-off-by: Hongjun Ni <hongjun.ni@intel.com>
2017-10-09vppapigen: support per-file (major,minor,patch) version stampsDave Barach1-0/+2
Add one of these statements to foo.api: vl_api_version 1.2.3 to generate a version tuple stanza in foo.api.h: /****** Version tuple *****/ vl_api_version_tuple(foo, 1, 2, 3) Change-Id: Ic514439e4677999daa8463a94f948f76b132ff15 Signed-off-by: Dave Barach <dave@barachs.net> Signed-off-by: Ole Troan <ot@cisco.com>
2017-10-06Separate CP and DP fib table for PPPoEHongjun Ni3-56/+66
CP table: link_table DP table: session_table Change-Id: I2adbfd8f6a63d51d00d6dd291f32aebf20d13e4d Signed-off-by: Hongjun Ni <hongjun.ni@intel.com>
2017-10-03Repair vlib API socket serverDave Barach2-2/+2
- Teach vpp_api_test to send/receive API messages over sockets - Add memfd-based shared memory - Add api messages to create memfd-based shared memory segments - vpp_api_test supports both socket and shared memory segment connections - vpp_api_test pivot from socket to shared memory API messaging - add socket client support to libvlibclient.so - dead client reaper sends ping messages, container-friendly - dead client reaper falls back to kill (<pid>, 0) live checking if e.g. a python app goes silent for tens of seconds - handle ping messages in python client support code - teach show api ring about pairwise shared-memory segments - fix ip probing of already resolved destinations (VPP-998) We'll need this work to implement proper host-stack client isolation Change-Id: Ic23b65f75c854d0393d9a2e9d6b122a9551be769 Signed-off-by: Dave Barach <dave@barachs.net> Signed-off-by: Dave Wallace <dwallacelf@gmail.com> Signed-off-by: Florin Coras <fcoras@cisco.com>
2017-08-21PPPoE usses a midchain adjacency stack on an interface-tx DPONeale Ranns3-423/+111
1) introduce an interface-tx DPO. This is a simple wrapper around a sw_if_index. enhance DPO stacking functions to allow per-instance next-nodes and hence allow children to stack onto the interface per-instance tx node and not on 'interface-output'. 2) update PPPoE code to use ta midchain stack on a interface-tx DPO of the encap-interface. This remove the need for pppoe_encap node (which is replaced by the adj-midchain-tx) and interface-output node is no longer used (see above). Since PPPoE encap node is no longer needed, the PPPoE seesion does not need to be retrieved in the data-path, hence the cahce misses are removed. Change-Id: Id8b40f53daa14889a9c51d802e14fed7fba4399a Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-08-09PPPoE: use DPO protos in FIB entry path add/removeNeale Ranns1-2/+3
Change-Id: I6ac10ec0adf179b86f97269bbce2a7fd8796e72a Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-08-09Add PPPoE PluginHongjun Ni12-0/+2858
Supports 64K PPPoE sessions This plugin adds three graph nodes: 1) pppoe-input for PPPoE decapsulation 2) pppoe-encap for PPPoE encapsulation 3) pppoe-tap-dispatch for control plane process Below is the configuration to make PPPoE CP and DP work: vim /etc/vpp/startup.conf tuntap { enable ethernet name newtap } create pppoe tap tap-if-index 1 //Configure it after a subscriber's PPPoE discovery and PPP link establishment succeeds: create pppoe session client-ip 100.1.2.1 session-id 1 client-mac 00:11:01:00:00:01 show pppoe fib show pppoe session Change-Id: I73e724b6bf7c3e4181a9914c5752da1fa72d7e60 Signed-off-by: Hongjun Ni <hongjun.ni@intel.com>
bel */ .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 <plugins/l3xc/l3xc.h>

#include <vlib/vlib.h>
#include <vnet/plugin/plugin.h>
#include <vnet/fib/fib_path_list.h>

/**
 * FIB node type the attachment is registered
 */
fib_node_type_t l3xc_fib_node_type;

/**
 * Pool of L3XC objects
 */
l3xc_t *l3xc_pool;

/**
 * DB of L3XC objects
 */
static u32 *l3xc_db[FIB_PROTOCOL_IP_MAX];

index_t
l3xc_find (u32 sw_if_index, fib_protocol_t fproto)
{
  if (vec_len (l3xc_db[fproto]) <= sw_if_index)
    return ~0;

  return (l3xc_db[fproto][sw_if_index]);
}

static void
l3xc_db_add (u32 sw_if_index, fib_protocol_t fproto, index_t l3xci)
{
  vec_validate_init_empty (l3xc_db[fproto], sw_if_index, ~0);

  l3xc_db[fproto][sw_if_index] = l3xci;
}

static void
l3xc_db_remove (u32 sw_if_index, fib_protocol_t fproto)
{
  vec_validate_init_empty (l3xc_db[fproto], sw_if_index, ~0);

  l3xc_db[fproto][sw_if_index] = ~0;
}

static void
l3xc_stack (l3xc_t * l3xc)
{
  /*
   * stack the DPO on the forwarding contributed by the path-list
   */
  dpo_id_t via_dpo = DPO_INVALID;

  fib_path_list_contribute_forwarding (l3xc->l3xc_pl,
				       (FIB_PROTOCOL_IP4 == l3xc->l3xc_proto ?
					FIB_FORW_CHAIN_TYPE_UNICAST_IP4 :
					FIB_FORW_CHAIN_TYPE_UNICAST_IP6),
				       FIB_PATH_LIST_FWD_FLAG_NONE, &via_dpo);

  dpo_stack_from_node ((FIB_PROTOCOL_IP4 == l3xc->l3xc_proto ?
			l3xc_ip4_node.index :
			l3xc_ip6_node.index), &l3xc->l3xc_dpo, &via_dpo);
  dpo_reset (&via_dpo);
}

int
l3xc_update (u32 sw_if_index, u8 is_ip6, const fib_route_path_t * rpaths)
{
  fib_protocol_t fproto;
  l3xc_t *l3xc;
  u32 l3xci;

  fproto = (is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4);

  l3xci = l3xc_find (sw_if_index, fproto);

  if (INDEX_INVALID == l3xci)
    {
      /*
       * create a new x-connect
       */
      pool_get_aligned_zero (l3xc_pool, l3xc, CLIB_CACHE_LINE_BYTES);

      l3xci = l3xc - l3xc_pool;
      fib_node_init (&l3xc->l3xc_node, l3xc_fib_node_type);
      l3xc->l3xc_sw_if_index = sw_if_index;
      l3xc->l3xc_proto = fproto;

      /*
       * create and become a child of a path list so we get poked when
       * the forwarding changes and stack on the DPO the path-list provides
       */
      l3xc->l3xc_pl = fib_path_list_create ((FIB_PATH_LIST_FLAG_SHARED |
					     FIB_PATH_LIST_FLAG_NO_URPF),
					    rpaths);
      l3xc->l3xc_sibling = fib_path_list_child_add (l3xc->l3xc_pl,
						    l3xc_fib_node_type,
						    l3xci);
      l3xc_stack (l3xc);

      /*
       * add this new policy to the DB and enable the feature on input interface
       */
      l3xc_db_add (sw_if_index, fproto, l3xci);

      vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
				    "ip4-unicast" :
				    "ip6-unicast"),
				   (FIB_PROTOCOL_IP4 == fproto ?
				    "l3xc-input-ip4" :
				    "l3xc-input-ip6"),
				   l3xc->l3xc_sw_if_index,
				   1, &l3xci, sizeof (l3xci));
    }
  else
    {
      /*
       * update an existing x-connect.
       * - add the path to the path-list and swap our ancestry
       */
      l3xc = l3xc_get (l3xci);

      if (FIB_NODE_INDEX_INVALID != l3xc->l3xc_pl)
	{
	  fib_path_list_child_remove (l3xc->l3xc_pl, l3xc->l3xc_sibling);
	}

      l3xc->l3xc_pl = fib_path_list_create ((FIB_PATH_LIST_FLAG_SHARED |
					     FIB_PATH_LIST_FLAG_NO_URPF),
					    rpaths);

      l3xc->l3xc_sibling = fib_path_list_child_add (l3xc->l3xc_pl,
						    l3xc_fib_node_type,
						    l3xci);
    }
  return (0);
}

int
l3xc_delete (u32 sw_if_index, u8 is_ip6)
{
  fib_protocol_t fproto;
  l3xc_t *l3xc;
  u32 l3xci;

  fproto = (is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4);

  l3xci = l3xc_find (sw_if_index, fproto);

  if (INDEX_INVALID == l3xci)
    {
      /*
       * no such policy
       */
      return (VNET_API_ERROR_INVALID_VALUE);
    }
  else
    {
      l3xc = l3xc_get (l3xci);

      vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
				    "ip4-unicast" :
				    "ip6-unicast"),
				   (FIB_PROTOCOL_IP4 == fproto ?
				    "l3xc-input-ip4" :
				    "l3xc-input-ip6"),
				   l3xc->l3xc_sw_if_index,
				   0, &l3xci, sizeof (l3xci));

      fib_path_list_child_remove (l3xc->l3xc_pl, l3xc->l3xc_sibling);

      l3xc_db_remove (l3xc->l3xc_sw_if_index, fproto);
      pool_put (l3xc_pool, l3xc);
    }

  return (0);
}

static clib_error_t *
l3xc_cmd (vlib_main_t * vm,
	  unformat_input_t * main_input, vlib_cli_command_t * cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  fib_route_path_t *rpaths = NULL, rpath;
  u32 sw_if_index, is_del, is_ip6;
  dpo_proto_t payload_proto;
  vnet_main_t *vnm;
  int rv = 0;

  is_ip6 = is_del = 0;
  sw_if_index = ~0;
  vnm = vnet_get_main ();

  /* Get a line of input. */
  if (!unformat_user (main_input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat
	  (line_input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
	;
      else if (unformat (line_input, "ip6"))
	is_ip6 = 1;
      else if (unformat (line_input, "ip4"))
	is_ip6 = 0;
      else if (unformat (line_input, "del"))
	is_del = 1;
      else if (unformat (line_input, "add"))
	is_del = 0;
      else if (unformat (line_input, "via %U",
			 unformat_fib_route_path, &rpath, &payload_proto))
	vec_add1 (rpaths, rpath);
      else
	return (clib_error_return (0, "unknown input '%U'",
				   format_unformat_error, line_input));
    }

  if (~0 == sw_if_index)
    {
      vlib_cli_output (vm, "Specify an input interface");
      goto out;
    }
  if (vec_len (rpaths) == 0)
    {
      vlib_cli_output (vm, "Specify some paths");
      goto out;
    }

  if (!is_del)
    {
      rv = l3xc_update (sw_if_index, is_ip6, rpaths);

      if (rv)
	{
	  vlib_cli_output (vm, "Failed: %d", rv);
	  goto out;
	}
    }
  else
    {
      l3xc_delete (sw_if_index, is_ip6);
    }

out:
  unformat_free (line_input);
  return (NULL);
}

/* *INDENT-OFF* */
/**
 * Create an L3XC policy.
 */
VLIB_CLI_COMMAND (l3xc_cmd_node, static) = {
  .path = "l3xc",
  .function = l3xc_cmd,
  .short_help = "l3xc [add|del] <INTERFACE> via ...",
  .is_mp_safe = 1,
};
/* *INDENT-ON* */

static u8 *
format_l3xc (u8 * s, va_list * args)
{
  l3xc_t *l3xc = va_arg (*args, l3xc_t *);
  vnet_main_t *vnm = vnet_get_main ();

  s = format (s, "l3xc:[%d]: %U",
	      l3xc - l3xc_pool, format_vnet_sw_if_index_name,
	      vnm, l3xc->l3xc_sw_if_index);
  s = format (s, "\n");
  if (FIB_NODE_INDEX_INVALID == l3xc->l3xc_pl)
    {
      s = format (s, "no forwarding");
    }
  else
    {
      s = fib_path_list_format (l3xc->l3xc_pl, s);

      s = format (s, "\n  %U", format_dpo_id, &l3xc->l3xc_dpo, 4);
    }

  return (s);
}

void
l3xc_walk (l3xc_walk_cb_t cb, void *ctx)
{
  u32 l3xci;

  /* *INDENT-OFF* */
  pool_foreach_index(l3xci, l3xc_pool,
  ({
    if (!cb(l3xci, ctx))
      break;
  }));
  /* *INDENT-ON* */
}

static clib_error_t *
l3xc_show_cmd (vlib_main_t * vm,
	       unformat_input_t * input, vlib_cli_command_t * cmd)
{
  l3xc_t *l3xc;

  /* *INDENT-OFF* */
  pool_foreach(l3xc, l3xc_pool,
  ({
    vlib_cli_output(vm, "%U", format_l3xc, l3xc);
  }));
  /* *INDENT-ON* */

  return (NULL);
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (l3xc_show_cmd_node, static) = {
  .path = "show l3xc",
  .function = l3xc_show_cmd,
  .short_help = "show l3xc",
  .is_mp_safe = 1,
};
/* *INDENT-ON* */

static fib_node_t *
l3xc_get_node (fib_node_index_t index)
{
  l3xc_t *l3xc = l3xc_get (index);
  return (&(l3xc->l3xc_node));
}

static l3xc_t *
l3xc_get_from_node (fib_node_t * node)
{
  return ((l3xc_t *) (((char *) node) -
		      STRUCT_OFFSET_OF (l3xc_t, l3xc_node)));
}

static void
l3xc_last_lock_gone (fib_node_t * node)
{
}

/*
 * A back walk has reached this L3XC policy
 */
static fib_node_back_walk_rc_t
l3xc_back_walk_notify (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
{
  l3xc_stack (l3xc_get_from_node (node));

  return (FIB_NODE_BACK_WALK_CONTINUE);
}

/*
 * The BIER fmask's graph node virtual function table
 */
static const fib_node_vft_t l3xc_vft = {
  .fnv_get = l3xc_get_node,
  .fnv_last_lock = l3xc_last_lock_gone,
  .fnv_back_walk = l3xc_back_walk_notify,
};

static clib_error_t *
l3xc_init (vlib_main_t * vm)
{
  l3xc_fib_node_type = fib_node_register_new_type (&l3xc_vft);

  return (NULL);
}

VLIB_INIT_FUNCTION (l3xc_init);

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */