/* * 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. */ #include #include #include #include #include #include #include #include #include #include #include #define MFIB_TEST_I(_cond, _comment, _args...) \ ({ \ int _evald = (_cond); \ if (!(_evald)) { \ fformat(stderr, "FAIL:%d: " _comment "\n", \ __LINE__, ##_args); \ res = 1; \ } else { \ fformat(stderr, "PASS:%d: " _comment "\n", \ __LINE__, ##_args); \ } \ res; \ }) #define MFIB_TEST(_cond, _comment, _args...) \ { \ if (MFIB_TEST_I(_cond, _comment, ##_args)) { \ return 1; \ ASSERT(!("FAIL: " _comment)); \ } \ } #define MFIB_TEST_NS(_cond) \ { \ if (MFIB_TEST_I(_cond, "")) { \ return 1; \ ASSERT(!("FAIL: ")); \ } \ } /** * A 'i'm not fussed is this is not efficient' store of test data */ typedef struct test_main_t_ { /** * HW if indicies */ u32 hw_if_indicies[4]; /** * HW interfaces */ vnet_hw_interface_t * hw[4]; } test_main_t; static test_main_t test_main; /* fake ethernet device class, distinct from "fake-ethX" */ static u8 * format_test_interface_name (u8 * s, va_list * args) { u32 dev_instance = va_arg (*args, u32); return format (s, "test-eth%d", dev_instance); } static uword dummy_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { clib_warning ("you shouldn't be here, leaking buffers..."); return frame->n_vectors; } static clib_error_t * test_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags) { u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ? VNET_HW_INTERFACE_FLAG_LINK_UP : 0; vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags); return 0; } VNET_DEVICE_CLASS (test_interface_device_class,static) = { .name = "Test interface", .format_device_name = format_test_interface_name, .tx_function = dummy_interface_tx, .admin_up_down_function = test_interface_admin_up_down, }; static u8 *hw_address; static int mfib_test_mk_intf (u32 ninterfaces) { clib_error_t * error = NULL; test_main_t *tm = &test_main; u8 byte; int res; u32 i; res = 0; ASSERT(ninterfaces <= ARRAY_LEN(tm->hw_if_indicies)); for (i=0; i<6; i++) { byte = 0xd0+i; vec_add1(hw_address, byte); } for (i = 0; i < ninterfaces; i++) { hw_address[5] = i; error = ethernet_register_interface(vnet_get_main(), test_interface_device_class.index, i /* instance */, hw_address, &tm->hw_if_indicies[i], /* flag change */ 0); MFIB_TEST((NULL == error), "ADD interface %d", i); error = vnet_hw_interface_set_flags(vnet_get_main(), tm->hw_if_indicies[i], VNET_HW_INTERFACE_FLAG_LINK_UP); tm->hw[i] = vnet_get_hw_interface(vnet_get_main(), tm->hw_if_indicies[i]); vec_validate (ip4_main.fib_index_by_sw_if_index, tm->hw[i]->sw_if_index); vec_validate (ip6_main.fib_index_by_sw_if_index, tm->hw[i]->sw_if_index); ip4_main.fib_index_by_sw_if_index[tm->hw[i]->sw_if_index] = 0; ip6_main.fib_index_by_sw_if_index[tm->hw[i]->sw_if_index] = 0; vec_validate (ip4_main.mfib_index_by_sw_if_index, tm->hw[i]->sw_if_index); vec_validate (ip6_main.mfib_index_by_sw_if_index, tm->hw[i]->sw_if_index); ip4_main.mfib_index_by_sw_if_index[tm->hw[i]->sw_if_index] = 0; ip6_main.mfib_index_by_sw_if_index[tm->hw[i]->sw_if_index] = 0; error = vnet_sw_interface_set_flags(vnet_get_main(), tm->hw[i]->sw_if_index, VNET_SW_INTERFACE_FLAG_ADMIN_UP); MFIB_TEST((NULL == error), "UP interface %d", i); } /* * re-eval after the inevitable realloc */ for (i = 0; i < ninterfaces; i++) { tm->hw[i] = vnet_get_hw_interface(vnet_get_main(), tm->hw_if_indicies[i]); } return (res); } #define MFIB_TEST_REP(_cond, _comment, _args...) \ { \ if (MFIB_TEST_I(_cond, _comment, ##_args)) { \ return (1); \ } \ } static int mfib_test_validate_rep_v (const replicate_t *rep, u16 n_buckets, va_list *ap) { const dpo_id_t *dpo; adj_index_t ai; dpo_type_t dt; int bucket; int res; res = 0; MFIB_TEST_REP((n_buckets == rep->rep_n_buckets), "n_buckets = %d", rep->rep_n_buckets); for (bucket = 0; bucket < n_buckets; bucket++) { dt = va_arg(*ap, int); // type promotion ai = va_arg(*ap, adj_index_t); dpo = replicate_get_bucket_i(rep, bucket); MFIB_TEST_REP((dt == dpo->dpoi_type), "bucket %d stacks on %U", bucket, format_dpo_type, dpo->dpoi_type); if (DPO_RECEIVE != dt) { MFIB_TEST_REP((ai == dpo->dpoi_index), "bucket %d [exp:%d] stacks on %U", bucket, ai, format_dpo_id, dpo, 0); } } return (res); } static int mfib_test_entry (fib_node_index_t fei, mfib_entry_flags_t eflags, int n_buckets, ...) { const mfib_entry_t *mfe; const replicate_t *rep; mfib_prefix_t pfx; va_list ap; int res; res = 0; mfe = mfib_entry_get(fei); mfib_entry_get_prefix(fei, &pfx); MFIB_TEST_REP((eflags == mfe->mfe_flags), "%U has %U expect %U", format_mfib_prefix, &pfx, format_mfib_entry_flags, mfe->mfe_flags, format_mfib_entry_flags, eflags); if (0 == n_buckets) { MFIB_TEST_REP((DPO_DROP == mfe->mfe_rep.dpoi_type), "%U links to %U", format_mfib_prefix, &pfx, format_dpo_id, &mfe->mfe_rep, 0); } else { dpo_id_t tmp = DPO_INVALID; mfib_entry_contribute_forwarding( fei, fib_forw_chain_type_from_fib_proto(pfx.fp_proto), &tmp); rep = replicate_get(tmp.dpoi_index); MFIB_TEST_REP((DPO_REPLICATE == tmp.dpoi_type), "%U links to %U", format_mfib_prefix, &pfx, format_dpo_type, tmp.dpoi_type); va_start(ap, n_buckets); res = mfib_test_validate_rep_v(rep, n_buckets, &ap); va_end(ap); dpo_reset(&tmp); } return (res); } static int mfib_test_entry_itf (fib_node_index_t fei, u32 sw_if_index, mfib_itf_flags_t flags) { const mfib_entry_t *mfe; const mfib_itf_t *mfi; mfib_prefix_t pfx; int res; res = 0; mfe = mfib_entry_get(fei); mfi = mfib_entry_get_itf(mfe, sw_if_index); mfib_entry_get_prefix(fei, &pfx); MFIB_TEST_REP((NULL != mfi), "%U has interface %d", format_mfib_prefix, &pfx, sw_if_index); MFIB_TEST_REP((flags == mfi->mfi_flags), "%U interface %d has flags %U expect %U", format_mfib_prefix, &pfx, sw_if_index, format_mfib_itf_flags, flags, format_mfib_itf_flags, mfi->mfi_flags); return (res); } static int mfib_test_entry_no_itf (fib_node_index_t fei, u32 sw_if_index) { const mfib_entry_t *mfe; const mfib_itf_t *mfi; mfib_prefix_t pfx; int res; res = 0; mfe = mfib_entry_get(fei); mfi = mfib_entry_get_itf(mfe, sw_if_index); mfib_entry_get_prefix(fei, &pfx); MFIB_TEST_REP((NULL == mfi), "%U has no interface %d", format_mfib_prefix, &pfx, sw_if_index); return (res); } static int mfib_test_i (fib_protocol_t PROTO, vnet_link_t LINKT, const mfib_prefix_t *pfx_no_forward, const mfib_prefix_t *pfx_s_g, const mfib_prefix_t *pfx_star_g_1, const mfib_prefix_t *pfx_star_g_2, const mfib_prefix_t *pfx_star_g_3, const mfib_prefix_t *pfx_star_g_slash_m, const fib_prefix_t *pfx_itf, const ip46_address_t *addr_nbr1, const ip46_address_t *addr_nbr2) { fib_node_index_t mfei, mfei_dflt, mfei_no_f, mfei_s_g, mfei_g_1, mfei_g_2, mfei_g_3, mfei_g_m; u32 fib_index, n_entries, n_itfs, n_reps, n_pls; fib_node_index_t ai_1, ai_2, ai_3, ai_nbr1, ai_nbr2; test_main_t *tm; int res; mfib_prefix_t all_1s; clib_memset(&all_1s, 0xfd, sizeof(all_1s)); res = 0; n_entries = pool_elts(mfib_entry_pool); n_itfs = pool_elts(mfib_itf_pool); n_reps = pool_elts(replicate_pool); n_pls = fib_path_list_pool_size(); tm = &test_main; ai_1 = adj_mcast_add_or_lock(PROTO, LINKT, tm->hw[1]->sw_if_index); ai_2 = adj_mcast_add_or_lock(PROTO, LINKT, tm->hw[2]->sw_if_index); ai_3 = adj_mcast_add_or_lock(PROTO, LINKT, tm->hw[3]->sw_if_index); ai_nbr1 = adj_nbr_add_or_lock(PROTO, LINKT, addr_nbr1, tm->hw[0]->sw_if_index); ai_nbr2 = adj_nbr_add_or_lock(PROTO, LINKT, addr_nbr2, tm->hw[0]->sw_if_index); MFIB_TEST(3 == adj_mcast_db_size(), "3 MCAST adjs"); /* Find or create FIB table 11 */ fib_index = mfib_table_find_or_create_and_lock(PROTO, 11, MFIB_SOURCE_API); fib_table_entry_update_one_path(0, pfx_itf, FIB_SOURCE_INTERFACE, (FIB_ENTRY_FLAG_CONNECTED | FIB_ENTRY_FLAG_ATTACHED), DPO_PROTO_IP4, NULL, tm->hw[0]->sw_if_index, ~0, // invalid fib index 1, // weight NULL, FIB_ROUTE_PATH_FLAG_NONE); mfib_prefix_t pfx_dft = { .fp_len = 0, .fp_proto = PROTO, }; mfei_dflt = mfib_table_lookup_exact_match(fib_index, &pfx_dft); MFIB_TEST(FIB_NODE_INDEX_INVALID != mfei_dflt, "(*,*) presnet"); MFIB_TEST(!mfib_test_entry(mfei_dflt, MFIB_ENTRY_FLAG_DROP, 0), "(*,*) no replcaitions"); MFIB_TEST(FIB_NODE_INDEX_INVALID != mfei_dflt, "(*,*) presnet"); MFIB_TEST(!mfib_test_entry(mfei_dflt, MFIB_ENTRY_FLAG_DROP, 0), "(*,*) no replcaitions"); fib_route_path_t path_via_if0 = { .frp_proto = fib_proto_to_dpo(PROTO), .frp_addr = zero_addr, .frp_sw_if_index = tm->hw[0]->sw_if_index, .frp_fib_index = ~0, .frp_weight = 0, .frp_flags = 0, }; mfib_table_entry_path_update(fib_index, pfx_no_forward, MFIB_SOURCE_API, &path_via_if0, MFIB_ITF_FLAG_ACCEPT); mfei_no_f = mfib_table_lookup_exact_match(fib_index, pfx_no_forward); MFIB_TEST(!mfib_test_entry(mfei_no_f, MFIB_ENTRY_FLAG_NONE, 0), "%U no replcaitions", format_mfib_prefix, pfx_no_forward); MFIB_TEST_NS(!mfib_test_entry_itf(mfei_no_f, tm->hw[0]->sw_if_index, MFIB_ITF_FLAG_ACCEPT)); fib_route_path_t path_via_if1 = { .frp_proto = fib_proto_to_dpo(PROTO), .frp_addr = zero_addr, .frp_sw_if_index = tm->hw[1]->sw_if_index, .frp_fib_index = ~0, .frp_weight = 0, .frp_flags = 0, }; fib_route_path_t path_via_if2 = { .frp_proto = fib_proto_to_dpo(PROTO), .frp_addr = zero_addr, .frp_sw_if_index = tm->hw[2]->sw_if_index, .frp_fib_index = ~0, .frp_weight = 0, .frp_flags = 0, }; fib_route_path_t path_via_if3 = { .frp_proto = fib_proto_to_dpo(PROTO), .frp_addr = zero_addr, .frp_sw_if_index = tm->hw[3]->sw_if_i
/*
 * Copyright (c) 2017 Intel 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 <vat/vat.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>

#include <vppinfra/error.h>
#include <pppoe/pppoe.h>
#include <vnet/format_fns.h>

#include <vnet/ip/ip_types_api.h>

#define __plugin_msg_base pppoe_test_main.msg_id_base
#include <vlibapi/vat_helper_macros.h>


uword unformat_ip46_address (unformat_input_t * input, va_list * args)
{
  ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
  ip46_type_t type = va_arg (*args, ip46_type_t);
  if ((type != IP46_TYPE_IP6) &&
      unformat(input, "%U", unformat_ip4_address, &ip46->ip4)) {
    ip46_address_mask_ip4(ip46);
    return 1;
  } else if ((type != IP46_TYPE_IP4) &&
      unformat(input, "%U", unformat_ip6_address, &ip46->ip6)) {
    return 1;
  }
  return 0;
}
uword unformat_ip46_prefix (unformat_input_t * input, va_list * args)
{
  ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
  u8 *len = va_arg (*args, u8 *);
  ip46_type_t type = va_arg (*args, ip46_type_t);

  u32 l;
  if ((type != IP46_TYPE_IP6) && unformat(input, "%U/%u", unformat_ip4_address, &ip46->ip4, &l)) {
    if (l > 32)
      return 0;
    *len = l + 96;
    ip46->pad[0] = ip46->pad[1] = ip46->pad[2] = 0;
  } else if ((type != IP46_TYPE_IP4) && unformat(input, "%U/%u", unformat_ip6_address, &ip46->ip6, &l)) {
    if (l > 128)
      return 0;
    *len = l;
  } else {
    return 0;
  }
  return 1;
}
/////////////////////////

#include <pppoe/pppoe.api_enum.h>
#include <pppoe/pppoe.api_types.h>

typedef struct {
    /* API message ID base */
    u16 msg_id_base;
    vat_main_t *vat_main;
} pppoe_test_main_t;

