/* * 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 #define foreach_lb_error \ _(NONE, "no error") \ _(PROTO_NOT_SUPPORTED, "protocol not supported") typedef enum { #define _(sym,str) LB_ERROR_##sym, foreach_lb_error #undef _ LB_N_ERROR, } lb_error_t; static char *lb_error_strings[] = { #define _(sym,string) string, foreach_lb_error #undef _ }; typedef struct { u32 vip_index; u32 as_index; } lb_trace_t; typedef struct { u32 vip_index; u32 node_port; } lb_nodeport_trace_t; typedef struct { u32 vip_index; u32 as_index; u32 rx_sw_if_index; u32 next_index; } lb_nat_trace_t; u8 * format_lb_trace (u8 * s, va_list * args) { lb_main_t *lbm = &lb_main; CLIB_UNUSED(vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED(vlib_node_t * node) = va_arg (*args, vlib_node_t *); lb_trace_t *t = va_arg (*args, lb_trace_t *); if (pool_is_free_index(lbm->vips, t->vip_index)) { s = format (s, "lb vip[%d]: This VIP was freed since capture\n"); } else { s = format (s, "lb vip[%d]: %U\n", t->vip_index, format_lb_vip, &lbm->vips[t->vip_index]); } if (pool_is_free_index(lbm->ass, t->as_index)) { s = format (s, "lb as[%d]: This AS was freed since capture\n"); } else { s = format (s, "lb as[%d]: %U\n", t->as_index, format_lb_as, &lbm->ass[t->as_index]); } return s; } u8 * format_lb_nat_trace (u8 * s, va_list * args) { lb_main_t *lbm = &lb_main; CLIB_UNUSED(vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED(vlib_node_t * node) = va_arg (*args, vlib_node_t *); lb_nat_trace_t *t = va_arg (*args, lb_nat_trace_t *); if (pool_is_free_index(lbm->vips, t->vip_index)) { s = format (s, "lb vip[%d]: This VIP was freed since capture\n"); } else { s = format (s, "lb vip[%d]: %U\n", t->vip_index, format_lb_vip, &lbm->vips[t->vip_index]); } if (pool_is_free_index(lbm->ass, t->as_index)) { s = format (s, "lb as[%d]: This AS was freed since capture\n"); } else { s = format (s, "lb as[%d]: %U\n", t->as_index, format_lb_as, &lbm->ass[t->as_index]); } s = format (s, "lb nat: rx_sw_if_index = %d, next_index = %d", t->rx_sw_if_index, t->next_index); return s; } lb_hash_t * lb_get_sticky_table (u32 thread_index) { lb_main_t *lbm = &lb_main; lb_hash_t *sticky_ht = lbm->per_cpu[thread_index].sticky_ht; //Check if size changed if (PREDICT_FALSE( sticky_ht && (lbm->per_cpu_sticky_buckets != lb_hash_nbuckets(sticky_ht)))) { //Dereference everything in there lb_hash_bucket_t *b; u32 i; lb_hash_foreach_entry(sticky_ht, b, i) { vlib_refcount_add (&lbm->as_refcount, thread_index, b->value[i], -1); vlib_refcount_add (&lbm->as_refcount, thread_index, 0, 1); } lb_hash_free (sticky_ht); sticky_ht = NULL; } //Create if necessary if (PREDICT_FALSE(sticky_ht == NULL)) { lbm->per_cpu[thread_index].sticky_ht = lb_hash_alloc ( lbm->per_cpu_sticky_buckets, lbm->flow_timeout); sticky_ht = lbm->per_cpu[thread_index].sticky_ht; clib_warning("Regenerated sticky table %p", sticky_ht); } ASSERT(sticky_ht); //Update timeout sticky_ht->timeout = lbm->flow_timeout; return sticky_ht; } u64 lb_node_get_other_ports4 (ip4_header_t *ip40) { return 0; } u64 lb_node_get_other_ports6 (ip6_header_t *ip60) { return 0; } static_always_inline void lb_node_get_hash (lb_main_t *lbm, vlib_buffer_t *p, u8 is_input_v4, u32 *hash, u32 *vip_idx, u8 per_port_vip) { vip_port_key_t key; clib_bihash_kv_8_8_t kv, value; /* For vip case, retrieve vip index for ip lookup */ *vip_idx = vnet_buffer (p)->ip.adj_index[VLIB_TX]; if (per_port_vip) { /* For per-port-vip case, ip lookup stores placeholder index */ key.vip_prefix_index = *vip_idx; } if (is_input_v4) { ip4_header_t *ip40; u64 ports; ip40 = vlib_buffer_get_current (p); if (PREDICT_TRUE( ip40->protocol == IP_PROTOCOL_TCP || ip40->protocol == IP_PROTOCOL_UDP)) ports = ((u64) ((udp_header_t *) (ip40 + 1))->src_port << 16) | ((u64) ((udp_header_t *) (ip40 + 1))->dst_port); else ports = lb_node_get_other_ports4 (ip40); *hash = lb_hash_hash (*((u64 *) &ip40->address_pair), ports, 0, 0, 0); if (per_port_vip) { key.protocol = ip40->protocol; key.port = (u16)(ports & 0xFFFF); } } else { ip6_header_t *ip60; ip60 = vlib_buffer_get_current (p); u64 ports; if (PREDICT_TRUE( ip60->protocol == IP_PROTOCOL_TCP || ip60->protocol == IP_PROTOCOL_UDP)) ports = ((u64) ((udp_header_t *) (ip60 + 1))->src_port << 16) | ((u64) ((udp_header_t *) (ip60 + 1))->dst_port); else ports = lb_node_get_other_ports6 (ip60); *hash = lb_hash_hash (ip60->src_address.as_u64[0], ip60->src_address.as_u64[1], ip60->dst_address.as_u64[0], ip60->dst_address.as_u64[1], ports); if (per_port_vip) { key.protocol = ip60->protocol; key.port = (u16)(ports & 0xFFFF); } } /* For per-port-vip case, retrieve vip index for vip_port_filter table */ if (per_port_vip) { kv.key = key.as_u64; if (clib_bihash_search_8_8(&lbm->vip_index_per_port, &kv, &value) < 0) { /* return default vip */ *vip_idx = 0; return; } *vip_idx = value.value; } } static_always_inline uword lb_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame, u8 is_input_v4, //Compile-time parameter stating that is input is v4 (or v6) lb_encap_type_t encap_type, //Compile-time parameter is GRE4/GRE6/L3DSR/NAT4/NAT6 u8 per_port_vip) //Compile-time parameter stating that is per_port_vip or not { lb_main_t *lbm = &lb_main; u32 n_left_from, *from, next_index, *to_next, n_left_to_next; u32 thread_index = vm->thread_index; u32 lb_time = lb_hash_time_now (vm); lb_hash_t *sticky_ht = lb_get_sticky_table (thread_index); from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; next_index = node->cached_next_index; u32 nexthash0 = 0; u32 next_vip_idx0 = ~0; if (PREDICT_TRUE(n_left_from > 0)) { vlib_buffer_t *p0 = vlib_get_buffer (vm, from[0]); lb_node_get_hash (lbm, p0, is_input_v4, &nexthash0, &next_vip_idx0, per_port_vip); } while (n_left_from > 0) { vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next); while (n_left_from > 0 && n_left_to_next > 0) { u32 pi0; vlib_buffer_t *p0; lb_vip_t *vip0; u32 asindex0 = 0; u16 len0; u32 available_index0; u8 counter = 0; u32 hash0 = nexthash0; u32 vip_index0 = next_vip_idx0; u32 next0; if (PREDICT_TRUE(n_left_from > 1)) { vlib_buffer_t *p1 = vlib_get_buffer (vm, from[1]); //Compute next hash and prefetch bucket lb_node_get_hash (lbm, p1, is_input_v4, &nexthash0, &next_vip_idx0, per_port_vip); lb_hash_prefetch_bucket (sticky_ht, nexthash0); //Prefetch for encap, next CLIB_PREFETCH(vlib_buffer_get_current (p1) - 64, 64, STORE); } if (PREDICT_TRUE(n_left_from > 2)) { vlib_buffer_t *p2; p2 = vlib_get_buffer (vm, from[2]); /* prefetch packet header and data */ vlib_prefetch_buffer_header(p2, STORE); CLIB_PREFETCH(vlib_buffer_get_current (p2), 64, STORE); } pi0 = to_next[0] = from[0]; from += 1; n_left_from -= 1; to_next += 1; n_left_to_next -= 1; p0 = vlib_get_buffer (vm, pi0); vip0 = pool_elt_at_index(lbm->vips, vip_index0); if (is_input_v4) { ip4_header_t *ip40; ip40 = vlib_buffer_get_current (p0); len0 = clib_net_to_host_u16 (ip40->length); } else { ip6_header_t *ip60; ip60 = vlib_buffer_get_current (p0); len0 = clib_net_to_host_u16 (ip60->payload_length) + sizeof(ip6_header_t); } lb_hash_get (sticky_ht, hash0, vip_index0, lb_time, &available_index0, &asindex0); if (PREDICT_TRUE(asindex0 != 0)) { //Found an existing entry counter = LB_VIP_COUNTER_NEXT_PACKET; } else if (PREDICT_TRUE(available_index0 != ~0)) { //There is an available slot for a new flow asindex0 = vip0->new_flow_table[hash0 & vip0->new_flow_table_mask].as_index;
/*
 *------------------------------------------------------------------
 * Copyright (c) 2017 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 <igmp/igmp_ssm_range.h>

typedef struct igmp_group_prefix_t
{
  fib_prefix_t igp_prefix;
  igmp_group_prefix_type_t igp_type;
} igmp_group_prefix_t;

static igmp_group_prefix_t *igmp_group_prefixs;

u8 *
format_igmp_group_prefix_type (u8 * s, va_list * args)
{
  igmp_group_prefix_type_t type = va_arg (*args, int);

  switch (type)
    {
#define _(n,f) case IGMP_GROUP_PREFIX_TYPE_##f: return (format (s, "%s", #f));
      foreach_igmp_group_prefix_type
#undef _
    }
  return format (s, "unknown:%d", type);
}

static int
igmp_group_prefix_cmp (const igmp_group_prefix_t * gp1,
		       const fib_prefix_t * p)
{
  return (fib_prefix_cmp (&gp1->igp_prefix, p));
}

void
igmp_group_prefix_set (const fib_prefix_t * pfx,
		       igmp_group_prefix_type_t type)
{
  u32 pos;

  pos =
    vec_search_with_function (igmp_group_prefixs, pfx, igmp_group_prefix_cmp);

  if ((~0 == pos) && (IGMP_GROUP_PREFIX_TYPE_SSM == type))
    {
      igmp_group_prefix_t gp = {
	.igp_prefix = *pfx,
	.igp_type = type,
      };

      vec_add1 (igmp_group_prefixs, gp);
    }
  if ((~0 != pos) && (IGMP_GROUP_PREFIX_TYPE_ASM == type))
    {
      vec_del1 (igmp_group_prefixs, pos);
    }
}

static void
igmp_ssm_range_populate (void)
{
  igmp_group_prefix_t *ssm_default;

  vec_add2 (igmp_group_prefixs, ssm_default, 1);

  ssm_default->igp_prefix.fp_addr.ip4.as_u32 = IGMP_SSM_DEFAULT;
  ssm_default->igp_prefix.fp_proto = FIB_PROTOCOL_IP4;
  ssm_default->igp_prefix.fp_len = 8;
  ssm_default->igp_type = IGMP_GROUP_PREFIX_TYPE_SSM;
}

igmp_group_prefix_type_t
igmp_group_prefix_get_type (const ip46_address_t * gaddr)
{
  igmp_group_prefix_t *igp;

  vec_foreach (igp, igmp_group_prefixs)
  {
    if (ip4_destination_matches_route (&ip4_main,
				       &gaddr->ip4,
				       &igp->igp_prefix.fp_addr.ip4,
				       igp->igp_prefix.fp_len))
      return (IGMP_GROUP_PREFIX_TYPE_SSM);
  }

  return (IGMP_GROUP_PREFIX_TYPE_ASM);
}

void
igmp_ssm_range_walk (igmp_ssm_range_walk_t fn, void *ctx)
{
  igmp_group_prefix_t *igp;

  vec_foreach (igp, igmp_group_prefixs)
  {
    if (WALK_STOP == fn (&igp->igp_prefix, igp->igp_type, ctx))
      break;
  }
}

static clib_error_t *
igmp_ssm_range_show (vlib_main_t * vm,
		     unformat_input_t * input, vlib_cli_command_t * cmd)
{
  igmp_group_prefix_t *igp;

  vec_foreach (igp, igmp_group_prefixs)
  {
    vlib_cli_output (vm, "%U => %U",
		     format_fib_prefix, &igp->igp_prefix,
		     format_igmp_group_prefix_type, igp->igp_type);
  }
  return (NULL);
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (igmp_show_timers_command, static) = {
  .path = "show igmp ssm-ranges",
  .short_help = "show igmp ssm-ranges",
  .function = igmp_ssm_range_show,
};
/* *INDENT-ON* */

