aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/fib/fib_entry.c
AgeCommit message (Collapse)AuthorFilesLines
2018-01-18FIB Inherited SrouceNeale Ranns1-82/+156
forwarding provided by the source is pushed to all other entries it covers in the sub-tree Change-Id: I2a45222ef653358f55c2436de3e3c6353cfadba2 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-12-05Revert "FIB: optimise for src memory allocations"Neale Ranns1-45/+22
This reverts commit 84517cfd1508f6da24937f310f7fffe752f22584. Change-Id: Ic7eeffa2ed4607d3d653f34b93c20c833c789ee1 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-11-26FIB: optimise for src memory allocationsNeale Ranns1-22/+45
Most FIB entries will only ever have one source providing forwarding information. Currently the source infom is stored in a vector of sources on the FIB entry. Change this to a union of one source inline and a vector. This saves the need to alloc a vector of sources for each FIB entry. before: vpp# ip route add count 1500000 1.0.0.1/32 via 10.10.10.2 loop0 4.392857e5 routes/sec vpp# ip route del count 1500000 1.0.0.1/32 via 10.10.10.2 loop0 9.175464e5 routes/sec vpp# ip route add count 1500000 1.0.0.1/32 via 10.10.10.2 loop0 5.193375e5 routes/sec vpp# sh fib mem FIB memory Name Size in-use /allocated totals Entry 72 1500011/ 1500011 108000792/108000792 Entry Source 32 1500011/ 1500011 48000352/48000352 after: vpp# ip route add count 1500000 1.0.0.1/32 via 10.10.10.2 loop0 4.726560e5 routes/sec vpp# ip route del count 1500000 1.0.0.1/32 via 10.10.10.2 loop0 1.041629e6 routes/sec vpp# ip route add count 1500000 1.0.0.1/32 via 10.10.10.2 loop0 5.702895e5 routes/sec vpp# sh fib mem FIB memory Name Size in-use /allocated totals Entry 96 1500011/ 1500011 144001056/144001056 Entry Source 32 0 / 0 0/0 Change-Id: Ic71e413eaff1ec152656beda3b94186f7894ea49 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-11-26FIB: store the node type not the function pointer.Neale Ranns1-2/+0
Saves memory at no appreciable performance cost. before: DBGvpp# sh fib mem FIB memory Name Size in-use /allocated totals Entry 80 7 / 150 560/12000 after: DBGvpp# sh fib mem FIB memory Name Size in-use /allocated totals Entry 72 7 / 7 504/504 Change-Id: Ic5d3920ceb57b54260dc9af2078c26484335fef1 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-11-09BIERNeale Ranns1-0/+1
- see draft-ietf-bier-mpls-encapsulation-10 - midpoint, head and tail functions - supported payload protocols; IPv4 and IPv6 only. Change-Id: I59d7363bb6fdfdce8e4016a68a9c8f5a5e5791cb Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-09-13Add a name to the creation of an IP and MPLS tableNeale Ranns1-4/+2
Change-Id: I4b4648831551519b2ffb6f93255d28a4b8726c22 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-09-11FIB table add/delete APINeale Ranns1-2/+13
part 2; - this adds the code to create an IP and MPLS table via the API. - but the enforcement that the table must be created before it is used is still missing, this is so that CSIT can pass. Change-Id: Id124d884ade6cb7da947225200e3bb193454c555 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-08-08L2 over MPLSNeale Ranns1-1/+7
[support for VPWS/VPLS] - switch to using dpo_proto_t rather than fib_protocol_t in fib_paths so that we can describe L2 paths - VLIB nodes to handle pop/push of MPLS labels to L2 Change-Id: Id050d06a11fd2c9c1c81ce5a0654e6c5ae6afa6e Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-07-18FIB path preferenceNeale Ranns1-1/+1
Paths are given a preference, lowest value is 'best'. Only paths that are up are up contribute to fprwarding - that's unchanged. What's new is that of the path's that re up only those that have the best preference contribute. A poor man's primary and backup. It's not true primary/backup function because the FIB must converge before the lower preference paths are used. Change-Id: Ie4453c4a7b1094c6c2b51fe1594b8302103bb68e Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-06-21ARP: ignore non-connected routes and non-interface sources when determing if ↵Neale Ranns1-3/+7
source is connected Change-Id: I39fb0ec44cc322eaa12c0ff0700fc405d3982bfc Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-05-23ARP/ND entries for the same address on different interfaces (VPP-848)Neale Ranns1-10/+2
there are, intentionally, no validation checks in the ARP/ND code to prevent an ARP/ND entry from being installed for an address that is not local to the interface's sub-net. This is ok, since the adjacency/FIB code is designed to handle this case using the 'refinement' criteria - i.e. only installing a FIB entry for the address if the address 'refines' (i.e. is more specific than) the interface's sub-net. However, the refinement criteria currently operates on the FIB entry's prefix (which is a /32, so on the address) and not on the next-hop in the path. So, enter multiple ARP entries for the same address on different links, and this refinement criteria uses only the last added path, and so will remove the FIB entry should the ARP entries be added in the 'wrong' order. This fix updates the refinement criteria to work on each path of the FIB entry. The entry is installed if one of the paths refines the covers and only paths refining the cover contribute forwarding. Per-path refinement checks are stored in path-extensions. The patch is rather large as path-extension, which were previously used only for out-going MPLS labels, have been generalized. Change-Id: I00be359148cb948c32c52109e832a70537a7920a Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-04-26IP Flow Hash Config fixesNeale Ranns1-0/+29
- the flow hash config is (and was) cached on the load-balance object so the fib_table_t struct is not used a switch time. Therefore changes to the table's flow hash config need to be propagated to all load-balances and hance all FIB entries in the table. - enable API for setting the IPv6 table flow hash config - use only the hash config in the fib_table_t object and not on the ipX_fib_t - add tests. Change-Id: Ib804c11162c6d4972c764957562c372f663e05d4 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-04-07MPLS McastNeale Ranns1-7/+40
1 - interface-DPO Used in the Data-plane to change a packet's input interface 2 - MPLS multicast FIB entry Same as a unicast entry but it links to a replicate not a load-balance DPO 3 - Multicast MPLS tunnel Update MPLS tunnels to use a FIB path-list to describe the endpoint[s]. Use the path-list to generate the forwarding chain (DPOs) to link to . 4 - Resolve a path via a local label (of an mLDP LSP) For IP multicast entries to use an LSP in the replication list, we need to decribe the 'resolve-via-label' where the label is that of a multicast LSP. 5 - MPLS disposition path sets RPF-ID For a interface-less LSP (i.e. mLDP not RSVP-TE) at the tail of the LSP we still need to perform an RPF check. An MPLS disposition DPO performs the MPLS pop validation checks and sets the RPF-ID in the packet. 6 - RPF check with per-entry RPF-ID An RPF-ID is used instead of a real interface SW if index in the case the IP traffic arrives from an LSP that does not have an associated interface. Change-Id: Ib92e177be919147bafeb599729abf3d1abc2f4b3 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-04-06BFD-FIB interactionsNeale Ranns1-16/+34
- single-hop BFD: attach a delegate to the appropriate adjacency - multi-hop BFD [not supported yet]: attach a delegate to the FIB entry. adjacency/fib_entry state tracks the BFD session state. when the state is down the object does not contribute forwarding hence and hence dependent objects will not use it. For example, if a route is ECMP via two adjacencies and one of them is BFD down, then only the other is used to forward (i.e. we don't drop half the traffic). Change-Id: I0ef53e20e73b067001a132cd0a3045408811a822 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-04-01MTRIE Optimisations 2Neale Ranns1-2/+2
1) 16-8-8 stride. Reduce trie depth walk traded with increased memory in the top PLY. 2) separate the vector of protocol-independent (PI) fib_table_t with the vector of protocol dependent (PD) FIBs. PD FIBs are large structures, we don't want to burn the memory for ech PD type 3) Go straight to the PD FIB in the data-path thus avoiding an indirection through, e.g., a PLY pool. Change-Id: I800d1ed0b2049040d5da95213f3ed6b12bdd78b7 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-02-24FIB: 1) fix pool realloc during prefix export. 2) don't walk off the end of ↵Neale Ranns1-4/+24
the path-extension vector Change-Id: I8bd8f6917ace089edb1f65bd017b478ee198c03f Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-02-20FIB reset leaves residual routes. Wrong API used to remove the routes meant ↵Neale Ranns1-1/+4
the lock count on the entry did not drop to zero Change-Id: I6e2dff8c3c7976fd1c2e4c5258f5dc73123aa9b7 Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-01-05FIB memory leaks (VPP-578)Neale Ranns1-0/+1
1) vec_free the fe_srcs of a fib_entry_t when the fib_entry_t is itself reed 2) in the load-balance fixup if a drop path is required add this to a new vector of next-hops 'fixed_nhs'. This vector is managed by the load-balance function. The caller continues to manage its own set. The function is now const implying that the caller is safe to assume the next-hops do not change. Change-Id: I0f29203ee16b9a270f40edf237488fa99ba65320 Signed-off-by: Neale Ranns <nranns@cisco.com> Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
2016-12-28Reorganize source tree to use single autotools instanceDamjan Marion1-0/+1503
Change-Id: I7b51f88292e057c6443b12224486f2d0c9f8ae23 Signed-off-by: Damjan Marion <damarion@cisco.com>
} /* 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) 2015 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.
 */