pppoe_test_main_t pppoe_test_main;

static void vl_api_pppoe_add_del_session_reply_t_handler
  (vl_api_pppoe_add_del_session_reply_t * mp)
{
  vat_main_t *vam = &vat_main;
  i32 retval = ntohl (mp->retval);
  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
    }
  else
    {
      vam->retval = retval;
      vam->sw_if_index = ntohl (mp->sw_if_index);
      vam->result_ready = 1;
    }
}

static int
api_pppoe_add_del_session (vat_main_t * vam)
{
  unformat_input_t *line_input = vam->input;
  vl_api_pppoe_add_del_session_t *mp;
  u16 session_id = 0;
  ip46_address_t client_ip;
  u8 is_add = 1;
  u8 client_ip_set = 0;
  u8 ipv4_set = 0;
  u8 ipv6_set = 0;
  u32 decap_vrf_id = 0;
  u8 client_mac[6] = { 0 };
  u8 client_mac_set = 0;
  int ret;

  /* Can't "universally zero init" (={0}) due to GCC bug 53119 */
  clib_memset (&client_ip, 0, sizeof client_ip);

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "del"))
	{
	  is_add = 0;
	}
      else if (unformat (line_input, "session_id %d", &session_id))
	;
      else if (unformat (line_input, "client-ip %U",
			 unformat_ip4_address, &client_ip.ip4))
	{
	  client_ip_set = 1;
	  ipv4_set = 1;
	}
      else if (unformat (line_input, "client-ip %U",
			 unformat_ip6_address, &client_ip.ip6))
	{
	  client_ip_set = 1;
	  ipv6_set = 1;
	}
      else if (unformat (line_input, "decap-vrf-id %d", &decap_vrf_id))
        ;
      else if (unformat (line_input, "client-mac %U", unformat_ethernet_address, client_mac))
	client_mac_set = 1;
      else
	{
	  return -99;
	}
    }

  if (client_ip_set == 0)
    {
      errmsg ("session client_ip address not specified");
      return -99;
    }

  if (ipv4_set && ipv6_set)
    {
      errmsg ("both IPv4 and IPv6 addresses specified");
      return -99;
    }

  if (client_mac_set == 0)
    {
      errmsg("session client mac not specified");
      return -99;
    }

  M (PPPOE_ADD_DEL_SESSION, mp);


  if (ipv6_set)
    {
      ip_address_encode(&client_ip, IP46_TYPE_IP6, &mp->client_ip);
    }
  else
    {
      ip_address_encode(&client_ip, IP46_TYPE_IP4, &mp->client_ip);
    }

  mp->decap_vrf_id = ntohl (decap_vrf_id);
  mp->session_id = ntohl (session_id);
  mp->is_add = is_add;
  memcpy (mp->client_mac, client_mac, 6);

  S (mp);
  W (ret);
  return ret;
}