static clib_error_t *
igmp_ssm_range_init (vlib_main_t * vm)
{
  igmp_ssm_range_populate ();

  IGMP_DBG ("ssm-range-initialized");

  return (0);
}

/* *INDENT-OFF* */
VLIB_INIT_FUNCTION (igmp_ssm_range_init) =
{
  .runs_after = VLIB_INITS("igmp_init"),
};
/* *INDENT-ON* */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
{ [LB_NEXT_DROP] = "error-drop" }, }; VLIB_REGISTER_NODE (lb6_gre4_node) = { .function = lb6_gre4_node_fn, .name = "lb6-gre4", .vector_size = sizeof(u32), .format_trace = format_lb_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB_N_NEXT, .next_nodes = { [LB_NEXT_DROP] = "error-drop" }, }; VLIB_REGISTER_NODE (lb4_gre6_node) = { .function = lb4_gre6_node_fn, .name = "lb4-gre6", .vector_size = sizeof(u32), .format_trace = format_lb_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB_N_NEXT, .next_nodes = { [LB_NEXT_DROP] = "error-drop" }, }; VLIB_REGISTER_NODE (lb4_gre4_node) = { .function = lb4_gre4_node_fn, .name = "lb4-gre4", .vector_size = sizeof(u32), .format_trace = format_lb_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB_N_NEXT, .next_nodes = { [LB_NEXT_DROP] = "error-drop" }, }; VLIB_REGISTER_NODE (lb6_gre6_port_node) = { .function = lb6_gre6_port_node_fn, .name = "lb6-gre6-port", .vector_size = sizeof(u32), .format_trace = format_lb_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB_N_NEXT, .next_nodes = { [LB_NEXT_DROP] = "error-drop" }, }; VLIB_REGISTER_NODE (lb6_gre4_port_node) = { .function = lb6_gre4_port_node_fn, .name = "lb6-gre4-port", .vector_size = sizeof(u32), .format_trace = format_lb_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB_N_NEXT, .next_nodes = { [LB_NEXT_DROP] = "error-drop" }, }; VLIB_REGISTER_NODE (lb4_gre6_port_node) = { .function = lb4_gre6_port_node_fn, .name = "lb4-gre6-port", .vector_size = sizeof(u32), .format_trace = format_lb_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB_N_NEXT, .next_nodes = { [LB_NEXT_DROP] = "error-drop" }, }; VLIB_REGISTER_NODE (lb4_gre4_port_node) = { .function = lb4_gre4_port_node_fn, .name = "lb4-gre4-port", .vector_size = sizeof(u32), .format_trace = format_lb_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB_N_NEXT, .next_nodes = { [LB_NEXT_DROP] = "error-drop" }, }; VLIB_REGISTER_NODE (lb4_l3dsr_port_node) = { .function = lb4_l3dsr_port_node_fn, .name = "lb4-l3dsr-port", .vector_size = sizeof(u32), .format_trace = format_lb_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB_N_NEXT, .next_nodes = { [LB_NEXT_DROP] = "error-drop" }, }; VLIB_REGISTER_NODE (lb4_l3dsr_node) = { .function = lb4_l3dsr_node_fn, .name = "lb4-l3dsr", .vector_size = sizeof(u32), .format_trace = format_lb_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB_N_NEXT, .next_nodes = { [LB_NEXT_DROP] = "error-drop" }, }; VLIB_REGISTER_NODE (lb6_nat6_port_node) = { .function = lb6_nat6_port_node_fn, .name = "lb6-nat6-port", .vector_size = sizeof(u32), .format_trace = format_lb_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB_N_NEXT, .next_nodes = { [LB_NEXT_DROP] = "error-drop" }, }; VLIB_REGISTER_NODE (lb4_nat4_port_node) = { .function = lb4_nat4_port_node_fn, .name = "lb4-nat4-port", .vector_size = sizeof(u32), .format_trace = format_lb_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB_N_NEXT, .next_nodes = { [LB_NEXT_DROP] = "error-drop" }, }; static uword lb4_nodeport_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { return lb_nodeport_node_fn (vm, node, frame, 1); } static uword lb6_nodeport_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { return lb_nodeport_node_fn (vm, node, frame, 0); } VLIB_REGISTER_NODE (lb4_nodeport_node) = { .function = lb4_nodeport_node_fn, .name = "lb4-nodeport", .vector_size = sizeof(u32), .format_trace = format_nodeport_lb_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB4_NODEPORT_N_NEXT, .next_nodes = { [LB4_NODEPORT_NEXT_IP4_NAT4] = "lb4-nat4-port", [LB4_NODEPORT_NEXT_DROP] = "error-drop", }, }; VLIB_REGISTER_NODE (lb6_nodeport_node) = { .function = lb6_nodeport_node_fn, .name = "lb6-nodeport", .vector_size = sizeof(u32), .format_trace = format_nodeport_lb_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB6_NODEPORT_N_NEXT, .next_nodes = { [LB6_NODEPORT_NEXT_IP6_NAT6] = "lb6-nat6-port", [LB6_NODEPORT_NEXT_DROP] = "error-drop", }, }; VNET_FEATURE_INIT (lb_nat4_in2out_node_fn, static) = { .arc_name = "ip4-unicast", .node_name = "lb-nat4-in2out", .runs_before = VNET_FEATURES("ip4-lookup"), }; VLIB_REGISTER_NODE (lb_nat4_in2out_node) = { .function = lb_nat4_in2out_node_fn, .name = "lb-nat4-in2out", .vector_size = sizeof(u32), .format_trace = format_lb_nat_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB_NAT4_IN2OUT_N_NEXT, .next_nodes = { [LB_NAT4_IN2OUT_NEXT_DROP] = "error-drop", [LB_NAT4_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", }, }; VNET_FEATURE_INIT (lb_nat6_in2out_node_fn, static) = { .arc_name = "ip6-unicast", .node_name = "lb-nat6-in2out", .runs_before = VNET_FEATURES("ip6-lookup"), }; VLIB_REGISTER_NODE (lb_nat6_in2out_node) = { .function = lb_nat6_in2out_node_fn, .name = "lb-nat6-in2out", .vector_size = sizeof(u32), .format_trace = format_lb_nat_trace, .n_errors = LB_N_ERROR, .error_strings = lb_error_strings, .n_next_nodes = LB_NAT6_IN2OUT_N_NEXT, .next_nodes = { [LB_NAT6_IN2OUT_NEXT_DROP] = "error-drop", [LB_NAT6_IN2OUT_NEXT_LOOKUP] = "ip6-lookup", }, };