/*
 *------------------------------------------------------------------
 * ad-flow.c - SRv6 Flow-based Dynamic Proxy (AD.Flow) behavior
 *------------------------------------------------------------------
 */

#include <vnet/vnet.h>
#include <vnet/adj/adj.h>
#include <vnet/plugin/plugin.h>
#include <vpp/app/version.h>
#include <srv6-ad-flow/ad-flow.h>

#include <vppinfra/bihash_template.c>

#define SID_CREATE_IFACE_FEATURE_ERROR -1
#define SID_CREATE_INVALID_IFACE_TYPE  -3
#define SID_CREATE_INVALID_IFACE_INDEX -4
#define SID_CREATE_INVALID_ADJ_INDEX   -5

unsigned char function_name[] = "SRv6-AD-Flow-plugin";
unsigned char keyword_str[] = "End.AD.Flow";
unsigned char def_str[] =
  "Endpoint with flow-based dynamic proxy to SR-unaware appliance";
unsigned char params_str[] = "nh <next-hop> oif <iface-out> iif <iface-in>";

srv6_ad_flow_main_t srv6_ad_flow_main;

static u32
ad_flow_calc_bihash_buckets (u32 n_elts)
{
  return 1 << (max_log2 (n_elts >> 1) + 1);
}

static u32
ad_flow_calc_bihash_memory (u32 n_buckets, uword kv_size)
{
  return n_buckets * (8 + kv_size * 4);
}

