/* * ethernet/arp.c: IP v4 ARP node * * Copyright (c) 2010 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 /** * @file * @brief IPv4 ARP. * * This file contains code to manage the IPv4 ARP tables (IP Address * to MAC Address lookup). */ /** * @brief Per-interface ARP configuration and state */ typedef struct ethernet_arp_interface_t_ { /** * Is ARP enabled on this interface */ u32 enabled; } ethernet_arp_interface_t; typedef struct { /* Hash tables mapping name to opcode. */ uword *opcode_by_name; /** Per interface state */ ethernet_arp_interface_t *ethernet_arp_by_sw_if_index; /* ARP feature arc index */ u8 feature_arc_index; } ethernet_arp_main_t; static ethernet_arp_main_t ethernet_arp_main; static const u8 vrrp_prefix[] = { 0x00, 0x00, 0x5E, 0x00, 0x01 }; static uword unformat_ethernet_arp_opcode_host_byte_order (unformat_input_t * input, va_list * args) { int *result = va_arg (*args, int *); ethernet_arp_main_t *am = ðernet_arp_main; int x, i; /* Numeric opcode. */ if (unformat (input, "0x%x", &x) || unformat (input, "%d", &x)) { if (x >= (1 << 16)) return 0; *result = x; return 1; } /* Named type. */ if (unformat_user (input, unformat_vlib_number_by_name, am->opcode_by_name, &i)) { *result = i; return 1; } return 0; } static uword unformat_ethernet_arp_opcode_net_byte_order (unformat_input_t * input, va_list * args) { int *result = va_arg (*args, int *); if (!unformat_user (input, unformat_ethernet_arp_opcode_host_byte_order, result)) return 0; *result = clib_host_to_net_u16 ((u16) * result); return 1; } typedef struct { u8 packet_data[64]; } ethernet_arp_input_trace_t; static u8 * format_ethernet_arp_input_trace (u8 * s, va_list * va) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *); ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *); s = format (s, "%U", format_ethernet_arp_header, t->packet_data, sizeof (t->packet_data)); return s; } static int arp_is_enabled (ethernet_arp_main_t * am, u32 sw_if_index) { if (vec_len (am->ethernet_arp_by_sw_if_index) <= sw_if_index) return 0; return (am->ethernet_arp_by_sw_if_index[sw_if_index].enabled); } static void arp_enable (ethernet_arp_main_t * am, u32 sw_if_index) { if (arp_is_enabled (am, sw_if_index)) return; vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index); am->ethernet_arp_by_sw_if_index[sw_if_index].enabled = 1; vnet_feature_enable_disable ("arp", "arp-reply", sw_if_index, 1, NULL, 0); vnet_feature_enable_disable ("arp", "arp-disabled", sw_if_index, 0, NULL, 0); } static void arp_disable (ethernet_arp_main_t * am, u32 sw_if_index) { if (!arp_is_enabled (am, sw_if_index)) return; vnet_feature_enable_disable ("arp", "arp-disabled", sw_if_index, 1, NULL, 0); vnet_feature_enable_disable ("arp", "arp-reply", sw_if_index, 0, NULL, 0); am->ethernet_arp_by_sw_if_index[sw_if_index].enabled = 0; } static int arp_unnumbered (vlib_buffer_t * p0, u32 input_sw_if_index, u32 conn_sw_if_index) { vnet_main_t *vnm = vnet_get_main (); vnet_interface_main_t *vim = &vnm->interface_main; vnet_sw_interface_t *si; /* verify that the input interface is unnumbered to the connected. * the connected interface is the interface on which the subnet is * configured */ si = &vim->sw_interfaces[input_sw_if_index]; if (!(si->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED && (si->unnumbered_sw_if_index == conn_sw_if_index))) { /* the input interface is not unnumbered to the interface on which * the sub-net is configured that covers the ARP request. * So this is not the case for unnumbered.. */ return 0; } return !0; } always_inline u32 arp_learn (u32 sw_if_index, const ethernet_arp_ip4_over_ethernet_address_t * addr) { /* *INDENT-OFF* */ ip_neighbor_learn_t l = { .ip = { .ip.ip4 = addr->ip4, .version = AF_IP4, }, .mac = addr->mac, .sw_if_index = sw_if_index, }; /* *INDENT-ON* */ ip_neighbor_learn_dp (&l); return (ETHERNET_ARP_ERROR_l3_src_address_learned); } typedef enum arp_input_next_t_ { ARP_INPUT_NEXT_DROP, ARP_INPUT_NEXT_DISABLED, ARP_INPUT_N_NEXT, } arp_input_next_t; static uword arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { u32 n_left_from, next_index, *from, *to_next, n_left_to_next; ethernet_arp_main_t *am = ðernet_arp_main; from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; next_index = node->cached_next_index; if (node->flags & VLIB_NODE_FLAG_TRACE) vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors, /* stride */ 1, sizeof (ethernet_arp_input_trace_t)); 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) { const ethernet_arp_header_t *arp0; arp_input_next_t next0; vlib_buffer_t *p0; u32 pi0, error0; pi0 = to_next[0] = from[0]; from += 1; to_next += 1; n_left_from -= 1; n_left_to_next -= 1; p0 = vlib_get_buffer (vm, pi0); arp0 = vlib_buffer_get_current (p0); error0 = ETHERNET_ARP_ERROR_replies_sent; next0 = ARP_INPUT_NEXT_DROP; error0 = (arp0->l2_type != clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet) ? ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0); error0 = (arp0->l3_type != clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ? ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0); error0 = (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ? ETHERNET_ARP_ERROR_l3_dst_address_unset : error0); if (ETHERNET_ARP_ERROR_replies_sent == error0) { next0 = ARP_INPUT_NEXT_DISABLED; vnet_feature_arc_start (am->feature_arc_index, vnet_buffer (p0)->sw_if_index[VLIB_RX], &next0, p0); } else p0->error = node->errors[error0]; vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next, pi0, next0); } vlib_put_next_frame (vm, node, next_index, n_left_to_next); } return frame->n_vectors; } typedef enum arp_disabled_next_t_ { ARP_DISABLED_NEXT_DROP, ARP_DISABLED_N_NEXT, } arp_disabled_next_t; #define foreach_arp_disabled_error \ _ (DISABLED, "ARP Disabled on this interface") \ typedef enum { #define _(sym,string) ARP_DISABLED_ERROR_##sym, foreach_arp_disabled_error #undef _ ARP_DISABLED_N_ERROR, } arp_disabled_error_t; static char *arp_disabled_error_strings[] = { #define _(sym,string) string, foreach_arp_disabled_error #undef _ }; static uword arp_disabled (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { u32 n_left_from, next_index, *from, *to_next, n_left_to_next; from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; next_index = node->cached_next_index; if (node->flags & VLIB_NODE_FLAG_TRACE) vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors, /* stride */ 1, sizeof (ethernet_arp_input_trace_t)); 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) { arp_disabled_next_t next0 = ARP_DISABLED_NEXT_DROP; vlib_buffer_t *p0; u32 pi0, error0; next0 = ARP_DISABLED_NEXT_DROP; error0 = ARP_DISABLED_ERROR_DISABLED; pi0 = to_next[0] = from[0]; from += 1; to_next += 1; n_left_from -= 1; n_left_to_next -= 1; p0 = vlib_get_buffer (vm, pi0); p0->error = node->errors[error0]; vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next, pi0, next0); } vlib_put_next_frame (vm, node, next_index, n_left_to_next); } return frame->n_vectors; } enum arp_dst_fib_type { ARP_DST_FIB_NONE, ARP_DST_FIB_ADJ, ARP_DST_FIB_CONN }; /* * we're looking for FIB sources that indicate the destination * is attached. There may be interposed DPO prior to the one * we are looking for */ static enum arp_dst_fib_type arp_dst_fib_check (const fib_node_index_t fei, fib_entry_flag_t * flags) { const fib_entry_t *entry = fib_entry_get (fei); const fib_entry_src_t *entry_src; fib_source_t src; /* *INDENT-OFF* */ FOR_EACH_SRC_ADDED(entry, entry_src, src, ({ *flags = fib_entry_get_flags_for_source (fei, src); if (fib_entry_is_sourced (fei, FIB_SOURCE_ADJ)) return ARP_DST_FIB_ADJ; else if (FIB_ENTRY_FLAG_CONNECTED & *flags) return ARP_DST_FIB_CONN; })) /* *INDENT-ON* */ return ARP_DST_FIB_NONE; } static uword arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { vnet_main_t *vnm = vnet_get_main (); u32 n_left_from, next_index, *from, *to_next; u32 n_replies_sent = 0; from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; next_index = node->cached_next_index; if (node->flags & VLIB_NODE_FLAG_TRACE) vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors, /* stride */ 1, sizeof (ethernet_arp_input_trace_t)); while (n_left_from > 0) { u32 n_left_to_next; vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); while (n_left_from > 0 && n_left_to_next > 0) { vlib_buffer_t *p0; ethernet_arp_header_t *arp0; ethernet_header_t *eth_rx; const ip4_address_t *if_addr0; u32 pi0, error0, next0, sw_if_index0, conn_sw_if_index0, fib_index0; u8 dst_is_local0, is_vrrp_reply0; fib_node_index_t dst_fei, src_fei; const fib_prefix_t *pfx0; fib_entry_flag_t src_flags, dst_flags; pi0 = from[0]; to_next[0] = pi0; from += 1; to_next += 1; n_left_from -= 1; n_left_to_next -= 1; p0 = vlib_get_buffer (vm, pi0); arp0 = vlib_buffer_get_current (p0);
/*
 * 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.
 */

