blob: 184edbf8be830a1d7e8a742c263149a179cd4b82 [file] [log] [blame]
/*
* 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 <vnet/arp/arp.h>
#include <vnet/arp/arp_packet.h>
#include <vnet/fib/ip4_fib.h>
typedef struct
{
ip4_address_t lo_addr;
ip4_address_t hi_addr;
u32 fib_index;
} ethernet_proxy_arp_t;
typedef struct arp_proxy_main_t_
{
/** Per interface state */
bool *enabled_by_sw_if_index;
/* Proxy arp vector */
ethernet_proxy_arp_t *proxy_arps;
} arp_proxy_main_t;
arp_proxy_main_t arp_proxy_main;
void
proxy_arp_walk (proxy_arp_walk_t cb, void *data)
{
arp_proxy_main_t *am = &arp_proxy_main;
ethernet_proxy_arp_t *pa;
vec_foreach (pa, am->proxy_arps)
{
if (!cb (&pa->lo_addr, &pa->hi_addr, pa->fib_index, data))
break;
}
}
int
arp_proxy_disable (u32 sw_if_index)
{
arp_proxy_main_t *am = &arp_proxy_main;
vec_validate (am->enabled_by_sw_if_index, sw_if_index);
if (am->enabled_by_sw_if_index[sw_if_index])
{
vnet_feature_enable_disable ("arp", "arp-proxy",
sw_if_index, 0, NULL, 0);
}
am->enabled_by_sw_if_index[sw_if_index] = false;
return (0);
}
int
arp_proxy_enable (u32 sw_if_index)
{
arp_proxy_main_t *am = &arp_proxy_main;
vec_validate (am->enabled_by_sw_if_index, sw_if_index);
if (!am->enabled_by_sw_if_index[sw_if_index])
{
vnet_feature_enable_disable ("arp", "arp-proxy",
sw_if_index, 1, NULL, 0);
}
am->enabled_by_sw_if_index[sw_if_index] = true;
return (0);
}
static int
vnet_proxy_arp_add_del (const ip4_address_t * lo_addr,
const ip4_address_t * hi_addr,
u32 fib_index, int is_del)
{
arp_proxy_main_t *am = &arp_proxy_main;
ethernet_proxy_arp_t *pa;
u32 found_at_index = ~0;
vec_foreach (pa, am->proxy_arps)
{
if (pa->lo_addr.as_u32 == lo_addr->as_u32 &&
pa->hi_addr.as_u32 == hi_addr->as_u32 && pa->fib_index == fib_index)
{
found_at_index = pa - am->proxy_arps;
break;
}
}
if (found_at_index != ~0)
{
/* Delete, otherwise it's already in the table */
if (is_del)
vec_delete (am->proxy_arps, 1, found_at_index);
return 0;
}
/* delete, no such entry */
if (is_del)
return VNET_API_ERROR_NO_SUCH_ENTRY;
/* add, not in table */
vec_add2 (am->proxy_arps, pa, 1);
pa->lo_addr.as_u32 = lo_addr->as_u32;
pa->hi_addr.as_u32 = hi_addr->as_u32;
pa->fib_index = fib_index;
return 0;
}
int
arp_proxy_add (u32 fib_index,
const ip4_address_t * lo, const ip4_address_t * hi)
{
return (vnet_proxy_arp_add_del (lo, hi, fib_index, 0));
}
int
arp_proxy_del (u32 fib_index,
const ip4_address_t * lo, const ip4_address_t * hi)
{
return (vnet_proxy_arp_add_del (lo, hi, fib_index, 1));
}
void
proxy_arp_intfc_walk (proxy_arp_intf_walk_t cb, void *data)
{
arp_proxy_main_t *am = &arp_proxy_main;
bool *enabled;
vec_foreach (enabled, am->enabled_by_sw_if_index)
{
if (*enabled)
cb (enabled - am->enabled_by_sw_if_index, data);
}
}
static clib_error_t *
set_int_proxy_arp_command_fn (vlib_main_t * vm,
unformat_input_t * input,
vlib_cli_command_t * cmd)
{
vnet_main_t *vnm = vnet_get_main ();
u32 sw_if_index;
int enable = 0;
sw_if_index = ~0;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "%U", unformat_vnet_sw_interface,
vnm, &sw_if_index))
;
else if (unformat (input, "enable") || unformat (input, "on"))
enable = 1;
else if (unformat (input, "disable") || unformat (input, "off"))
enable = 0;
else
break;
}
if (~0 == sw_if_index)
return clib_error_return (0, "unknown input '%U'",
format_unformat_error, input);
if (enable)
arp_proxy_enable (sw_if_index);
else
arp_proxy_disable (sw_if_index);
return 0;
}
static clib_error_t *
set_arp_proxy (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
ip4_address_t start = {.as_u32 = ~0 }, end =
{
.as_u32 = ~0};
u32 fib_index, table_id = 0;
int add = 1;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "table-id %d", &table_id))
;
else if (unformat (input, "start %U", unformat_ip4_address, &start))
;
else if (unformat (input, "end %U", unformat_ip4_address, &end))
;
else if (unformat (input, "del") || unformat (input, "delete"))
add = 0;
else
break;
}
fib_index = fib_table_find (FIB_PROTOCOL_IP4, table_id);
if (~0 == fib_index)
return (clib_error_return (0, "no such table: %d", table_id));
if (add)
arp_proxy_add (fib_index, &start, &end);
else
arp_proxy_del (fib_index, &start, &end);
return (NULL);
}
/* *INDENT-OFF* */
/*?
* Enable proxy-arp on an interface. The vpp stack will answer ARP
* requests for the indicated address range. Multiple proxy-arp
* ranges may be provisioned.
*
* @note Proxy ARP as a technology is infamous for blackholing traffic.
* Also, the underlying implementation has not been performance-tuned.
* Avoid creating an unnecessarily large set of ranges.
*
* @cliexpar
* To enable proxy arp on a range of addresses, use:
* @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11}
* Append 'del' to delete a range of proxy ARP addresses:
* @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11 del}
* You must then specifically enable proxy arp on individual interfaces:
* @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 enable}
* To disable proxy arp on an individual interface:
* @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 disable}
?*/
VLIB_CLI_COMMAND (set_int_proxy_enable_command, static) = {
.path = "set interface proxy-arp",
.short_help =
"set interface proxy-arp <intfc> [enable|disable]",
.function = set_int_proxy_arp_command_fn,
};
/* *INDENT-ON* */
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (set_arp_proxy_command, static) = {
.path = "set arp proxy",
.short_help = "set arp proxy [del] table-ID <table-ID> start <start-address> end <end-addres>",
.function = set_arp_proxy,
};
/* *INDENT-ON* */
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 uword
arp_proxy (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
{
arp_proxy_main_t *am = &arp_proxy_main;
vnet_main_t *vnm = vnet_get_main ();
u32 n_left_from, next_index, *from, *to_next;
u32 n_arp_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;
ip4_address_t proxy_src;
u32 pi0, error0, next0, sw_if_index0, fib_index0;
u8 is_request0;
ethernet_proxy_arp_t *pa;
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);
/* Fill in ethernet header. */
eth_rx = ethernet_buffer_get_header (p0);
is_request0 = arp0->opcode
== clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request);
error0 = ARP_ERROR_REPLIES_SENT;
sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
next0 = ARP_REPLY_NEXT_DROP;
fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
if (~0 == fib_index0)
{
error0 = ARP_ERROR_INTERFACE_NO_TABLE;
}
if (0 == error0 && is_request0)
{
u32 this_addr = clib_net_to_host_u32
(arp0->ip4_over_ethernet[1].ip4.as_u32);
vec_foreach (pa, am->proxy_arps)
{
u32 lo_addr = clib_net_to_host_u32 (pa->lo_addr.as_u32);
u32 hi_addr = clib_net_to_host_u32 (pa->hi_addr.as_u32);
/* an ARP request hit in the proxy-arp table? */
if ((this_addr >= lo_addr && this_addr <= hi_addr) &&
(fib_index0 == pa->fib_index))
{
proxy_src.as_u32 =
arp0->ip4_over_ethernet[1].ip4.data_u32;
/*
* change the interface address to the proxied
*/
n_arp_replies_sent++;
next0 =
arp_mk_reply (vnm, p0, sw_if_index0, &proxy_src, arp0,
eth_rx);
}
}
}
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);
}
vlib_error_count (vm, node->node_index, ARP_ERROR_REPLIES_SENT,
n_arp_replies_sent);
return frame->n_vectors;
}
VLIB_REGISTER_NODE (arp_proxy_node, static) =
{
.function = arp_proxy,
.name = "arp-proxy",
.vector_size = sizeof (u32),
.n_errors = ARP_N_ERROR,
.error_counters = arp_error_counters,
.n_next_nodes = ARP_REPLY_N_NEXT,
.next_nodes =
{
[ARP_REPLY_NEXT_DROP] = "error-drop",
[ARP_REPLY_NEXT_REPLY_TX] = "interface-output",
},
.format_buffer = format_ethernet_arp_header,
.format_trace = format_ethernet_arp_input_trace,
};
static clib_error_t *
show_ip4_arp (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
arp_proxy_main_t *am = &arp_proxy_main;
ethernet_proxy_arp_t *pa;
if (vec_len (am->proxy_arps))
{
vlib_cli_output (vm, "Proxy arps enabled for:");
vec_foreach (pa, am->proxy_arps)
{
vlib_cli_output (vm, "Fib_index %d %U - %U ",
pa->fib_index,
format_ip4_address, &pa->lo_addr,
format_ip4_address, &pa->hi_addr);
}
}
return (NULL);
}
/*?
* Display all the IPv4 ARP proxy entries.
*
* @cliexpar
* Example of how to display the IPv4 ARP table:
* @cliexstart{show ip arp}
* Time FIB IP4 Flags Ethernet Interface
* 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
* 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
* 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
* Proxy arps enabled for:
* Fib_index 0 6.0.0.1 - 6.0.0.11
* @cliexend
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (show_ip4_arp_command, static) = {
.path = "show arp proxy",
.function = show_ip4_arp,
.short_help = "show ip arp",
};
/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/