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