/*****************************************/
/* SRv6 LocalSID instantiation and removal functions */
static int
srv6_ad_flow_localsid_creation_fn (ip6_sr_localsid_t *localsid)
{
  ip6_sr_main_t *srm = &sr_main;
  srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
  srv6_ad_flow_localsid_t *ls_mem = localsid->plugin_mem;
  u32 localsid_index = localsid - srm->localsids;

  /* Step 1: Prepare xconnect adjacency for sending packets to the VNF */

  /* Retrieve the adjacency corresponding to the (OIF, next_hop) */
  adj_index_t nh_adj_index = ADJ_INDEX_INVALID;
  if (ls_mem->inner_type == AD_TYPE_IP4)
    nh_adj_index =
      adj_nbr_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4, &ls_mem->nh_addr,
			   ls_mem->sw_if_index_out);
  else if (ls_mem->inner_type == AD_TYPE_IP6)
    nh_adj_index =
      adj_nbr_add_or_lock (FIB_PROTOCOL_IP6, VNET_LINK_IP6, &ls_mem->nh_addr,
			   ls_mem->sw_if_index_out);

  if (nh_adj_index == ADJ_INDEX_INVALID)
    {
      clib_mem_free (ls_mem);
      return SID_CREATE_INVALID_ADJ_INDEX;
    }

  ls_mem->nh_adj = nh_adj_index;

  /* Step 2: Prepare inbound policy for packets returning from the VNF */

  /* Sanitise the SW_IF_INDEX */
  if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces,
			  ls_mem->sw_if_index_in))
    {
      adj_unlock (ls_mem->nh_adj);
      clib_mem_free (ls_mem);
      return SID_CREATE_INVALID_IFACE_INDEX;
    }

  vnet_sw_interface_t *sw =
    vnet_get_sw_interface (sm->vnet_main, ls_mem->sw_if_index_in);
  if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
    {
      adj_unlock (ls_mem->nh_adj);
      clib_mem_free (ls_mem);
      return SID_CREATE_INVALID_IFACE_TYPE;
    }

  if (ls_mem->inner_type == AD_TYPE_IP4)
    {
      /* Enable End.AD4 rewrite node for this interface */
      int ret =
	vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-flow-rewrite",
				     ls_mem->sw_if_index_in, 1, 0, 0);
      if (ret != 0)
	{
	  adj_unlock (ls_mem->nh_adj);
	  clib_mem_free (ls_mem);
	  return SID_CREATE_IFACE_FEATURE_ERROR;
	}

      /* Associate local SID index to this interface (resize vector if needed)
       */
      if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid4))
	{
	  vec_resize (sm->sw_iface_localsid4,
		      (pool_len (sm->vnet_main->interface_main.sw_interfaces) -
		       vec_len (sm->sw_iface_localsid4)));
	}
      sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = localsid_index;
    }
  else if (ls_mem->inner_type == AD_TYPE_IP6)
    {
      /* Enable End.AD6 rewrite node for this interface */
      int ret =
	vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-flow-rewrite",
				     ls_mem->sw_if_index_in, 1, 0, 0);
      if (ret != 0)
	{
	  adj_unlock (ls_mem->nh_adj);
	  clib_mem_free (ls_mem);
	  return SID_CREATE_IFACE_FEATURE_ERROR;
	}

      /* Associate local SID index to this interface (resize vector if needed)
       */
      if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid6))
	{
	  vec_resize (sm->sw_iface_localsid6,
		      (pool_len (sm->vnet_main->interface_main.sw_interfaces) -
		       vec_len (sm->sw_iface_localsid6)));
	}
      sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = localsid_index;
    }

  /* Initialize flow and cache tables */
  ls_mem->cache_size = SRV6_AD_FLOW_DEFAULT_CACHE_SIZE;
  ls_mem->cache_buckets = ad_flow_calc_bihash_buckets (ls_mem->cache_size);
  ls_mem->cache_memory_size = ad_flow_calc_bihash_memory (
    ls_mem->cache_buckets, sizeof (clib_bihash_40_8_t));

  pool_alloc (ls_mem->cache, ls_mem->cache_size);
  pool_alloc (ls_mem->lru_pool, ls_mem->cache_size);

  dlist_elt_t *head;
  pool_get (ls_mem->lru_pool, head);
  ls_mem->lru_head_index = head - ls_mem->lru_pool;
  clib_dlist_init (ls_mem->lru_pool, ls_mem->lru_head_index);

  clib_bihash_init_40_8 (&ls_mem->ftable, "ad-flow", ls_mem->cache_buckets,
			 ls_mem->cache_memory_size);

  /* Step 3: Initialize rewrite counters */
  srv6_ad_flow_localsid_t **ls_p;
  pool_get (sm->sids, ls_p);
  *ls_p = ls_mem;
  ls_mem->index = ls_p - sm->sids;

  vlib_validate_combined_counter (&(sm->sid_bypass_counters), ls_mem->index);
  vlib_validate_combined_counter (&(sm->sid_punt_counters), ls_mem->index);
  vlib_validate_combined_counter (&(sm->sid_cache_full_counters),
				  ls_mem->index);
  vlib_validate_combined_counter (&(sm->rw_valid_counters), ls_mem->index);
  vlib_validate_combined_counter (&(sm->rw_invalid_counters), ls_mem->index);

  vlib_zero_combined_counter (&(sm->sid_bypass_counters), ls_mem->index);
  vlib_zero_combined_counter (&(sm->sid_punt_counters), ls_mem->index);
  vlib_zero_combined_counter (&(sm->sid_cache_full_counters), ls_mem->index);
  vlib_zero_combined_counter (&(sm->rw_valid_counters), ls_mem->index);
  vlib_zero_combined_counter (&(sm->rw_invalid_counters), ls_mem->index);

  return 0;
}

