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