summaryrefslogtreecommitdiffstats
path: root/src/plugins/srv6-ad/ad.c
blob: 045ddeb466d01d766fcce8c61ffec3ccfd244043 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

@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) 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.
 */
/*
  Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus

  Permission is hereby granted, free of charge, to any person obtaining
  a copy of this software and associated documentation files (the
  "Software"), to deal in the Software without restriction, including
  without limitation the rights to use, copy, modify, merge, publish,
  distribute, sublicense, and/or sell copies of the Software, and to
  permit persons to whom the Software is furnished to do so, subject to
  the following conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef included_clib_string_h
#define included_clib_string_h

#include <vppinfra/clib.h>	/* for CLIB_LINUX_KERNEL */
#include <vppinfra/vector.h>

#ifdef CLIB_LINUX_KERNEL
#include <linux/string.h>
#endif

#ifdef CLIB_UNIX
#include <string.h>
#endif

#ifdef CLIB_STANDALONE
#include <vppinfra/standalone_string.h>
#endif

/* Exchanges source and destination. */
void clib_memswap (void *_a, void *_b, uword bytes);

/*
 * the vector unit memcpy variants confuse coverity
 * so don't let it anywhere near them.
 */
#ifndef __COVERITY__
#if __AVX__
#include <vppinfra/memcpy_avx.h>
#elif __SSSE3__
#include <vppinfra/memcpy_sse3.h>
#else
#define clib_memcpy(a,b,c) memcpy(a,b,c)
#endif
#else /* __COVERITY__ */
#define clib_memcpy(a,b,c) memcpy(a,b,c)
#endif

#endi
/*
 * 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.c - SRv6 Dynamic Proxy (AD) function
 *------------------------------------------------------------------
 */

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

#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-plugin";
unsigned char keyword_str[] = "End.AD";
unsigned char def_str[] =
  "Endpoint with dynamic proxy to SR-unaware appliance";
unsigned char params_str[] = "nh <next-hop> oif <iface-out> iif <iface-in>";

srv6_ad_main_t srv6_ad_main;

/*****************************************/
/* SRv6 LocalSID instantiation and removal functions */
static int
srv6_ad_localsid_creation_fn (ip6_sr_localsid_t * localsid)
{
  ip6_sr_main_t *srm = &sr_main;
  srv6_ad_main_t *sm = &srv6_ad_main;
  srv6_ad_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_L2)
    {
      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_L2)
    {
      /* Enable End.AD2 rewrite node for this interface */
      int ret =
	vnet_feature_enable_disable ("device-input", "srv6-ad2-rewrite",
				     ls_mem->sw_if_index_in, 1, 0, 0);
      if (ret != 0)
	{
	  clib_mem_free (ls_mem);
	  return SID_CREATE_IFACE_FEATURE_ERROR;
	}

      /* Set interface in promiscuous mode */
      vnet_main_t *vnm = vnet_get_main ();
      vnet_hw_interface_t *hi =
	vnet_get_sup_hw_interface (vnm, ls_mem->sw_if_index_in);
      /* Make sure it is main interface */
      if (hi->sw_if_index == ls_mem->sw_if_index_in)
	ethernet_set_flags (vnm, hi->hw_if_index,
			    ETHERNET_INTERFACE_FLAG_ACCEPT_ALL);

      /* Associate local SID index to this interface (resize vector if needed) */
      if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid2))
	{
	  vec_resize (sm->sw_iface_localsid2,
		      (pool_len (sm->vnet_main->interface_main.sw_interfaces)
		       - vec_len (sm->sw_iface_localsid2)));
	}
      sm->sw_iface_localsid2[ls_mem->sw_if_index_in] = localsid_index;
    }
  else 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-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-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;
    }

  ls_mem->rw_len = 0;

  /* Step 3: Initialize rewrite counters */
  srv6_ad_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->valid_counters), ls_mem->index);
  vlib_validate_combined_counter (&(sm->invalid_counters), ls_mem->index);

  vlib_zero_combined_counter (&(sm->valid_counters), ls_mem->index);
  vlib_zero_combined_counter (&(sm->invalid_counters), ls_mem->index);

  return 0;
}