static int
srv6_ad_flow_localsid_removal_fn (ip6_sr_localsid_t *localsid)
{
  srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
  srv6_ad_flow_localsid_t *ls_mem = localsid->plugin_mem;

  if (ls_mem->inner_type == AD_TYPE_IP4)
    {
      /* Disable End.AD4 rewrite node for this interface */
      int ret =
	vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-flow-rewrite",
				     ls_mem->sw_if_index_in, 0, 0, 0);
      if (ret != 0)
	return -1;

      /* Remove local SID pointer from interface table */
      sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = ~(u32) 0;
    }
  else if (ls_mem->inner_type == AD_TYPE_IP6)
    {
      /* Disable End.AD6 rewrite node for this interface */
      int ret =
	vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-flow-rewrite",
				     ls_mem->sw_if_index_in, 0, 0, 0);
      if (ret != 0)
	return -1;

      /* Remove local SID pointer from interface table */
      sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = ~(u32) 0;
    }

  /* Unlock (OIF, NHOP) adjacency */
  adj_unlock (ls_mem->nh_adj);

  /* Delete SID entry */
  pool_put (sm->sids, pool_elt_at_index (sm->sids, ls_mem->index));

  /* Clean up local SID memory */
  srv6_ad_flow_entry_t *e;
  pool_foreach (e, ls_mem->cache)
    {
      vec_free (e->rw_data);
    }
  pool_free (ls_mem->cache);
  pool_free (ls_mem->lru_pool);
  clib_bihash_free_40_8 (&ls_mem->ftable);
  clib_mem_free (localsid->plugin_mem);

  return 0;
}