#include <vppinfra/dlist.h>

typedef struct
{
  dlist_elt_t *test_pool;
  u32 head_index;
} test_main_t;

test_main_t test_main;

int
test_dlist_main (unformat_input_t * input)
{
  test_main_t *tm = &test_main;
  dlist_elt_t *head, *elt;
  u32 elt_index, head_index;
  u32 value;
  int i;

  pool_get (tm->test_pool, head);
  head_index = head - tm->test_pool;
  clib_dlist_init (tm->test_pool, head - tm->test_pool);

  for (i = 1; i <= 3; i++)
    {
      pool_get (tm->test_pool, elt);
      elt_index = elt - tm->test_pool;

      clib_dlist_init (tm->test_pool, elt_index);
      elt->value = i;
      clib_dlist_addtail (tm->test_pool, head_index, elt_index);
    }

  head = pool_elt_at_index (tm->test_pool, head_index);

  fformat (stdout, "Dump forward links\n");
  elt_index = head->next;
  i = 1;
  value = 0;
  while (value != ~0)
    {
      elt = pool_elt_at_index (tm->test_pool, elt_index);
      fformat (stdout, "elt %d value %d\n", i++, elt->value);
      elt_index = elt->next;
      value = elt->value;
    }

  fformat (stdout, "Dump reverse links\n");
  elt_index = head->prev;
  i = 1;
  value = 0;
  while (value != ~0)
    {
      elt = pool_elt_at_index (tm->test_pool, elt_index);
      fformat (stdout, "elt %d value %d\n", i++, elt->value);
      elt_index = elt->prev;
      value = elt->value;
    }

  fformat (stdout, "remove first element\n");

  elt_index = clib_dlist_remove_head (tm->test_pool, head_index);
  elt = pool_elt_at_index (tm->test_pool, elt_index);