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