/**********************************/
/* SRv6 LocalSID format functions */
/*
 * Prints nicely the parameters of a localsid
 * Example: print "Table 5"
 */
u8 *
format_srv6_ad_flow_localsid (u8 *s, va_list *args)
{
  srv6_ad_flow_localsid_t *ls_mem = va_arg (*args, void *);

  vnet_main_t *vnm = vnet_get_main ();
  srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;

  if (ls_mem->inner_type == AD_TYPE_IP4)
    {
      s = format (s, "Next-hop:\t%U\n\t", format_ip4_address,
		  &ls_mem->nh_addr.ip4);
    }
  else if (ls_mem->inner_type == AD_TYPE_IP6)
    {
      s = format (s, "Next-hop:\t%U\n\t", format_ip6_address,
		  &ls_mem->nh_addr.ip6);
    }

  s = format (s, "Outgoing iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
	      ls_mem->sw_if_index_out);
  s = format (s, "\tIncoming iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
	      ls_mem->sw_if_index_in);

  vlib_counter_t sid_bypass, sid_punt, sid_full, rw_valid, rw_invalid;
  vlib_get_combined_counter (&(sm->sid_bypass_counters), ls_mem->index,
			     &sid_bypass);
  vlib_get_combined_counter (&(sm->sid_punt_counters), ls_mem->index,
			     &sid_punt);
  vlib_get_combined_counter (&(sm->sid_cache_full_counters), ls_mem->index,
			     &sid_full);
  vlib_get_combined_counter (&(sm->rw_valid_counters), ls_mem->index,
			     &rw_valid);
  vlib_get_combined_counter (&(sm->rw_invalid_counters), ls_mem->index,
			     &rw_invalid);

  s =
    format (s, "\tTraffic that bypassed the NF: \t[%Ld packets : %Ld bytes]\n",
	    sid_bypass.packets, sid_bypass.bytes);
  s = format (s, "\tPunted traffic: \t[%Ld packets : %Ld bytes]\n",
	      sid_punt.packets, sid_punt.bytes);
  s =
    format (s, "\tDropped traffic (cache full): \t[%Ld packets : %Ld bytes]\n",
	    sid_full.packets, sid_full.bytes);
  s = format (s, "\tGood rewrite traffic: \t[%Ld packets : %Ld bytes]\n",
	      rw_valid.packets, rw_valid.bytes);
  s = format (s, "\tBad rewrite traffic:  \t[%Ld packets : %Ld bytes]\n",
	      rw_invalid.packets, rw_invalid.bytes);

  return s;
}

/*
 * Process the parameters of a localsid
 * Example: process from:
 * sr localsid address cafe::1 behavior new_srv6_localsid 5
 * everything from behavior on... so in this case 'new_srv6_localsid 5'
 * Notice that it MUST match the keyword_str and params_str defined above.
 */
