text: /*
   * nsh.c - nsh mapping
   *
   * Copyright (c) 2013 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 <vnet/vnet.h>
  #include <vnet/plugin/plugin.h>
  #include <nsh/nsh.h>
  #include <vnet/gre/gre.h>
  #include <vnet/vxlan/vxlan.h>
  #include <vnet/vxlan-gpe/vxlan_gpe.h>
  #include <vnet/l2/l2_classify.h>
  #include <vnet/adj/adj.h>
  #include <vpp/app/version.h>
  
  nsh_main_t nsh_main;
  
  /* Uses network order's class and type to register */
  int
  nsh_md2_register_option (u16 class,
  			 u8 type,
  			 u8 option_size,
  			 int add_options (u8 * opt,
  					  u8 * opt_size),
  			 int options (vlib_buffer_t * b,
  				      nsh_tlv_header_t * opt),
  			 int swap_options (vlib_buffer_t * b,
  					   nsh_tlv_header_t * old_opt,
  					   nsh_tlv_header_t * new_opt),
  			 int pop_options (vlib_buffer_t * b,
  					  nsh_tlv_header_t * opt),
  			 u8 * trace (u8 * s, nsh_tlv_header_t * opt))
  {
    nsh_main_t *nm = &nsh_main;
    nsh_option_map_by_key_t key, *key_copy;
    uword *p;
    nsh_option_map_t *nsh_option;
  
    key.class = class;
    key.type = type;
    key.pad = 0;
  
    p = hash_get_mem (nm->nsh_option_map_by_key, &key);
    /* Already registered */
    if (p != 0)
      {
        return (-1);
      }
  
    pool_get_aligned (nm->nsh_option_mappings, nsh_option,
  		    CLIB_CACHE_LINE_BYTES);
    clib_memset (nsh_option, 0, sizeof (*nsh_option));
    nsh_option->option_id = nsh_option - nm->nsh_option_mappings;
  
    key_copy = clib_mem_alloc (sizeof (*key_copy));
    clib_memcpy (key_copy, &key, sizeof (*key_copy));
    hash_set_mem (nm->nsh_option_map_by_key, key_copy,
  		nsh_option - nm->nsh_option_mappings);
  
    if (option_size > (MAX_NSH_OPTION_LEN + sizeof (nsh_tlv_header_t)))
      {
        return (-1);
      }
    nm->options_size[nsh_option->option_id] = option_size;
    nm->add_options[nsh_option->option_id] = add_options;
    nm->options[nsh_option->option_id] = options;
    nm->swap_options[nsh_option->option_id] = swap_options;
    nm->pop_options[nsh_option->option_id] = pop_options;
    nm->trace[nsh_option->option_id] = trace;
  
    return (0);
  }
  
  /* Uses network order's class and type to lookup */
  nsh_option_map_t *
  nsh_md2_lookup_option (u16 class, u8 type)
  {
    nsh_main_t *nm = &nsh_main;
    nsh_option_map_by_key_t key;
    uword *p;
  
    key.class = class;
    key.type = type;
    key.pad = 0;
  
    p = hash_get_mem (nm->nsh_option_map_by_key, &key);
    /* not registered */
    if (p == 0)
      {
        return NULL;
      }
  
    return pool_elt_at_index (nm->nsh_option_mappings, p[0]);
  
  }
  
  /* Uses network order's class and type to unregister */
  int
  nsh_md2_unregister_option (u16 class,
  			   u8 type,
  			   int options (vlib_buffer_t * b,
  					nsh_tlv_header_t * opt),
  			   u8 * trace (u8 * s, nsh_tlv_header_t * opt))
  {
    nsh_main_t *nm = &nsh_main;
    nsh_option_map_by_key_t key, *key_copy;
    uword *p;
    hash_pair_t *hp;
    nsh_option_map_t *nsh_option;
  
    key.class = class;
    key.type = type;
    key.pad = 0;
  
    p = hash_get_mem (nm->nsh_option_map_by_key, &key);
    /* not registered */
    if (p == 0)
      {
        return (-1);
      }
  
    nsh_option = pool_elt_at_index (nm->nsh_option_mappings, p[0]);
    nm->options[nsh_option->option_id] = NULL;
    nm->add_options[nsh_option->option_id] = NULL;
    nm->pop_options[nsh_option->option_id] = NULL;
    nm->trace[nsh_option->option_id] = NULL;
  
    hp = hash_get_pair (nm->nsh_option_map_by_key, &key);
    key_copy = (void *) (hp->key);
    hash_unset_mem (nm->nsh_option_map_by_key, &key_copy);
    clib_mem_free (key_copy);
  
    pool_put (nm->nsh_option_mappings, nsh_option);
  
    return (0);
  }
  
  /**
   * @brief Formatting function for tracing VXLAN GPE with length
   *
   * @param *s
   * @param *args
   *
   * @return *s
   *
   */
  static u8 *
  format_nsh_tunnel_with_length (u8 * s, va_list * args)
  {
    u32 dev_instance = va_arg (*args, u32);
    s = format (s, "unimplemented dev %u", dev_instance);
    return s;
  }
  
  /* *INDENT-OFF* */
  VNET_HW_INTERFACE_CLASS (nsh_hw_class) = {
    .name = "NSH",
    .format_header = format_nsh_tunnel_with_length,
    .build_rewrite = default_build_rewrite,
    .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
  };
  /* *INDENT-ON* */
  
  void
  nsh_md2_set_next_ioam_export_override (uword next)
  {
    nsh_main_t *hm = &nsh_main;
    hm->decap_v4_next_override = next;
    return;
  }
  
  clib_error_t *
  nsh_init (vlib_main_t * vm)
  {
    vlib_node_t *node;
    nsh_main_t *nm = &nsh_main;
    clib_error_t *error = 0;
    uword next_node;
  
    /* Init the main structures from VPP */
    nm->vlib_main = vm;
    nm->vnet_main = vnet_get_main ();
  
    /* Various state maintenance mappings */
    nm->nsh_mapping_by_key = hash_create_mem (0, sizeof (u32), sizeof (uword));
  
    nm->nsh_mapping_by_mapped_key
      = hash_create_mem (0, sizeof (u32), sizeof (uword));
  
    nm->nsh_entry_by_key = hash_create_mem (0, sizeof (u32), sizeof (uword));
  
    nm->nsh_proxy_session_by_key
      =
      hash_create_mem (0, sizeof (nsh_proxy_session_by_key_t), sizeof (uword));
  
    nm->nsh_option_map_by_key
      = hash_create_mem (0, sizeof (nsh_option_map_by_key_t), sizeof (uword));
  
    error = nsh_api_init (vm, nm);
    if (error)
      return error;
  
    node = vlib_get_node_by_name (vm, (u8 *) "nsh-input");
    nm->nsh_input_node_index = node->index;
  
    node = vlib_get_node_by_name (vm, (u8 *) "nsh-proxy");
    nm->nsh_proxy_node_index = node->index;
  
    node = vlib_get_node_by_name (vm, (u8 *) "nsh-classifier");
    nm->nsh_classifier_node_index = node->index;
  
    /* Add dispositions to nodes that feed nsh-input */
    //alagalah - validate we don't really need to use the node value
    next_node =
      vlib_node_add_next (vm, vxlan4_gpe_input_node.index,
  			nm->nsh_input_node_index);
    vlib_node_add_next (vm, vxlan4_gpe_input_node.index,
  		      nm->nsh_proxy_node_index);
    vlib_node_add_next (vm, vxlan4_gpe_input_node.index,
  		      nsh_aware_vnf_proxy_node.index);
    vxlan_gpe_register_decap_protocol (VXLAN_GPE_PROTOCOL_NSH, next_node);
  
    vlib_node_add_next (vm, vxlan6_gpe_input_node.index,
  		      nm->nsh_input_node_index);
    vlib_node_add_next (vm, vxlan6_gpe_input_node.index,
  		      nm->nsh_proxy_node_index);
    vlib_node_add_next (vm, vxlan6_gpe_input_node.index,
  		      nsh_aware_vnf_proxy_node.index);
  
    vlib_node_add_next (vm, gre4_input_node.index, nm->nsh_input_node_index);
    vlib_node_add_next (vm, gre4_input_node.index, nm->nsh_proxy_node_index);
    vlib_node_add_next (vm, gre4_input_node.index,
  		      nsh_aware_vnf_proxy_node.index);
  
    vlib_node_add_next (vm, gre6_input_node.index, nm->nsh_input_node_index);
    vlib_node_add_next (vm, gre6_input_node.index, nm->nsh_proxy_node_index);
    vlib_node_add_next (vm, gre6_input_node.index,
  		      nsh_aware_vnf_proxy_node.index);
  
    /* Add NSH-Proxy support */
    vlib_node_add_next (vm, vxlan4_input_node.index, nm->nsh_proxy_node_index);
    vlib_node_add_next (vm, vxlan6_input_node.index, nm->nsh_proxy_node_index);
  
    /* Add NSH-Classifier support */
    vlib_node_add_next (vm, ip4_classify_node.index,
  		      nm->nsh_classifier_node_index);
    vlib_node_add_next (vm, ip6_classify_node.index,
  		      nm->nsh_classifier_node_index);
    vlib_node_add_next (vm, l2_input_classify_node.index,
  		      nm->nsh_classifier_node_index);
  
    /* Add Ethernet+NSH support */
    ethernet_register_input_type (vm, ETHERNET_TYPE_NSH,
  				nm->nsh_input_node_index);
  
    return error;
  }
  
  VLIB_INIT_FUNCTION (nsh_init);
  
  /* *INDENT-OFF* */
  VLIB_PLUGIN_REGISTER () = {
      .version = VPP_BUILD_VER,
      .description = "Network Service Header",
  };
  /* *INDENT-ON* */
  
  /*
   * fd.io coding-style-patch-verification: ON
   *
   * Local Variables:
   * eval: (c-set-style "gnu")
   * End:
   */
  