diff options
Diffstat (limited to 'src/vnet/arp/arp_proxy.c')
-rw-r--r-- | src/vnet/arp/arp_proxy.c | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/src/vnet/arp/arp_proxy.c b/src/vnet/arp/arp_proxy.c new file mode 100644 index 00000000000..346a21775f8 --- /dev/null +++ b/src/vnet/arp/arp_proxy.c @@ -0,0 +1,407 @@ +/* + * 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; +} + +/* *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* */ + +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 = ETHERNET_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 = ETHERNET_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, + ETHERNET_ARP_ERROR_replies_sent, n_arp_replies_sent); + + return frame->n_vectors; +} + +static char *ethernet_arp_error_strings[] = { +#define _(sym,string) string, + foreach_ethernet_arp_error +#undef _ +}; + +VLIB_REGISTER_NODE (arp_proxy_node, static) = +{ + .function = arp_proxy,.name = "arp-proxy",.vector_size = + sizeof (u32),.n_errors = ETHERNET_ARP_N_ERROR,.error_strings = + ethernet_arp_error_strings,.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: + */ |