ip: Protocol Independent IP Neighbors
[vpp.git] / src / vnet / arp / arp.c
1 /*
2  * ethernet/arp.c: IP v4 ARP node
3  *
4  * Copyright (c) 2010 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vnet/arp/arp.h>
19 #include <vnet/arp/arp_packet.h>
20
21 #include <vnet/fib/ip4_fib.h>
22 #include <vnet/fib/fib_entry_src.h>
23 #include <vnet/adj/adj_nbr.h>
24 #include <vnet/adj/adj_mcast.h>
25
26 #include <vnet/ip-neighbor/ip_neighbor.h>
27 #include <vnet/ip-neighbor/ip_neighbor_dp.h>
28
29 #include <vlibmemory/api.h>
30
31 /**
32  * @file
33  * @brief IPv4 ARP.
34  *
35  * This file contains code to manage the IPv4 ARP tables (IP Address
36  * to MAC Address lookup).
37  */
38
39 /**
40  * @brief Per-interface ARP configuration and state
41  */
42 typedef struct ethernet_arp_interface_t_
43 {
44   /**
45    * Is ARP enabled on this interface
46    */
47   u32 enabled;
48 } ethernet_arp_interface_t;
49
50 typedef struct
51 {
52   /* Hash tables mapping name to opcode. */
53   uword *opcode_by_name;
54
55   /** Per interface state */
56   ethernet_arp_interface_t *ethernet_arp_by_sw_if_index;
57
58   /* ARP feature arc index */
59   u8 feature_arc_index;
60 } ethernet_arp_main_t;
61
62 static ethernet_arp_main_t ethernet_arp_main;
63
64 static const u8 vrrp_prefix[] = { 0x00, 0x00, 0x5E, 0x00, 0x01 };
65
66 static uword
67 unformat_ethernet_arp_opcode_host_byte_order (unformat_input_t * input,
68                                               va_list * args)
69 {
70   int *result = va_arg (*args, int *);
71   ethernet_arp_main_t *am = &ethernet_arp_main;
72   int x, i;
73
74   /* Numeric opcode. */
75   if (unformat (input, "0x%x", &x) || unformat (input, "%d", &x))
76     {
77       if (x >= (1 << 16))
78         return 0;
79       *result = x;
80       return 1;
81     }
82
83   /* Named type. */
84   if (unformat_user (input, unformat_vlib_number_by_name,
85                      am->opcode_by_name, &i))
86     {
87       *result = i;
88       return 1;
89     }
90
91   return 0;
92 }
93
94 static uword
95 unformat_ethernet_arp_opcode_net_byte_order (unformat_input_t * input,
96                                              va_list * args)
97 {
98   int *result = va_arg (*args, int *);
99   if (!unformat_user
100       (input, unformat_ethernet_arp_opcode_host_byte_order, result))
101     return 0;
102
103   *result = clib_host_to_net_u16 ((u16) * result);
104   return 1;
105 }
106
107 typedef struct
108 {
109   u8 packet_data[64];
110 } ethernet_arp_input_trace_t;
111
112 static u8 *
113 format_ethernet_arp_input_trace (u8 * s, va_list * va)
114 {
115   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
116   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
117   ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
118
119   s = format (s, "%U",
120               format_ethernet_arp_header,
121               t->packet_data, sizeof (t->packet_data));
122
123   return s;
124 }
125
126 static int
127 arp_is_enabled (ethernet_arp_main_t * am, u32 sw_if_index)
128 {
129   if (vec_len (am->ethernet_arp_by_sw_if_index) <= sw_if_index)
130     return 0;
131
132   return (am->ethernet_arp_by_sw_if_index[sw_if_index].enabled);
133 }
134
135 static void
136 arp_enable (ethernet_arp_main_t * am, u32 sw_if_index)
137 {
138   if (arp_is_enabled (am, sw_if_index))
139     return;
140
141   vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index);
142
143   am->ethernet_arp_by_sw_if_index[sw_if_index].enabled = 1;
144
145   vnet_feature_enable_disable ("arp", "arp-reply", sw_if_index, 1, NULL, 0);
146   vnet_feature_enable_disable ("arp", "arp-disabled", sw_if_index, 0, NULL,
147                                0);
148 }
149
150 static void
151 arp_disable (ethernet_arp_main_t * am, u32 sw_if_index)
152 {
153   if (!arp_is_enabled (am, sw_if_index))
154     return;
155
156   vnet_feature_enable_disable ("arp", "arp-disabled", sw_if_index, 1, NULL,
157                                0);
158   vnet_feature_enable_disable ("arp", "arp-reply", sw_if_index, 0, NULL, 0);
159
160   am->ethernet_arp_by_sw_if_index[sw_if_index].enabled = 0;
161 }
162
163 static int
164 arp_unnumbered (vlib_buffer_t * p0,
165                 u32 input_sw_if_index, u32 conn_sw_if_index)
166 {
167   vnet_main_t *vnm = vnet_get_main ();
168   vnet_interface_main_t *vim = &vnm->interface_main;
169   vnet_sw_interface_t *si;
170
171   /* verify that the input interface is unnumbered to the connected.
172    * the connected interface is the interface on which the subnet is
173    * configured */
174   si = &vim->sw_interfaces[input_sw_if_index];
175
176   if (!(si->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED &&
177         (si->unnumbered_sw_if_index == conn_sw_if_index)))
178     {
179       /* the input interface is not unnumbered to the interface on which
180        * the sub-net is configured that covers the ARP request.
181        * So this is not the case for unnumbered.. */
182       return 0;
183     }
184
185   return !0;
186 }
187
188 always_inline u32
189 arp_learn (u32 sw_if_index,
190            const ethernet_arp_ip4_over_ethernet_address_t * addr)
191 {
192   ip_neighbor_learn_t l = {
193     .ip.ip4 = addr->ip4,
194     .type = IP46_TYPE_IP4,
195     .mac = addr->mac,
196     .sw_if_index = sw_if_index,
197   };
198
199   ip_neighbor_learn_dp (&l);
200
201   return (ETHERNET_ARP_ERROR_l3_src_address_learned);
202 }
203
204 typedef enum arp_input_next_t_
205 {
206   ARP_INPUT_NEXT_DROP,
207   ARP_INPUT_NEXT_DISABLED,
208   ARP_INPUT_N_NEXT,
209 } arp_input_next_t;
210
211 static uword
212 arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
213 {
214   u32 n_left_from, next_index, *from, *to_next, n_left_to_next;
215   ethernet_arp_main_t *am = &ethernet_arp_main;
216
217   from = vlib_frame_vector_args (frame);
218   n_left_from = frame->n_vectors;
219   next_index = node->cached_next_index;
220
221   if (node->flags & VLIB_NODE_FLAG_TRACE)
222     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
223                                    /* stride */ 1,
224                                    sizeof (ethernet_arp_input_trace_t));
225
226   while (n_left_from > 0)
227     {
228       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
229
230       while (n_left_from > 0 && n_left_to_next > 0)
231         {
232           const ethernet_arp_header_t *arp0;
233           arp_input_next_t next0;
234           vlib_buffer_t *p0;
235           u32 pi0, error0;
236
237           pi0 = to_next[0] = from[0];
238           from += 1;
239           to_next += 1;
240           n_left_from -= 1;
241           n_left_to_next -= 1;
242
243           p0 = vlib_get_buffer (vm, pi0);
244           arp0 = vlib_buffer_get_current (p0);
245
246           error0 = ETHERNET_ARP_ERROR_replies_sent;
247           next0 = ARP_INPUT_NEXT_DROP;
248
249           error0 =
250             (arp0->l2_type !=
251              clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet) ?
252              ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0);
253           error0 =
254             (arp0->l3_type !=
255              clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ?
256              ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0);
257           error0 =
258             (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ?
259              ETHERNET_ARP_ERROR_l3_dst_address_unset : error0);
260
261           if (ETHERNET_ARP_ERROR_replies_sent == error0)
262             {
263               next0 = ARP_INPUT_NEXT_DISABLED;
264               vnet_feature_arc_start (am->feature_arc_index,
265                                       vnet_buffer (p0)->sw_if_index[VLIB_RX],
266                                       &next0, p0);
267             }
268           else
269             p0->error = node->errors[error0];
270
271           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
272                                            n_left_to_next, pi0, next0);
273         }
274
275       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
276     }
277
278   return frame->n_vectors;
279 }
280
281 typedef enum arp_disabled_next_t_
282 {
283   ARP_DISABLED_NEXT_DROP,
284   ARP_DISABLED_N_NEXT,
285 } arp_disabled_next_t;
286
287 #define foreach_arp_disabled_error                                      \
288   _ (DISABLED, "ARP Disabled on this interface")                    \
289
290 typedef enum
291 {
292 #define _(sym,string) ARP_DISABLED_ERROR_##sym,
293   foreach_arp_disabled_error
294 #undef _
295     ARP_DISABLED_N_ERROR,
296 } arp_disabled_error_t;
297
298 static char *arp_disabled_error_strings[] = {
299 #define _(sym,string) string,
300   foreach_arp_disabled_error
301 #undef _
302 };
303
304 static uword
305 arp_disabled (vlib_main_t * vm,
306               vlib_node_runtime_t * node, vlib_frame_t * frame)
307 {
308   u32 n_left_from, next_index, *from, *to_next, n_left_to_next;
309
310   from = vlib_frame_vector_args (frame);
311   n_left_from = frame->n_vectors;
312   next_index = node->cached_next_index;
313
314   if (node->flags & VLIB_NODE_FLAG_TRACE)
315     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
316                                    /* stride */ 1,
317                                    sizeof (ethernet_arp_input_trace_t));
318
319   while (n_left_from > 0)
320     {
321       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
322
323       while (n_left_from > 0 && n_left_to_next > 0)
324         {
325           arp_disabled_next_t next0 = ARP_DISABLED_NEXT_DROP;
326           vlib_buffer_t *p0;
327           u32 pi0, error0;
328
329           next0 = ARP_DISABLED_NEXT_DROP;
330           error0 = ARP_DISABLED_ERROR_DISABLED;
331
332           pi0 = to_next[0] = from[0];
333           from += 1;
334           to_next += 1;
335           n_left_from -= 1;
336           n_left_to_next -= 1;
337
338           p0 = vlib_get_buffer (vm, pi0);
339           p0->error = node->errors[error0];
340
341           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
342                                            n_left_to_next, pi0, next0);
343         }
344
345       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
346     }
347
348   return frame->n_vectors;
349 }
350
351 enum arp_dst_fib_type
352 {
353   ARP_DST_FIB_NONE,
354   ARP_DST_FIB_ADJ,
355   ARP_DST_FIB_CONN
356 };
357
358 /*
359  * we're looking for FIB sources that indicate the destination
360  * is attached. There may be interposed DPO prior to the one
361  * we are looking for
362  */
363 static enum arp_dst_fib_type
364 arp_dst_fib_check (const fib_node_index_t fei, fib_entry_flag_t * flags)
365 {
366   const fib_entry_t *entry = fib_entry_get (fei);
367   const fib_entry_src_t *entry_src;
368   fib_source_t src;
369   /* *INDENT-OFF* */
370   FOR_EACH_SRC_ADDED(entry, entry_src, src,
371   ({
372     *flags = fib_entry_get_flags_for_source (fei, src);
373     if (fib_entry_is_sourced (fei, FIB_SOURCE_ADJ))
374         return ARP_DST_FIB_ADJ;
375       else if (FIB_ENTRY_FLAG_CONNECTED & *flags)
376         return ARP_DST_FIB_CONN;
377   }))
378   /* *INDENT-ON* */
379
380   return ARP_DST_FIB_NONE;
381 }
382
383 static uword
384 arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
385 {
386   vnet_main_t *vnm = vnet_get_main ();
387   u32 n_left_from, next_index, *from, *to_next;
388   u32 n_replies_sent = 0;
389
390   from = vlib_frame_vector_args (frame);
391   n_left_from = frame->n_vectors;
392   next_index = node->cached_next_index;
393
394   if (node->flags & VLIB_NODE_FLAG_TRACE)
395     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
396                                    /* stride */ 1,
397                                    sizeof (ethernet_arp_input_trace_t));
398
399   while (n_left_from > 0)
400     {
401       u32 n_left_to_next;
402
403       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
404
405       while (n_left_from > 0 && n_left_to_next > 0)
406         {
407           vlib_buffer_t *p0;
408           ethernet_arp_header_t *arp0;
409           ethernet_header_t *eth_rx;
410           const ip4_address_t *if_addr0;
411           u32 pi0, error0, next0, sw_if_index0, conn_sw_if_index0, fib_index0;
412           u8 dst_is_local0, is_vrrp_reply0;
413           fib_node_index_t dst_fei, src_fei;
414           const fib_prefix_t *pfx0;
415           fib_entry_flag_t src_flags, dst_flags;
416
417           pi0 = from[0];
418           to_next[0] = pi0;
419           from += 1;
420           to_next += 1;
421           n_left_from -= 1;
422           n_left_to_next -= 1;
423
424           p0 = vlib_get_buffer (vm, pi0);
425           arp0 = vlib_buffer_get_current (p0);
426           /* Fill in ethernet header. */
427           eth_rx = ethernet_buffer_get_header (p0);
428
429           next0 = ARP_REPLY_NEXT_DROP;
430           error0 = ETHERNET_ARP_ERROR_replies_sent;
431           sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
432
433           /* Check that IP address is local and matches incoming interface. */
434           fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
435           if (~0 == fib_index0)
436             {
437               error0 = ETHERNET_ARP_ERROR_interface_no_table;
438               goto drop;
439
440             }
441
442           {
443             /*
444              * we're looking for FIB entries that indicate the source
445              * is attached. There may be more specific non-attached
446              * routes that match the source, but these do not influence
447              * whether we respond to an ARP request, i.e. they do not
448              * influence whether we are the correct way for the sender
449              * to reach us, they only affect how we reach the sender.
450              */
451             fib_entry_t *src_fib_entry;
452             const fib_prefix_t *pfx;
453             fib_entry_src_t *src;
454             fib_source_t source;
455             int attached;
456             int mask;
457
458             mask = 32;
459             attached = 0;
460
461             do
462               {
463                 src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
464                                                 &arp0->
465                                                 ip4_over_ethernet[0].ip4,
466                                                 mask);
467                 src_fib_entry = fib_entry_get (src_fei);
468
469                 /*
470                  * It's possible that the source that provides the
471                  * flags we need, or the flags we must not have,
472                  * is not the best source, so check then all.
473                  */
474                 /* *INDENT-OFF* */
475                 FOR_EACH_SRC_ADDED(src_fib_entry, src, source,
476                 ({
477                   src_flags = fib_entry_get_flags_for_source (src_fei, source);
478
479                   /* Reject requests/replies with our local interface
480                      address. */
481                   if (FIB_ENTRY_FLAG_LOCAL & src_flags)
482                     {
483                       error0 = ETHERNET_ARP_ERROR_l3_src_address_is_local;
484                       /*
485                        * When VPP has an interface whose address is also
486                        * applied to a TAP interface on the host, then VPP's
487                        * TAP interface will be unnumbered  to the 'real'
488                        * interface and do proxy ARP from the host.
489                        * The curious aspect of this setup is that ARP requests
490                        * from the host will come from the VPP's own address.
491                        * So don't drop immediately here, instead go see if this
492                        * is a proxy ARP case.
493                        */
494                       goto next_feature;
495                     }
496                   /* A Source must also be local to subnet of matching
497                    * interface address. */
498                   if ((FIB_ENTRY_FLAG_ATTACHED & src_flags) ||
499                       (FIB_ENTRY_FLAG_CONNECTED & src_flags))
500                     {
501                       attached = 1;
502                       break;
503                     }
504                   /*
505                    * else
506                    *  The packet was sent from an address that is not
507                    *  connected nor attached i.e. it is not from an
508                    *  address that is covered by a link's sub-net,
509                    *  nor is it a already learned host resp.
510                    */
511                 }));
512                 /* *INDENT-ON* */
513
514                 /*
515                  * shorter mask lookup for the next iteration.
516                  */
517                 pfx = fib_entry_get_prefix (src_fei);
518                 mask = pfx->fp_len - 1;
519
520                 /*
521                  * continue until we hit the default route or we find
522                  * the attached we are looking for. The most likely
523                  * outcome is we find the attached with the first source
524                  * on the first lookup.
525                  */
526               }
527             while (!attached &&
528                    !fib_entry_is_sourced (src_fei, FIB_SOURCE_DEFAULT_ROUTE));
529
530             if (!attached)
531               {
532                 /*
533                  * the matching route is a not attached, i.e. it was
534                  * added as a result of routing, rather than interface/ARP
535                  * configuration. If the matching route is not a host route
536                  * (i.e. a /32)
537                  */
538                 error0 = ETHERNET_ARP_ERROR_l3_src_address_not_local;
539                 goto drop;
540               }
541           }
542
543           dst_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
544                                           &arp0->ip4_over_ethernet[1].ip4,
545                                           32);
546           switch (arp_dst_fib_check (dst_fei, &dst_flags))
547             {
548             case ARP_DST_FIB_ADJ:
549               /*
550                * We matched an adj-fib on ths source subnet (a /32 previously
551                * added as a result of ARP). If this request is a gratuitous
552                * ARP, then learn from it.
553                * The check for matching an adj-fib, is to prevent hosts
554                * from spamming us with gratuitous ARPS that might otherwise
555                * blow our ARP cache
556                */
557               if (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
558                   arp0->ip4_over_ethernet[1].ip4.as_u32)
559                 error0 =
560                   arp_learn (sw_if_index0, &arp0->ip4_over_ethernet[0]);
561               goto drop;
562             case ARP_DST_FIB_CONN:
563               /* destination is connected, continue to process */
564               break;
565             case ARP_DST_FIB_NONE:
566               /* destination is not connected, stop here */
567               error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
568               goto next_feature;
569             }
570
571           dst_is_local0 = (FIB_ENTRY_FLAG_LOCAL & dst_flags);
572           pfx0 = fib_entry_get_prefix (dst_fei);
573           if_addr0 = &pfx0->fp_addr.ip4;
574
575           is_vrrp_reply0 =
576             ((arp0->opcode ==
577               clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
578              &&
579              (!memcmp
580               (arp0->ip4_over_ethernet[0].mac.bytes, vrrp_prefix,
581                sizeof (vrrp_prefix))));
582
583           /* Trash ARP packets whose ARP-level source addresses do not
584              match their L2-frame-level source addresses, unless it's
585              a reply from a VRRP virtual router */
586           if (!ethernet_mac_address_equal
587               (eth_rx->src_address,
588                arp0->ip4_over_ethernet[0].mac.bytes) && !is_vrrp_reply0)
589             {
590               error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
591               goto drop;
592             }
593
594           /* Learn or update sender's mapping only for replies to addresses
595            * that are local to the subnet */
596           if (arp0->opcode ==
597               clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
598             {
599               if (dst_is_local0)
600                 error0 =
601                   arp_learn (sw_if_index0, &arp0->ip4_over_ethernet[0]);
602               else
603                 /* a reply for a non-local destination could be a GARP.
604                  * GARPs for hosts we know were handled above, so this one
605                  * we drop */
606                 error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
607
608               goto next_feature;
609             }
610           else if (arp0->opcode ==
611                    clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request) &&
612                    (dst_is_local0 == 0))
613             {
614               goto next_feature;
615             }
616
617           /* Honor unnumbered interface, if any */
618           conn_sw_if_index0 = fib_entry_get_resolving_interface (dst_fei);
619           if (sw_if_index0 != conn_sw_if_index0 ||
620               sw_if_index0 != fib_entry_get_resolving_interface (src_fei))
621             {
622               /*
623                * The interface the ARP is sent to or was received on is not the
624                * interface on which the covering prefix is configured.
625                * Maybe this is a case for unnumbered.
626                */
627               if (!arp_unnumbered (p0, sw_if_index0, conn_sw_if_index0))
628                 {
629                   error0 = ETHERNET_ARP_ERROR_unnumbered_mismatch;
630                   goto drop;
631                 }
632             }
633           if (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
634               arp0->ip4_over_ethernet[1].ip4.as_u32)
635             {
636               error0 = ETHERNET_ARP_ERROR_gratuitous_arp;
637               goto drop;
638             }
639
640           next0 = arp_mk_reply (vnm, p0, sw_if_index0,
641                                 if_addr0, arp0, eth_rx);
642
643           /* We are going to reply to this request, so, in the absence of
644              errors, learn the sender */
645           if (!error0)
646             error0 = arp_learn (sw_if_index0, &arp0->ip4_over_ethernet[1]);
647
648           n_replies_sent += 1;
649           goto enqueue;
650
651         next_feature:
652           vnet_feature_next (&next0, p0);
653           goto enqueue;
654
655         drop:
656           p0->error = node->errors[error0];
657
658         enqueue:
659           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
660                                            n_left_to_next, pi0, next0);
661         }
662
663       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
664     }
665
666   vlib_error_count (vm, node->node_index,
667                     ETHERNET_ARP_ERROR_replies_sent, n_replies_sent);
668
669   return frame->n_vectors;
670 }
671
672
673 static char *ethernet_arp_error_strings[] = {
674 #define _(sym,string) string,
675   foreach_ethernet_arp_error
676 #undef _
677 };
678
679 /* *INDENT-OFF* */
680
681 VLIB_REGISTER_NODE (arp_input_node, static) =
682 {
683   .function = arp_input,
684   .name = "arp-input",
685   .vector_size = sizeof (u32),
686   .n_errors = ETHERNET_ARP_N_ERROR,
687   .error_strings = ethernet_arp_error_strings,
688   .n_next_nodes = ARP_INPUT_N_NEXT,
689   .next_nodes = {
690     [ARP_INPUT_NEXT_DROP] = "error-drop",
691     [ARP_INPUT_NEXT_DISABLED] = "arp-disabled",
692   },
693   .format_buffer = format_ethernet_arp_header,
694   .format_trace = format_ethernet_arp_input_trace,
695 };
696
697 VLIB_REGISTER_NODE (arp_disabled_node, static) =
698 {
699   .function = arp_disabled,
700   .name = "arp-disabled",
701   .vector_size = sizeof (u32),
702   .n_errors = ARP_DISABLED_N_ERROR,
703   .error_strings = arp_disabled_error_strings,
704   .n_next_nodes = ARP_DISABLED_N_NEXT,
705   .next_nodes = {
706     [ARP_INPUT_NEXT_DROP] = "error-drop",
707   },
708   .format_buffer = format_ethernet_arp_header,
709   .format_trace = format_ethernet_arp_input_trace,
710 };
711
712 VLIB_REGISTER_NODE (arp_reply_node, static) =
713 {
714   .function = arp_reply,
715   .name = "arp-reply",
716   .vector_size = sizeof (u32),
717   .n_errors = ETHERNET_ARP_N_ERROR,
718   .error_strings = ethernet_arp_error_strings,
719   .n_next_nodes = ARP_REPLY_N_NEXT,
720   .next_nodes = {
721     [ARP_REPLY_NEXT_DROP] = "error-drop",
722     [ARP_REPLY_NEXT_REPLY_TX] = "interface-output",
723   },
724   .format_buffer = format_ethernet_arp_header,
725   .format_trace = format_ethernet_arp_input_trace,
726 };
727
728 /* Built-in ARP rx feature path definition */
729 VNET_FEATURE_ARC_INIT (arp_feat, static) =
730 {
731   .arc_name = "arp",
732   .start_nodes = VNET_FEATURES ("arp-input"),
733   .last_in_arc = "error-drop",
734   .arc_index_ptr = &ethernet_arp_main.feature_arc_index,
735 };
736
737 VNET_FEATURE_INIT (arp_reply_feat_node, static) =
738 {
739   .arc_name = "arp",
740   .node_name = "arp-reply",
741   .runs_before = VNET_FEATURES ("arp-disabled"),
742 };
743
744 VNET_FEATURE_INIT (arp_proxy_feat_node, static) =
745 {
746   .arc_name = "arp",
747   .node_name = "arp-proxy",
748   .runs_after = VNET_FEATURES ("arp-reply"),
749   .runs_before = VNET_FEATURES ("arp-disabled"),
750 };
751
752 VNET_FEATURE_INIT (arp_disabled_feat_node, static) =
753 {
754   .arc_name = "arp",
755   .node_name = "arp-disabled",
756   .runs_before = VNET_FEATURES ("error-drop"),
757 };
758
759 VNET_FEATURE_INIT (arp_drop_feat_node, static) =
760 {
761   .arc_name = "arp",
762   .node_name = "error-drop",
763   .runs_before = 0,     /* last feature */
764 };
765
766 /* *INDENT-ON* */
767
768 typedef struct
769 {
770   pg_edit_t l2_type, l3_type;
771   pg_edit_t n_l2_address_bytes, n_l3_address_bytes;
772   pg_edit_t opcode;
773   struct
774   {
775     pg_edit_t mac;
776     pg_edit_t ip4;
777   } ip4_over_ethernet[2];
778 } pg_ethernet_arp_header_t;
779
780 static inline void
781 pg_ethernet_arp_header_init (pg_ethernet_arp_header_t * p)
782 {
783   /* Initialize fields that are not bit fields in the IP header. */
784 #define _(f) pg_edit_init (&p->f, ethernet_arp_header_t, f);
785   _(l2_type);
786   _(l3_type);
787   _(n_l2_address_bytes);
788   _(n_l3_address_bytes);
789   _(opcode);
790   _(ip4_over_ethernet[0].mac);
791   _(ip4_over_ethernet[0].ip4);
792   _(ip4_over_ethernet[1].mac);
793   _(ip4_over_ethernet[1].ip4);
794 #undef _
795 }
796
797 uword
798 unformat_pg_arp_header (unformat_input_t * input, va_list * args)
799 {
800   pg_stream_t *s = va_arg (*args, pg_stream_t *);
801   pg_ethernet_arp_header_t *p;
802   u32 group_index;
803
804   p = pg_create_edit_group (s, sizeof (p[0]), sizeof (ethernet_arp_header_t),
805                             &group_index);
806   pg_ethernet_arp_header_init (p);
807
808   /* Defaults. */
809   pg_edit_set_fixed (&p->l2_type, ETHERNET_ARP_HARDWARE_TYPE_ethernet);
810   pg_edit_set_fixed (&p->l3_type, ETHERNET_TYPE_IP4);
811   pg_edit_set_fixed (&p->n_l2_address_bytes, 6);
812   pg_edit_set_fixed (&p->n_l3_address_bytes, 4);
813
814   if (!unformat (input, "%U: %U/%U -> %U/%U",
815                  unformat_pg_edit,
816                  unformat_ethernet_arp_opcode_net_byte_order, &p->opcode,
817                  unformat_pg_edit,
818                  unformat_mac_address_t, &p->ip4_over_ethernet[0].mac,
819                  unformat_pg_edit,
820                  unformat_ip4_address, &p->ip4_over_ethernet[0].ip4,
821                  unformat_pg_edit,
822                  unformat_mac_address_t, &p->ip4_over_ethernet[1].mac,
823                  unformat_pg_edit,
824                  unformat_ip4_address, &p->ip4_over_ethernet[1].ip4))
825     {
826       /* Free up any edits we may have added. */
827       pg_free_edit_group (s);
828       return 0;
829     }
830   return 1;
831 }
832
833 /*
834  * callback when an interface address is added or deleted
835  */
836 static void
837 arp_enable_disable_interface (ip4_main_t * im,
838                               uword opaque, u32 sw_if_index, u32 is_enable)
839 {
840   ethernet_arp_main_t *am = &ethernet_arp_main;
841
842   if (is_enable)
843     arp_enable (am, sw_if_index);
844   else
845     arp_disable (am, sw_if_index);
846 }
847
848 /*
849  * Remove any arp entries associated with the specified interface
850  */
851 static clib_error_t *
852 vnet_arp_add_del_sw_interface (vnet_main_t * vnm, u32 sw_if_index, u32 is_add)
853 {
854   ethernet_arp_main_t *am = &ethernet_arp_main;
855
856   if (!is_add && sw_if_index != ~0)
857     {
858       arp_disable (am, sw_if_index);
859     }
860   else if (is_add)
861     {
862       vnet_feature_enable_disable ("arp", "arp-disabled",
863                                    sw_if_index, 1, NULL, 0);
864     }
865
866   return (NULL);
867 }
868
869 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (vnet_arp_add_del_sw_interface);
870
871 const static ip_neighbor_vft_t arp_vft = {
872   .inv_proxy4_add = arp_proxy_add,
873   .inv_proxy4_del = arp_proxy_del,
874   .inv_proxy4_enable = arp_proxy_disable,
875   .inv_proxy4_disable = arp_proxy_disable,
876 };
877
878 static clib_error_t *
879 ethernet_arp_init (vlib_main_t * vm)
880 {
881   ethernet_arp_main_t *am = &ethernet_arp_main;
882   ip4_main_t *im = &ip4_main;
883   pg_node_t *pn;
884
885   ethernet_register_input_type (vm, ETHERNET_TYPE_ARP, arp_input_node.index);
886
887   pn = pg_get_node (arp_input_node.index);
888   pn->unformat_edit = unformat_pg_arp_header;
889
890   am->opcode_by_name = hash_create_string (0, sizeof (uword));
891 #define _(o) hash_set_mem (am->opcode_by_name, #o, ETHERNET_ARP_OPCODE_##o);
892   foreach_ethernet_arp_opcode;
893 #undef _
894
895   /* don't trace ARP error packets */
896   {
897     vlib_node_runtime_t *rt =
898       vlib_node_get_runtime (vm, arp_input_node.index);
899
900 #define _(a,b)                                  \
901     vnet_pcap_drop_trace_filter_add_del         \
902         (rt->errors[ETHERNET_ARP_ERROR_##a],    \
903          1 /* is_add */);
904     foreach_ethernet_arp_error
905 #undef _
906   }
907
908   {
909     ip4_enable_disable_interface_callback_t cb = {
910       .function = arp_enable_disable_interface,
911     };
912     vec_add1 (im->enable_disable_interface_callbacks, cb);
913   }
914
915   ip_neighbor_register (IP46_TYPE_IP4, &arp_vft);
916
917   return 0;
918 }
919
920 /* *INDENT-OFF* */
921 VLIB_INIT_FUNCTION (ethernet_arp_init) =
922 {
923   .runs_after = VLIB_INITS("ethernet_init",
924                            "ip_neighbor_init"),
925 };
926 /* *INDENT-ON* */
927
928 /*
929  * fd.io coding-style-patch-verification: ON
930  *
931  * Local Variables:
932  * eval: (c-set-style "gnu")
933  * End:
934  */