6a32229addab3c66099671b464e11afa452d4c17
[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           conn_sw_if_index0 = fib_entry_get_resolving_interface (dst_fei);
547
548           switch (arp_dst_fib_check (dst_fei, &dst_flags))
549             {
550             case ARP_DST_FIB_ADJ:
551               /*
552                * We matched an adj-fib on ths source subnet (a /32 previously
553                * added as a result of ARP). If this request is a gratuitous
554                * ARP, then learn from it.
555                * The check for matching an adj-fib, is to prevent hosts
556                * from spamming us with gratuitous ARPS that might otherwise
557                * blow our ARP cache
558                */
559               if (conn_sw_if_index0 != sw_if_index0)
560                 error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
561               else if (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
562                        arp0->ip4_over_ethernet[1].ip4.as_u32)
563                 error0 = arp_learn (sw_if_index0,
564                                     &arp0->ip4_over_ethernet[0]);
565               goto drop;
566             case ARP_DST_FIB_CONN:
567               /* destination is connected, continue to process */
568               break;
569             case ARP_DST_FIB_NONE:
570               /* destination is not connected, stop here */
571               error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
572               goto next_feature;
573             }
574
575           dst_is_local0 = (FIB_ENTRY_FLAG_LOCAL & dst_flags);
576           pfx0 = fib_entry_get_prefix (dst_fei);
577           if_addr0 = &pfx0->fp_addr.ip4;
578
579           is_vrrp_reply0 =
580             ((arp0->opcode ==
581               clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
582              &&
583              (!memcmp
584               (arp0->ip4_over_ethernet[0].mac.bytes, vrrp_prefix,
585                sizeof (vrrp_prefix))));
586
587           /* Trash ARP packets whose ARP-level source addresses do not
588              match their L2-frame-level source addresses, unless it's
589              a reply from a VRRP virtual router */
590           if (!ethernet_mac_address_equal
591               (eth_rx->src_address,
592                arp0->ip4_over_ethernet[0].mac.bytes) && !is_vrrp_reply0)
593             {
594               error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
595               goto drop;
596             }
597
598           /* Learn or update sender's mapping only for replies to addresses
599            * that are local to the subnet */
600           if (arp0->opcode ==
601               clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
602             {
603               if (dst_is_local0)
604                 error0 =
605                   arp_learn (sw_if_index0, &arp0->ip4_over_ethernet[0]);
606               else
607                 /* a reply for a non-local destination could be a GARP.
608                  * GARPs for hosts we know were handled above, so this one
609                  * we drop */
610                 error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
611
612               goto next_feature;
613             }
614           else if (arp0->opcode ==
615                    clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request) &&
616                    (dst_is_local0 == 0))
617             {
618               goto next_feature;
619             }
620
621           /* Honor unnumbered interface, if any */
622           if (sw_if_index0 != conn_sw_if_index0 ||
623               sw_if_index0 != fib_entry_get_resolving_interface (src_fei))
624             {
625               /*
626                * The interface the ARP is sent to or was received on is not the
627                * interface on which the covering prefix is configured.
628                * Maybe this is a case for unnumbered.
629                */
630               if (!arp_unnumbered (p0, sw_if_index0, conn_sw_if_index0))
631                 {
632                   error0 = ETHERNET_ARP_ERROR_unnumbered_mismatch;
633                   goto drop;
634                 }
635             }
636           if (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
637               arp0->ip4_over_ethernet[1].ip4.as_u32)
638             {
639               error0 = ETHERNET_ARP_ERROR_gratuitous_arp;
640               goto drop;
641             }
642
643           next0 = arp_mk_reply (vnm, p0, sw_if_index0,
644                                 if_addr0, arp0, eth_rx);
645
646           /* We are going to reply to this request, so, in the absence of
647              errors, learn the sender */
648           if (!error0)
649             error0 = arp_learn (sw_if_index0, &arp0->ip4_over_ethernet[1]);
650
651           n_replies_sent += 1;
652           goto enqueue;
653
654         next_feature:
655           vnet_feature_next (&next0, p0);
656           goto enqueue;
657
658         drop:
659           p0->error = node->errors[error0];
660
661         enqueue:
662           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
663                                            n_left_to_next, pi0, next0);
664         }
665
666       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
667     }
668
669   vlib_error_count (vm, node->node_index,
670                     ETHERNET_ARP_ERROR_replies_sent, n_replies_sent);
671
672   return frame->n_vectors;
673 }
674
675
676 static char *ethernet_arp_error_strings[] = {
677 #define _(sym,string) string,
678   foreach_ethernet_arp_error
679 #undef _
680 };
681
682 /* *INDENT-OFF* */
683
684 VLIB_REGISTER_NODE (arp_input_node, static) =
685 {
686   .function = arp_input,
687   .name = "arp-input",
688   .vector_size = sizeof (u32),
689   .n_errors = ETHERNET_ARP_N_ERROR,
690   .error_strings = ethernet_arp_error_strings,
691   .n_next_nodes = ARP_INPUT_N_NEXT,
692   .next_nodes = {
693     [ARP_INPUT_NEXT_DROP] = "error-drop",
694     [ARP_INPUT_NEXT_DISABLED] = "arp-disabled",
695   },
696   .format_buffer = format_ethernet_arp_header,
697   .format_trace = format_ethernet_arp_input_trace,
698 };
699
700 VLIB_REGISTER_NODE (arp_disabled_node, static) =
701 {
702   .function = arp_disabled,
703   .name = "arp-disabled",
704   .vector_size = sizeof (u32),
705   .n_errors = ARP_DISABLED_N_ERROR,
706   .error_strings = arp_disabled_error_strings,
707   .n_next_nodes = ARP_DISABLED_N_NEXT,
708   .next_nodes = {
709     [ARP_INPUT_NEXT_DROP] = "error-drop",
710   },
711   .format_buffer = format_ethernet_arp_header,
712   .format_trace = format_ethernet_arp_input_trace,
713 };
714
715 VLIB_REGISTER_NODE (arp_reply_node, static) =
716 {
717   .function = arp_reply,
718   .name = "arp-reply",
719   .vector_size = sizeof (u32),
720   .n_errors = ETHERNET_ARP_N_ERROR,
721   .error_strings = ethernet_arp_error_strings,
722   .n_next_nodes = ARP_REPLY_N_NEXT,
723   .next_nodes = {
724     [ARP_REPLY_NEXT_DROP] = "error-drop",
725     [ARP_REPLY_NEXT_REPLY_TX] = "interface-output",
726   },
727   .format_buffer = format_ethernet_arp_header,
728   .format_trace = format_ethernet_arp_input_trace,
729 };
730
731 /* Built-in ARP rx feature path definition */
732 VNET_FEATURE_ARC_INIT (arp_feat, static) =
733 {
734   .arc_name = "arp",
735   .start_nodes = VNET_FEATURES ("arp-input"),
736   .last_in_arc = "error-drop",
737   .arc_index_ptr = &ethernet_arp_main.feature_arc_index,
738 };
739
740 VNET_FEATURE_INIT (arp_reply_feat_node, static) =
741 {
742   .arc_name = "arp",
743   .node_name = "arp-reply",
744   .runs_before = VNET_FEATURES ("arp-disabled"),
745 };
746
747 VNET_FEATURE_INIT (arp_proxy_feat_node, static) =
748 {
749   .arc_name = "arp",
750   .node_name = "arp-proxy",
751   .runs_after = VNET_FEATURES ("arp-reply"),
752   .runs_before = VNET_FEATURES ("arp-disabled"),
753 };
754
755 VNET_FEATURE_INIT (arp_disabled_feat_node, static) =
756 {
757   .arc_name = "arp",
758   .node_name = "arp-disabled",
759   .runs_before = VNET_FEATURES ("error-drop"),
760 };
761
762 VNET_FEATURE_INIT (arp_drop_feat_node, static) =
763 {
764   .arc_name = "arp",
765   .node_name = "error-drop",
766   .runs_before = 0,     /* last feature */
767 };
768
769 /* *INDENT-ON* */
770
771 typedef struct
772 {
773   pg_edit_t l2_type, l3_type;
774   pg_edit_t n_l2_address_bytes, n_l3_address_bytes;
775   pg_edit_t opcode;
776   struct
777   {
778     pg_edit_t mac;
779     pg_edit_t ip4;
780   } ip4_over_ethernet[2];
781 } pg_ethernet_arp_header_t;
782
783 static inline void
784 pg_ethernet_arp_header_init (pg_ethernet_arp_header_t * p)
785 {
786   /* Initialize fields that are not bit fields in the IP header. */
787 #define _(f) pg_edit_init (&p->f, ethernet_arp_header_t, f);
788   _(l2_type);
789   _(l3_type);
790   _(n_l2_address_bytes);
791   _(n_l3_address_bytes);
792   _(opcode);
793   _(ip4_over_ethernet[0].mac);
794   _(ip4_over_ethernet[0].ip4);
795   _(ip4_over_ethernet[1].mac);
796   _(ip4_over_ethernet[1].ip4);
797 #undef _
798 }
799
800 uword
801 unformat_pg_arp_header (unformat_input_t * input, va_list * args)
802 {
803   pg_stream_t *s = va_arg (*args, pg_stream_t *);
804   pg_ethernet_arp_header_t *p;
805   u32 group_index;
806
807   p = pg_create_edit_group (s, sizeof (p[0]), sizeof (ethernet_arp_header_t),
808                             &group_index);
809   pg_ethernet_arp_header_init (p);
810
811   /* Defaults. */
812   pg_edit_set_fixed (&p->l2_type, ETHERNET_ARP_HARDWARE_TYPE_ethernet);
813   pg_edit_set_fixed (&p->l3_type, ETHERNET_TYPE_IP4);
814   pg_edit_set_fixed (&p->n_l2_address_bytes, 6);
815   pg_edit_set_fixed (&p->n_l3_address_bytes, 4);
816
817   if (!unformat (input, "%U: %U/%U -> %U/%U",
818                  unformat_pg_edit,
819                  unformat_ethernet_arp_opcode_net_byte_order, &p->opcode,
820                  unformat_pg_edit,
821                  unformat_mac_address_t, &p->ip4_over_ethernet[0].mac,
822                  unformat_pg_edit,
823                  unformat_ip4_address, &p->ip4_over_ethernet[0].ip4,
824                  unformat_pg_edit,
825                  unformat_mac_address_t, &p->ip4_over_ethernet[1].mac,
826                  unformat_pg_edit,
827                  unformat_ip4_address, &p->ip4_over_ethernet[1].ip4))
828     {
829       /* Free up any edits we may have added. */
830       pg_free_edit_group (s);
831       return 0;
832     }
833   return 1;
834 }
835
836 /*
837  * callback when an interface address is added or deleted
838  */
839 static void
840 arp_enable_disable_interface (ip4_main_t * im,
841                               uword opaque, u32 sw_if_index, u32 is_enable)
842 {
843   ethernet_arp_main_t *am = &ethernet_arp_main;
844
845   if (is_enable)
846     arp_enable (am, sw_if_index);
847   else
848     arp_disable (am, sw_if_index);
849 }
850
851 /*
852  * Remove any arp entries associated with the specified interface
853  */
854 static clib_error_t *
855 vnet_arp_add_del_sw_interface (vnet_main_t * vnm, u32 sw_if_index, u32 is_add)
856 {
857   ethernet_arp_main_t *am = &ethernet_arp_main;
858
859   if (!is_add && sw_if_index != ~0)
860     {
861       arp_disable (am, sw_if_index);
862     }
863   else if (is_add)
864     {
865       vnet_feature_enable_disable ("arp", "arp-disabled",
866                                    sw_if_index, 1, NULL, 0);
867     }
868
869   return (NULL);
870 }
871
872 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (vnet_arp_add_del_sw_interface);
873
874 const static ip_neighbor_vft_t arp_vft = {
875   .inv_proxy4_add = arp_proxy_add,
876   .inv_proxy4_del = arp_proxy_del,
877   .inv_proxy4_enable = arp_proxy_disable,
878   .inv_proxy4_disable = arp_proxy_disable,
879 };
880
881 static clib_error_t *
882 ethernet_arp_init (vlib_main_t * vm)
883 {
884   ethernet_arp_main_t *am = &ethernet_arp_main;
885   ip4_main_t *im = &ip4_main;
886   pg_node_t *pn;
887
888   ethernet_register_input_type (vm, ETHERNET_TYPE_ARP, arp_input_node.index);
889
890   pn = pg_get_node (arp_input_node.index);
891   pn->unformat_edit = unformat_pg_arp_header;
892
893   am->opcode_by_name = hash_create_string (0, sizeof (uword));
894 #define _(o) hash_set_mem (am->opcode_by_name, #o, ETHERNET_ARP_OPCODE_##o);
895   foreach_ethernet_arp_opcode;
896 #undef _
897
898   /* don't trace ARP error packets */
899   {
900     vlib_node_runtime_t *rt =
901       vlib_node_get_runtime (vm, arp_input_node.index);
902
903 #define _(a,b)                                  \
904     vnet_pcap_drop_trace_filter_add_del         \
905         (rt->errors[ETHERNET_ARP_ERROR_##a],    \
906          1 /* is_add */);
907     foreach_ethernet_arp_error
908 #undef _
909   }
910
911   {
912     ip4_enable_disable_interface_callback_t cb = {
913       .function = arp_enable_disable_interface,
914     };
915     vec_add1 (im->enable_disable_interface_callbacks, cb);
916   }
917
918   ip_neighbor_register (IP46_TYPE_IP4, &arp_vft);
919
920   return 0;
921 }
922
923 /* *INDENT-OFF* */
924 VLIB_INIT_FUNCTION (ethernet_arp_init) =
925 {
926   .runs_after = VLIB_INITS("ethernet_init",
927                            "ip_neighbor_init"),
928 };
929 /* *INDENT-ON* */
930
931 /*
932  * fd.io coding-style-patch-verification: ON
933  *
934  * Local Variables:
935  * eval: (c-set-style "gnu")
936  * End:
937  */