uword
unformat_srv6_ad_flow_localsid (unformat_input_t *input, va_list *args)
{
  void **plugin_mem_p = va_arg (*args, void **);
  srv6_ad_flow_localsid_t *ls_mem;

  vnet_main_t *vnm = vnet_get_main ();

  u8 inner_type = AD_TYPE_IP4;
  ip46_address_t nh_addr;
  u32 sw_if_index_out;
  u32 sw_if_index_in;

  u8 params = 0;
#define PARAM_AD_NH  (1 << 0)
#define PARAM_AD_OIF (1 << 1)
#define PARAM_AD_IIF (1 << 2)

  if (!unformat (input, "end.ad.flow"))
    return 0;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (!(params & PARAM_AD_NH) &&
	  unformat (input, "nh %U", unformat_ip4_address, &nh_addr.ip4))
	{
	  inner_type = AD_TYPE_IP4;
	  params |= PARAM_AD_NH;
	}
      if (!(params & PARAM_AD_NH) &&
	  unformat (input, "nh %U", unformat_ip6_address, &nh_addr.ip6))
	{
	  inner_type = AD_TYPE_IP6;
	  params |= PARAM_AD_NH;
	}
      else if (!(params & PARAM_AD_OIF) &&
	       unformat (input, "oif %U", unformat_vnet_sw_interface, vnm,
			 &sw_if_index_out))
	{
	  params |= PARAM_AD_OIF;
	}
      else if (!(params & PARAM_AD_IIF) &&
	       unformat (input, "iif %U", unformat_vnet_sw_interface, vnm,
			 &sw_if_index_in))
	{
	  params |= PARAM_AD_IIF;
	}
      else
	{
	  break;
	}
    }

  /* Make sure that all parameters are supplied */
  u8 params_chk = (PARAM_AD_NH | PARAM_AD_OIF | PARAM_AD_IIF);
  if ((params & params_chk) != params_chk)
    {
      return 0;
    }

  /* Allocate and initialize memory block for local SID parameters */
  ls_mem = clib_mem_alloc_aligned_at_offset (sizeof *ls_mem, 0, 0, 1);
  clib_memset (ls_mem, 0, sizeof *ls_mem);
  *plugin_mem_p = ls_mem;

  /* Set local SID parameters */
  ls_mem->inner_type = inner_type;
  if (inner_type == AD_TYPE_IP4)
    ls_mem->nh_addr.ip4 = nh_addr.ip4;
  else if (inner_type == AD_TYPE_IP6)
    ls_mem->nh_addr.ip6 = nh_addr.ip6;
  ls_mem->sw_if_index_out = sw_if_index_out;
  ls_mem->sw_if_index_in = sw_if_index_in;

  return 1;
}

/*************************/
/* SRv6 LocalSID FIB DPO */
static u8 *
format_srv6_ad_flow_dpo (u8 *s, va_list *args)
{
  index_t index = va_arg (*args, index_t);
  CLIB_UNUSED (u32 indent) = va_arg (*args, u32);

  return (format (s, "SR: dynamic_proxy_index:[%u]", index));
}

void
srv6_ad_flow_dpo_lock (dpo_id_t *dpo)
{
}

void
srv6_ad_flow_dpo_unlock (dpo_id_t *dpo)
{
}

const static dpo_vft_t srv6_ad_flow_vft = {
  .dv_lock = srv6_ad_flow_dpo_lock,
  .dv_unlock = srv6_ad_flow_dpo_unlock,
  .dv_format = format_srv6_ad_flow_dpo,
};

const static char *const srv6_ad_flow_ip6_nodes[] = {
  "srv6-ad-flow-localsid",
  NULL,
};

const static char *const *const srv6_ad_flow_nodes[DPO_PROTO_NUM] = {
  [DPO_PROTO_IP6] = srv6_ad_flow_ip6_nodes,
};

/**********************/
static clib_error_t *
srv6_ad_flow_init (vlib_main_t *vm)
{
  srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
  int rv = 0;

  sm->vlib_main = vm;
  sm->vnet_main = vnet_get_main ();

  /* Create DPO */
  sm->srv6_ad_flow_dpo_type =
    dpo_register_new_type (&srv6_ad_flow_vft, srv6_ad_flow_nodes);

  /* Register SRv6 LocalSID */
  rv = sr_localsid_register_function (
    vm, function_name, keyword_str, def_str, params_str, 128,
    &sm->srv6_ad_flow_dpo_type, format_srv6_ad_flow_localsid,
    unformat_srv6_ad_flow_localsid, srv6_ad_flow_localsid_creation_fn,
    srv6_ad_flow_localsid_removal_fn);
  if (rv < 0)
    clib_error_return (0, "SRv6 LocalSID function could not be registered.");
  else
    sm->srv6_localsid_behavior_id = rv;

  return 0;
}

VNET_FEATURE_INIT (srv6_ad4_flow_rewrite, static) = {
  .arc_name = "ip4-unicast",
  .node_name = "srv6-ad4-flow-rewrite",
  .runs_before = 0,
};

VNET_FEATURE_INIT (srv6_ad6_flow_rewrite, static) = {
  .arc_name = "ip6-unicast",
  .node_name = "srv6-ad6-flow-rewrite",
  .runs_before = 0,
};

VLIB_INIT_FUNCTION (srv6_ad_flow_init);

VLIB_PLUGIN_REGISTER () = {
  .version = VPP_BUILD_VER,
  .description = "Dynamic Segment Routing for IPv6 (SRv6) Proxy",
};

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