summaryrefslogtreecommitdiffstats
path: root/src/vnet/arp/arp_proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vnet/arp/arp_proxy.c')
-rw-r--r--src/vnet/arp/arp_proxy.c407
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:
+ */