static int
srv6_ad_localsid_removal_fn (ip6_sr_localsid_t * localsid)
{
  srv6_ad_main_t *sm = &srv6_ad_main;
  srv6_ad_localsid_t *ls_mem = localsid->plugin_mem;

  if (ls_mem->inner_type == AD_TYPE_L2)
    {
      /* Disable End.AD2 rewrite node for this interface */
      int ret =
	vnet_feature_enable_disable ("device-input", "srv6-ad2-rewrite",
				     ls_mem->sw_if_index_in, 0, 0, 0);
      if (ret != 0)
	return -1;

      /* Disable promiscuous mode on the interface */
      vnet_main_t *vnm = vnet_get_main ();
      vnet_hw_interface_t *hi =
	vnet_get_sup_hw_interface (vnm, ls_mem->sw_if_index_in);
      /* Make sure it is main interface */
      if (hi->sw_if_index == ls_mem->sw_if_index_in)
	ethernet_set_flags (vnm, hi->hw_if_index, 0);

      /* Remove local SID index from interface table */
      sm->sw_iface_localsid2[ls_mem->sw_if_index_in] = ~(u32) 0;
    }
  else 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-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-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 */
  vec_free (ls_mem->rewrite);
  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_localsid (u8 * s, va_list * args)
{
  srv6_ad_localsid_t *ls_mem = va_arg (*args, void *);

  vnet_main_t *vnm = vnet_get_main ();
  srv6_ad_main_t *sm = &srv6_ad_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 valid, invalid;
  vlib_get_combined_counter (&(sm->valid_counters), ls_mem->index, &valid);
  vlib_get_combined_counter (&(sm->invalid_counters), ls_mem->index,
			     &invalid);
  s = format (s, "\tGood rewrite traffic: \t[%Ld packets : %Ld bytes]\n",
	      valid.packets, valid.bytes);
  s = format (s, "\tBad rewrite traffic:  \t[%Ld packets : %Ld bytes]\n",
	      invalid.packets, 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_localsid (unformat_input_t * input, va_list * args)
{
  void **plugin_mem_p = va_arg (*args, void **);
  srv6_ad_localsid_t *ls_mem;

  vnet_main_t *vnm = vnet_get_main ();

  u8 inner_type = AD_TYPE_L2;
  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"))
    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_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_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_dpo_lock (dpo_id_t * dpo)
{
}

void
srv6_ad_dpo_unlock (dpo_id_t * dpo)
{
}

const static dpo_vft_t srv6_ad_vft = {
  .dv_lock = srv6_ad_dpo_lock,
  .dv_unlock = srv6_ad_dpo_unlock,
  .dv_format = format_srv6_ad_dpo,
};

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

const static char *const *const srv6_ad_nodes[DPO_PROTO_NUM] = {
  [DPO_PROTO_IP6] = srv6_ad_ip6_nodes,
};

/**********************/
static clib_error_t *
srv6_ad_init (vlib_main_t * vm)
{
  srv6_ad_main_t *sm = &srv6_ad_main;
  int rv = 0;

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

  /* Create DPO */
  sm->srv6_ad_dpo_type = dpo_register_new_type (&srv6_ad_vft, srv6_ad_nodes);

  /* Register SRv6 LocalSID */
  rv = sr_localsid_register_function (vm,
				      function_name,
				      keyword_str,
				      def_str,
				      params_str,
				      128,
				      &sm->srv6_ad_dpo_type,
				      format_srv6_ad_localsid,
				      unformat_srv6_ad_localsid,
				      srv6_ad_localsid_creation_fn,
				      srv6_ad_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;
}

/* *INDENT-OFF* */
VNET_FEATURE_INIT (srv6_ad2_rewrite, static) =
{
  .arc_name = "device-input",
  .node_name = "srv6-ad2-rewrite",
  .runs_before = VNET_FEATURES ("ethernet-input"),
};

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

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

VLIB_INIT_FUNCTION (srv6_ad_init);

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

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