ip: Protocol Independent IP Neighbors
[vpp.git] / src / plugins / mactime / mactime.c
1 /*
2  * mactime.c - time-based src mac address filtration
3  *
4  * Copyright (c) 2018 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/vnet.h>
19 #include <vnet/plugin/plugin.h>
20 #include <mactime/mactime.h>
21
22 #include <vlibapi/api.h>
23 #include <vlibmemory/api.h>
24 #include <vpp/app/version.h>
25
26 /* define message IDs */
27 #include <vnet/format_fns.h>
28 #include <mactime/mactime.api_enum.h>
29 #include <mactime/mactime.api_types.h>
30
31 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
32
33 #define REPLY_MSG_ID_BASE mm->msg_id_base
34 #include <vlibapi/api_helper_macros.h>
35
36 #include <vnet/ip-neighbor/ip_neighbor.h>
37
38 mactime_main_t mactime_main;
39
40 /** \file time-base src-mac filter device-input feature arc implementation
41  */
42
43 static void
44 feature_init (mactime_main_t * mm)
45 {
46   if (mm->feature_initialized == 0)
47     {
48       /* Create the lookup table */
49       clib_bihash_init_8_8 (&mm->lookup_table, "mactime lookup table",
50                             mm->lookup_table_num_buckets,
51                             mm->lookup_table_memory_size);
52       clib_timebase_init (&mm->timebase, mm->timezone_offset,
53                           CLIB_TIMEBASE_DAYLIGHT_USA);
54       mm->allow_counters.name = "allow";
55       mm->allow_counters.stat_segment_name = "/mactime/allow";
56       mm->drop_counters.name = "drop";
57       mm->drop_counters.stat_segment_name = "/mactime/drop";
58       mm->feature_initialized = 1;
59     }
60 }
61
62 /** Action function shared between message handler and debug CLI
63 */
64 int
65 mactime_enable_disable (mactime_main_t * mm, u32 sw_if_index,
66                         int enable_disable)
67 {
68   vnet_sw_interface_t *sw;
69   int rv = 0;
70   static u8 url_init_done;
71
72   feature_init (mm);
73
74   /* Utterly wrong? */
75   if (pool_is_free_index (mm->vnet_main->interface_main.sw_interfaces,
76                           sw_if_index))
77     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
78
79   /* Not a physical port? */
80   sw = vnet_get_sw_interface (mm->vnet_main, sw_if_index);
81   if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
82     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
83
84   vnet_feature_enable_disable ("device-input", "mactime",
85                                sw_if_index, enable_disable, 0, 0);
86   vnet_feature_enable_disable ("interface-output", "mactime-tx",
87                                sw_if_index, enable_disable, 0, 0);
88   if (url_init_done == 0)
89     {
90       mactime_url_init (mm->vlib_main);
91       url_init_done = 1;
92     }
93
94   return rv;
95 }
96
97 static clib_error_t *
98 mactime_enable_disable_command_fn (vlib_main_t * vm,
99                                    unformat_input_t * input,
100                                    vlib_cli_command_t * cmd)
101 {
102   mactime_main_t *mm = &mactime_main;
103   u32 sw_if_index = ~0;
104   int enable_disable = 1;
105
106   int rv;
107
108   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
109     {
110       if (unformat (input, "disable"))
111         enable_disable = 0;
112       else if (unformat (input, "%U", unformat_vnet_sw_interface,
113                          mm->vnet_main, &sw_if_index))
114         ;
115       else if (unformat (input, "sw_if_index %d", &sw_if_index))
116         ;
117       else
118         break;
119     }
120
121   if (sw_if_index == ~0)
122     return clib_error_return (0, "Please specify an interface...");
123
124   rv = mactime_enable_disable (mm, sw_if_index, enable_disable);
125
126   switch (rv)
127     {
128     case 0:
129       break;
130
131     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
132       return clib_error_return
133         (0, "Invalid interface, only works on physical ports");
134       break;
135
136     default:
137       return clib_error_return (0, "mactime_enable_disable returned %d", rv);
138     }
139   return 0;
140 }
141
142 /* *INDENT-OFF* */
143 VLIB_CLI_COMMAND (mactime_enable_disable_command, static) =
144 {
145   .path = "mactime enable-disable",
146   .short_help =
147   "mactime enable-disable <interface-name> [disable]",
148   .function = mactime_enable_disable_command_fn,
149 };
150 /* *INDENT-ON* */
151
152
153 /** Enable / disable time-base src mac filtration on an interface
154  */
155
156 static void vl_api_mactime_enable_disable_t_handler
157   (vl_api_mactime_enable_disable_t * mp)
158 {
159   vl_api_mactime_enable_disable_reply_t *rmp;
160   mactime_main_t *mm = &mactime_main;
161   int rv;
162
163   VALIDATE_SW_IF_INDEX (mp);
164
165   rv = mactime_enable_disable (mm, ntohl (mp->sw_if_index),
166                                (int) (mp->enable_disable));
167   BAD_SW_IF_INDEX_LABEL;
168   REPLY_MACRO (VL_API_MACTIME_ENABLE_DISABLE_REPLY);
169 }
170
171 static void
172 vl_api_mactime_dump_t_handler (vl_api_mactime_dump_t * mp)
173 {
174   vl_api_mactime_details_t *ep;
175   vl_api_mactime_dump_reply_t *rmp;
176   mactime_device_t *dev;
177   mactime_main_t *mm = &mactime_main;
178   vl_api_registration_t *rp;
179   int rv = 0, i;
180   u32 his_table_epoch = clib_net_to_host_u32 (mp->my_table_epoch);
181   u32 message_size;
182   u32 name_len;
183   u32 nranges;
184
185   rp = vl_api_client_index_to_registration (mp->client_index);
186   if (rp == 0)
187     return;
188
189   if (his_table_epoch == mm->device_table_epoch)
190     {
191       rv = VNET_API_ERROR_NO_CHANGE;
192       goto send_reply;
193     }
194
195   /* *INDENT-OFF* */
196   pool_foreach (dev, mm->devices,
197   ({
198     message_size = sizeof(*ep) + vec_len(dev->device_name) +
199       vec_len(dev->ranges) * sizeof(ep->ranges[0]);
200
201     ep = vl_msg_api_alloc (message_size);
202     memset (ep, 0, message_size);
203     ep->_vl_msg_id = clib_host_to_net_u16 (VL_API_MACTIME_DETAILS
204                                            + mm->msg_id_base);
205     ep->context = mp->context;
206     /* Index is the key for the stats segment combined counters */
207     ep->pool_index = clib_host_to_net_u32 (dev - mm->devices);
208
209     clib_memcpy_fast (ep->mac_address, dev->mac_address,
210                       sizeof (ep->mac_address));
211     ep->data_quota = clib_host_to_net_u64 (dev->data_quota);
212     ep->data_used_in_range = clib_host_to_net_u64 (dev->data_used_in_range);
213     ep->flags = clib_host_to_net_u32 (dev->flags);
214     nranges = vec_len (dev->ranges);
215     ep->nranges = clib_host_to_net_u32 (nranges);
216
217     for (i = 0; i < vec_len (dev->ranges); i++)
218       {
219         ep->ranges[i].start = dev->ranges[i].start;
220         ep->ranges[i].end = dev->ranges[i].end;
221       }
222
223     name_len = vec_len (dev->device_name);
224     name_len = (name_len < ARRAY_LEN(ep->device_name)) ?
225       name_len : ARRAY_LEN(ep->device_name) - 1;
226
227     clib_memcpy_fast (ep->device_name, dev->device_name,
228                       name_len);
229     ep->device_name [ARRAY_LEN(ep->device_name) -1] = 0;
230     vl_api_send_msg (rp, (u8 *)ep);
231   }));
232   /* *INDENT-OFF* */
233
234  send_reply:
235   /* *INDENT-OFF* */
236   REPLY_MACRO2 (VL_API_MACTIME_DUMP_REPLY,
237   ({
238     rmp->table_epoch = clib_host_to_net_u32 (mm->device_table_epoch);
239   }));
240   /* *INDENT-ON* */
241 }
242
243 /** Create a lookup table entry for the indicated mac address
244  */
245 void
246 mactime_send_create_entry_message (u8 * mac_address)
247 {
248   mactime_main_t *mm = &mactime_main;
249   api_main_t *am;
250   vl_shmem_hdr_t *shmem_hdr;
251   u8 *name;
252   vl_api_mactime_add_del_range_t *mp;
253
254   am = vlibapi_get_main ();
255   shmem_hdr = am->shmem_hdr;
256   mp = vl_msg_api_alloc_as_if_client (sizeof (*mp));
257   clib_memset (mp, 0, sizeof (*mp));
258   mp->_vl_msg_id = ntohs (VL_API_MACTIME_ADD_DEL_RANGE + mm->msg_id_base);
259   name = format (0, "mac-%U", format_mac_address, mac_address);
260
261   memcpy (mp->device_name, name, vec_len (name));
262   memcpy (mp->mac_address, mac_address, sizeof (mp->mac_address));
263   /* $$$ config: create allow / drop / range */
264   mp->allow = 1;
265   mp->is_add = 1;
266   vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) & mp);
267 }
268
269 /** Add or delete static / dynamic accept/drop configuration for a src mac
270  */
271
272 static void vl_api_mactime_add_del_range_t_handler
273   (vl_api_mactime_add_del_range_t * mp)
274 {
275   mactime_main_t *mm = &mactime_main;
276   vl_api_mactime_add_del_range_reply_t *rmp;
277   mactime_device_t *dp;
278   clib_bihash_kv_8_8_t kv;
279   int found = 1;
280   clib_bihash_8_8_t *lut = &mm->lookup_table;
281   u64 data_quota;
282   int i, rv = 0;
283
284   feature_init (mm);
285
286   /*
287    * Change the table epoch. Skip 0 so clients can code my_table_epoch = 0
288    * to receive a full dump.
289    */
290   mm->device_table_epoch++;
291   if (PREDICT_FALSE (mm->device_table_epoch == 0))
292     mm->device_table_epoch++;
293
294   data_quota = clib_net_to_host_u64 (mp->data_quota);
295
296   clib_memset (&kv, 0, sizeof (kv));
297   memcpy (&kv.key, mp->mac_address, sizeof (mp->mac_address));
298
299   /* See if we have a lookup table entry for this src mac address */
300   if (clib_bihash_search_8_8 (lut, &kv, &kv) < 0)
301     found = 0;
302
303   /* Add an entry? */
304   if (mp->is_add)
305     {
306       /* Create the device entry? */
307       if (found == 0)
308         {
309           pool_get (mm->devices, dp);
310           clib_memset (dp, 0, sizeof (*dp));
311           vlib_validate_combined_counter (&mm->allow_counters,
312                                           dp - mm->devices);
313           vlib_zero_combined_counter (&mm->allow_counters, dp - mm->devices);
314           vlib_validate_combined_counter (&mm->drop_counters,
315                                           dp - mm->devices);
316           vlib_zero_combined_counter (&mm->drop_counters, dp - mm->devices);
317           mp->device_name[ARRAY_LEN (mp->device_name) - 1] = 0;
318           dp->device_name = format (0, "%s%c", mp->device_name, 0);
319           memcpy (dp->mac_address, mp->mac_address, sizeof (mp->mac_address));
320           for (i = 0; i < clib_net_to_host_u32 (mp->count); i++)
321             {
322               clib_timebase_range_t _r, *r = &_r;
323               r->start = mp->ranges[i].start;
324               r->end = mp->ranges[i].end;
325               vec_add1 (dp->ranges, r[0]);
326             }
327           /* If we found some time ranges */
328           if (i)
329             {
330               /* Set allow/drop based on msg flags */
331               if (mp->drop)
332                 dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_DROP;
333               if (mp->allow)
334                 dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW;
335               if (mp->allow_quota)
336                 dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA;
337             }
338           else
339             {
340               /* no ranges, it's a static allow/drop */
341               if (mp->drop)
342                 dp->flags = MACTIME_DEVICE_FLAG_STATIC_DROP;
343               if (mp->allow)
344                 dp->flags = MACTIME_DEVICE_FLAG_STATIC_ALLOW;
345             }
346           if (mp->no_udp_10001)
347             dp->flags |= MACTIME_DEVICE_FLAG_DROP_UDP_10001;
348
349           dp->data_quota = data_quota;
350
351           /* Add the hash table entry */
352           kv.value = dp - mm->devices;
353           clib_bihash_add_del_8_8 (lut, &kv, 1 /* is_add */ );
354         }
355       else                      /* add more ranges, flags, etc. */
356         {
357           dp = pool_elt_at_index (mm->devices, kv.value);
358
359           for (i = 0; i < clib_net_to_host_u32 (mp->count); i++)
360             {
361               clib_timebase_range_t _r, *r = &_r;
362               r->start = mp->ranges[i].start;
363               r->end = mp->ranges[i].end;
364               vec_add1 (dp->ranges, r[0]);
365             }
366
367           if (vec_len (dp->ranges))
368             {
369               /* Set allow/drop based on msg flags */
370               if (mp->drop)
371                 dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_DROP;
372               if (mp->allow)
373                 dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW;
374               if (mp->allow_quota)
375                 dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA;
376             }
377           else
378             {
379               /* no ranges, it's a static allow/drop */
380               if (mp->drop)
381                 dp->flags = MACTIME_DEVICE_FLAG_STATIC_DROP;
382               if (mp->allow)
383                 dp->flags = MACTIME_DEVICE_FLAG_STATIC_ALLOW;
384             }
385           if (mp->no_udp_10001)
386             dp->flags |= MACTIME_DEVICE_FLAG_DROP_UDP_10001;
387
388           dp->data_quota = data_quota;
389         }
390     }
391   else                          /* delete case */
392     {
393       if (found == 0)
394         {
395           rv = VNET_API_ERROR_NO_SUCH_ENTRY;
396           goto reply;
397         }
398
399       /* find the device entry */
400       dp = pool_elt_at_index (mm->devices, kv.value);
401
402       /* Remove it from the lookup table */
403       clib_bihash_add_del_8_8 (lut, &kv, 0 /* is_add */ );
404       vec_free (dp->ranges);
405       pool_put (mm->devices, dp);
406     }
407
408 reply:
409   REPLY_MACRO (VL_API_MACTIME_ADD_DEL_RANGE_REPLY);
410 }
411
412 #include <mactime/mactime.api.c>
413 static clib_error_t *
414 mactime_init (vlib_main_t * vm)
415 {
416   mactime_main_t *mm = &mactime_main;
417
418   mm->vlib_main = vm;
419   mm->vnet_main = vnet_get_main ();
420
421   /* Ask for a correctly-sized block of API message decode slots */
422   mm->msg_id_base = setup_message_id_table ();
423
424   mm->lookup_table_num_buckets = MACTIME_NUM_BUCKETS;
425   mm->lookup_table_memory_size = MACTIME_MEMORY_SIZE;
426   mm->timezone_offset = -5;     /* US EST / EDT */
427   return 0;
428 }
429
430 /* *INDENT-OFF* */
431 VLIB_INIT_FUNCTION (mactime_init) =
432 {
433   .runs_after = VLIB_INITS("ip_neighbor_init"),
434 };
435 /* *INDENT-ON* */
436
437 static clib_error_t *
438 mactime_config (vlib_main_t * vm, unformat_input_t * input)
439 {
440   mactime_main_t *mm = &mactime_main;
441
442   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
443     {
444       if (unformat (input, "lookup-table-buckets %u",
445                     &mm->lookup_table_num_buckets))
446         ;
447       else if (unformat (input, "lookup-table-memory %U",
448                          unformat_memory_size, &mm->lookup_table_memory_size))
449         ;
450       else if (unformat (input, "timezone_offset %d", &mm->timezone_offset))
451         ;
452       else
453         {
454           return clib_error_return (0, "unknown input '%U'",
455                                     format_unformat_error, input);
456         }
457     }
458   return 0;
459 }
460
461 VLIB_CONFIG_FUNCTION (mactime_config, "mactime");
462
463 /* *INDENT-OFF* */
464 VNET_FEATURE_INIT (mactime, static) =
465 {
466   .arc_name = "device-input",
467   .node_name = "mactime",
468   .runs_before = VNET_FEATURES ("ethernet-input"),
469 };
470 /* *INDENT-ON */
471
472 /* *INDENT-OFF* */
473 VNET_FEATURE_INIT (mactime_tx, static) =
474 {
475   .arc_name = "interface-output",
476   .node_name = "mactime-tx",
477   .runs_before = VNET_FEATURES ("interface-tx"),
478 };
479 /* *INDENT-ON */
480
481 /* *INDENT-OFF* */
482 VLIB_PLUGIN_REGISTER () =
483 {
484   .version = VPP_BUILD_VER,
485   .description = "Time-based MAC Source Address Filter",
486 };
487 /* *INDENT-ON* */
488
489 u8 *
490 format_bytes_with_width (u8 * s, va_list * va)
491 {
492   uword nbytes = va_arg (*va, u64);
493   int width = va_arg (*va, int);
494   f64 nbytes_f64;
495   u8 *fmt;
496   char *suffix = "";
497
498   if (width > 0)
499     fmt = format (0, "%%%d.3f%%s%c", width, 0);
500   else
501     fmt = format (0, "%%.3f%%s%c", 0);
502
503   if (nbytes > (1024ULL * 1024ULL * 1024ULL))
504     {
505       nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0 * 1024.0);
506       suffix = "G";
507     }
508   else if (nbytes > (1024ULL * 1024ULL))
509     {
510       nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0);
511       suffix = "M";
512     }
513   else if (nbytes > 1024ULL)
514     {
515       nbytes_f64 = ((f64) nbytes) / (1024.0);
516       suffix = "K";
517     }
518   else
519     {
520       nbytes_f64 = (f64) nbytes;
521       suffix = "B";
522     }
523
524   s = format (s, (char *) fmt, nbytes_f64, suffix);
525   vec_free (fmt);
526   return s;
527 }
528
529 static walk_rc_t
530 mactime_ip_neighbor_copy (index_t ipni, void *ctx)
531 {
532   mactime_main_t *mm = ctx;
533
534   vec_add1 (mm->arp_cache_copy, ipni);
535
536   return (WALK_CONTINUE);
537 }
538
539 static clib_error_t *
540 show_mactime_command_fn (vlib_main_t * vm,
541                          unformat_input_t * input, vlib_cli_command_t * cmd)
542 {
543   mactime_main_t *mm = &mactime_main;
544   mactime_device_t *dp;
545   u8 *macstring = 0;
546   char *status_string;
547   u32 *pool_indices = 0;
548   int verbose = 0;
549   int current_status = 99;
550   int i, j;
551   f64 now;
552   vlib_counter_t allow, drop;
553   ip_neighbor_t *ipn;
554
555   vec_reset_length (mm->arp_cache_copy);
556   /* Walk all ip4 neighbours on all interfaces */
557   ip_neighbor_walk (IP46_TYPE_IP4, ~0, mactime_ip_neighbor_copy, mm);
558
559   now = clib_timebase_now (&mm->timebase);
560
561   if (PREDICT_FALSE ((now - mm->sunday_midnight) > 86400.0 * 7.0))
562     mm->sunday_midnight = clib_timebase_find_sunday_midnight (now);
563
564   if (unformat (input, "verbose %d", &verbose))
565     ;
566
567   if (unformat (input, "verbose"))
568     verbose = 1;
569
570   if (verbose)
571     vlib_cli_output (vm, "Time now: %U", format_clib_timebase_time, now);
572
573   /* *INDENT-OFF* */
574   pool_foreach (dp, mm->devices,
575   ({
576     vec_add1 (pool_indices, dp - mm->devices);
577   }));
578   /* *INDENT-ON* */
579
580   vlib_cli_output (vm, "%-15s %18s %14s %10s %11s %13s",
581                    "Device Name", "Addresses", "Status",
582                    "AllowPkt", "AllowByte", "DropPkt");
583
584   for (i = 0; i < vec_len (pool_indices); i++)
585     {
586       dp = pool_elt_at_index (mm->devices, pool_indices[i]);
587
588       /* Check dynamic ranges */
589       for (j = 0; j < vec_len (dp->ranges); j++)
590         {
591           clib_timebase_range_t *r = dp->ranges + j;
592           f64 start0, end0;
593
594           start0 = r->start + mm->sunday_midnight;
595           end0 = r->end + mm->sunday_midnight;
596           if (verbose > 1)
597             vlib_cli_output (vm, "  Range %d: %U - %U", j,
598                              format_clib_timebase_time, start0,
599                              format_clib_timebase_time, end0);
600
601           if (now >= start0 && now <= end0)
602             {
603               if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW)
604                 current_status = 3;
605               else if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA)
606                 current_status = 5;
607               else
608                 current_status = 2;
609               if (verbose)
610                 {
611                   vlib_cli_output (vm, "  Time in range %d:", j);
612                   vlib_cli_output (vm, "     %U - %U",
613                                    format_clib_timebase_time, start0,
614                                    format_clib_timebase_time, end0);
615                 }
616               goto print;
617             }
618         }
619       if (verbose && j)
620         vlib_cli_output (vm, "  No range match.");
621       if (dp->flags & MACTIME_DEVICE_FLAG_STATIC_DROP)
622         current_status = 0;
623       if (dp->flags & MACTIME_DEVICE_FLAG_STATIC_ALLOW)
624         current_status = 1;
625       if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW)
626         current_status = 2;
627       if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_DROP)
628         current_status = 3;
629       if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA)
630         current_status = 4;
631
632     print:
633       vec_reset_length (macstring);
634       macstring = format (0, "%U", format_mac_address, dp->mac_address);
635       switch (current_status)
636         {
637         case 0:
638           status_string = "static drop";
639           break;
640         case 1:
641           status_string = "static allow";
642           break;
643         case 2:
644           status_string = "dynamic drop";
645           break;
646         case 3:
647           status_string = "dynamic allow";
648           break;
649         case 4:
650           status_string = "d-quota inact";
651           break;
652         case 5:
653           status_string = "d-quota activ";
654           break;
655         default:
656           status_string = "code bug!";
657           break;
658         }
659       vlib_get_combined_counter (&mm->allow_counters, dp - mm->devices,
660                                  &allow);
661       vlib_get_combined_counter (&mm->drop_counters, dp - mm->devices, &drop);
662       vlib_cli_output (vm, "%-15s %18s %14s %10lld %U %13lld",
663                        dp->device_name, macstring, status_string,
664                        allow.packets, format_bytes_with_width, allow.bytes,
665                        10, drop.packets);
666       if (dp->data_quota > 0)
667         vlib_cli_output (vm, "%-54s %s%U %s%U", " ", "Quota ",
668                          format_bytes_with_width, dp->data_quota, 10,
669                          "Use ", format_bytes_with_width,
670                          dp->data_used_in_range, 8);
671       /* This is really only good for small N... */
672       for (j = 0; j < vec_len (mm->arp_cache_copy); j++)
673         {
674           ipn = ip_neighbor_get (mm->arp_cache_copy[j]);
675           if (!memcmp
676               (dp->mac_address, ipn->ipn_mac.bytes, sizeof (ipn->ipn_mac)))
677             {
678               vlib_cli_output (vm, "%17s%U", " ", format_ip46_address,
679                                ip_neighbor_get_ip (ipn), IP46_TYPE_IP4);
680             }
681         }
682     }
683   vec_free (macstring);
684   vec_free (pool_indices);
685
686   return 0;
687 }
688
689 /* *INDENT-OFF* */
690 VLIB_CLI_COMMAND (show_mactime_command, static) =
691 {
692   .path = "show mactime",
693   .short_help = "show mactime [verbose]",
694   .function = show_mactime_command_fn,
695 };
696 /* *INDENT-ON* */
697
698 static clib_error_t *
699 clear_mactime_command_fn (vlib_main_t * vm,
700                           unformat_input_t * input, vlib_cli_command_t * cmd)
701 {
702   mactime_main_t *mm = &mactime_main;
703
704   if (mm->feature_initialized == 0)
705     return clib_error_return (0, "feature not enabled");
706
707   vlib_clear_combined_counters (&mm->allow_counters);
708   vlib_clear_combined_counters (&mm->drop_counters);
709   vlib_cli_output (vm, "Mactime counters cleared...");
710   return 0;
711 }
712
713 /* *INDENT-OFF* */
714 VLIB_CLI_COMMAND (clear_mactime_command, static) =
715 {
716   .path = "clear mactime",
717   .short_help = "clear mactime counters",
718   .function = clear_mactime_command_fn,
719 };
720 /* *INDENT-ON* */
721
722
723
724 /*
725  * fd.io coding-style-patch-verification: ON
726  *
727  * Local Variables:
728  * eval: (c-set-style "gnu")
729  * End:
730  */