static void vl_api_pppoe_session_details_t_handler
  (vl_api_pppoe_session_details_t * mp)
{
  vat_main_t *vam = &vat_main;
  ip46_address_t client_ip;
  ip_address_decode(&mp->client_ip, &client_ip);
  print (vam->ofp, "%11d%14d%24U%14d%14d%30U%30U",
       ntohl (mp->sw_if_index), ntohl (mp->session_id),
       format_ip46_address, &client_ip, IP46_TYPE_ANY,
       ntohl (mp->encap_if_index), ntohl (mp->decap_vrf_id),
       format_ethernet_address, mp->local_mac,
       format_ethernet_address, mp->client_mac);
}

static int
api_pppoe_session_dump (vat_main_t * vam)
{
  unformat_input_t *i = vam->input;
  vl_api_pppoe_session_dump_t *mp;
  u32 sw_if_index;
  u8 sw_if_index_set = 0;
  int ret;

  /* Parse args required to build the message */
  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (i, "sw_if_index %d", &sw_if_index))
      sw_if_index_set = 1;
      else
      break;
    }

  if (sw_if_index_set == 0)
    {
      sw_if_index = ~0;
    }

  if (!vam->json_output)
    {
      print (vam->ofp, "%11s%24s%14s%14s%14s",
	   "sw_if_index", "client_ip", "session_id",
	   "encap_if_index", "decap_fib_index",
	   "local-mac", "client-mac");
    }

  /* Get list of pppoe-session interfaces */
  M (PPPOE_SESSION_DUMP, mp);

  mp->sw_if_index = htonl (sw_if_index);

  S (mp);

  W (ret);
  return ret;
}

static void vl_api_pppoe_add_del_cp_reply_t_handler
  (vl_api_pppoe_add_del_session_reply_t * mp)
{
  vat_main_t *vam = &vat_main;
  i32 retval = ntohl (mp->retval);
  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
    }
  else
    {
      vam->retval = retval;
      vam->result_ready = 1;
    }
}

static int
api_pppoe_add_del_cp (vat_main_t * vam)
{
  unformat_input_t *line_input = vam->input;
  vl_api_pppoe_add_del_cp_t *mp;
  u8 is_add = 1;
  u32 sw_if_index = ~0;
  int ret;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "del"))
	{
	  is_add = 0;
	}
      else if (unformat (line_input, "cp-if-index %d", &sw_if_index))
	;
    }

  M (PPPOE_ADD_DEL_CP, mp);

  mp->is_add = is_add;
  mp->sw_if_index = sw_if_index;

  S (mp);
  W (ret);
  return ret;
}

#include <pppoe/pppoe.api_test.c>
P6, .fp_addr = { .ip6.as_u64[0] = clib_host_to_net_u64(0x2001000000000000), .ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000001), }, }; const ip46_address_t nbr1 = { .ip6.as_u64[0] = clib_host_to_net_u64(0x2001000000000000), .ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000002), }; const ip46_address_t nbr2 = { .ip6.as_u64[0] = clib_host_to_net_u64(0x2001000000000000), .ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000003), }; return (mfib_test_i(FIB_PROTOCOL_IP6, VNET_LINK_IP6, &pfx_ffd_s_12, &pfx_2001_1_c_ff_1, &pfx_ff_1, &pfx_ff_2, &pfx_ff_3, &pfx_ff, &pfx_itf, &nbr1, &nbr2)); } static clib_error_t * mfib_test (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd_arg) { int res = 0; res += mfib_test_mk_intf(4); res += mfib_test_v4(); if (res) { return clib_error_return(0, "MFIB Unit Test Failed"); } res += mfib_test_v6(); if (res) { return clib_error_return(0, "MFIB Unit Test Failed"); } else { return (NULL); } } VLIB_CLI_COMMAND (test_fib_command, static) = { .path = "test mfib", .short_help = "mfib unit tests - DO NOT RUN ON A LIVE SYSTEM", .function = mfib_test, }; clib_error_t * mfib_test_init (vlib_main_t *vm) { return 0; } VLIB_INIT_FUNCTION (mfib_test_init);