summaryrefslogtreecommitdiffstats
path: root/src/tools
AgeCommit message (Expand)AuthorFilesLines
2018-04-17ACL based forwardingAndrew Yourtchenko1-1/+1
2018-04-11VPPAPIGEN: Consistent CRC32 on complete file.Ole Troan1-17/+25
2018-03-08vppapigen: require reply/details messages for requests/dumpsMarek Gradzki1-6/+6
2018-03-07VPPAPIGEN: Run tool directly from source tree.Ole Troan1-1/+4
2018-03-06vppapigen: require service definition for singleton messagesMarek Gradzki1-7/+2
2018-03-06vppapigen: do not allow to define message as both request and replyMarek Gradzki1-0/+4
2018-03-06vppapigen: require reply ID different than caller IDMarek Gradzki1-0/+4
2018-03-06API: Add service definitions for events and singleton messages (second attempt)Marek Gradzki1-17/+30
2018-03-05Revert "API: Add service definitions for events and singleton messages."Ole Trøan1-30/+17
2018-03-05API: Add service definitions for events and singleton messages.Ole Troan1-17/+30
2018-03-03VPPAPIGEN: Improve output module finding for out-of-tree builds.Ole Troan1-2/+12
2018-02-09vppapigen: simplify JSON format for servicesMarek Gradzki1-2/+2
2018-01-23VPPAPIGEN: vppapigen replacement in Python PLY.Ole Troan10-2980/+1171
2017-12-14Fix display of single-event event-logsDave Barach2-1/+8
2017-10-09vppapigen: support per-file (major,minor,patch) version stampsDave Barach4-4/+79
2017-10-04[aarch64] Fixes CLI crashes on dpaa2 platform.Christophe Fontaine1-1/+1
2017-05-10Allow to override the build dateBernhard M. Wiedemann1-1/+6
2017-05-09Fix remaining 32-bit compile issuesDamjan Marion2-5/+5
2017-04-25"autoreply" flag: autogenerate standard xxx_reply_t messagesDave Barach5-3/+65
2017-04-04Clear pid-vector, to avoid spurious track selectionDave Barach2-4/+4
2017-04-03g2: add multi-track time-slew controlsDave Barach3-15/+357
2017-04-01Clean up event log merge codeDave Barach1-2/+5
2017-03-25g2, c2cpel, and cpeldump: scale to 8M event log filesDave Barach3-0/+9
2017-03-02VPP-608: fix coverity warning in vppapigenDave Barach1-2/+2
2017-02-28Fix warning in generated codeDave Barach1-4/+14
2017-01-01Move java,lua api and remaining plugins to src/Damjan Marion3-38/+0
2016-12-28Reorganize source tree to use single autotools instanceDamjan Marion36-0/+14621
gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #75715e } /* Generic.Subheading */ .highlight .kc { color: #66d9ef } /* Keyword.Constant */ .highlight .kd { color: #66d9ef } /* Keyword.Declaration */ .highlight .kn { color: #f92672 } /* Keyword.Namespace */ .highlight .kp { color: #66d9ef } /* Keyword.Pseudo */ .highlight .kr { color: #66d9ef } /* Keyword.Reserved */ .highlight .kt { color: #66d9ef } /* Keyword.Type */ .highlight .ld { color: #e6db74 } /* Literal.Date */ .highlight .m { color: #ae81ff } /* Literal.Number */ .highlight .s { color: #e6db74 } /* Literal.String */ .highlight .na { color: #a6e22e } /* Name.Attribute */ .highlight .nb { color: #f8f8f2 } /* Name.Builtin */ .highlight .nc { color: #a6e22e } /* Name.Class */ .highlight .no { color: #66d9ef } /* Name.Constant */ .highlight .nd { color: #a6e22e } /* Name.Decorator */ .highlight .ni { color: #f8f8f2 } /* Name.Entity */ .highlight .ne { color: #a6e22e } /* Name.Exception */ .highlight .nf { color: #a6e22e } /* Name.Function */ .highlight .nl { color: #f8f8f2 } /* Name.Label */ .highlight .nn { color: #f8f8f2 } /* Name.Namespace */ .highlight .nx { color: #a6e22e } /* Name.Other */ .highlight .py { color: #f8f8f2 } /* Name.Property */ .highlight .nt { color: #f92672 } /* Name.Tag */ .highlight .nv { color: #f8f8f2 } /* Name.Variable */ .highlight .ow { color: #f92672 } /* Operator.Word */ .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ .highlight .mb { color: #ae81ff } /* Literal.Number.Bin */ .highlight .mf { color: #ae81ff } /* Literal.Number.Float */ .highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ .highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ .highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ .highlight .sa { color: #e6db74 } /* Literal.String.Affix */ .highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ .highlight .sc { color: #e6db74 } /* Literal.String.Char */ .highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */ .highlight .sd { color: #e6db74 } /* Literal.String.Doc */ .highlight .s2 { color: #e6db74 } /* Literal.String.Double */ .highlight .se { color: #ae81ff } /* Literal.String.Escape */ .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ .highlight .si { color: #e6db74 } /* Literal.String.Interpol */ .highlight .sx { color: #e6db74 } /* Literal.String.Other */ .highlight .sr { color: #e6db74 } /* Literal.String.Regex */ .highlight .s1 { color: #e6db74 } /* Literal.String.Single */ .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #a6e22e } /* Name.Function.Magic */ .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ } @media (prefers-color-scheme: light) { .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }
#include <vppinfra/time.h>
#include <vppinfra/hash.h>
#include <vppinfra/pool.h>
#include <vpp-api/client/stat_client.h>
#include <vppinfra/vec.h>
#include <mactime/mactime_device.h>
#include <vlibapi/api_common.h>
#include <vlibmemory/memory_client.h>
#include <vlibmemory/api.h>
#include <vnet/api_errno.h>
#include <svm/queue.h>

#include <vnet/format_fns.h>
#include <mactime/mactime.api_types.h>
#include <mactime/mactime.api_enum.h>

typedef struct
{
  /* device database */
  uword *device_by_device_name;
  mactime_device_t *devices;
  u32 my_table_epoch;

  /* Stat segment variables */
  stat_client_main_t *stat_client_main;
  u8 **pattern1, **pattern2;
  u32 *ls_result1, *ls_result2;
  vlib_counter_t *allow_counters;
  vlib_counter_t *drop_counters;

  /* Timebase */
  clib_time_t clib_time;
  clib_timebase_t timebase;
  f64 timezone_offset;
  f64 sunday_midnight;

  /* API message-handling */
  svm_queue_t *vl_input_queue;
  u32 my_client_index;
  u16 msg_id_base;
  volatile u32 result_ready;
  volatile i32 retval;
} mt_main_t;

mt_main_t mt_main;

/* Indispensable for debugging in gdb... */

u32
vl (void *x)
{
  return vec_len (x);
}

#define foreach_mactime_api_msg                 \
_(MACTIME_DUMP_REPLY, mactime_dump_reply)       \
_(MACTIME_DETAILS, mactime_details)

static void vl_api_mactime_dump_reply_t_handler
  (vl_api_mactime_dump_reply_t * mp)
{
  mt_main_t *mm = &mt_main;
  i32 retval = clib_net_to_host_u32 (mp->retval);

  mm->retval = retval;
  mm->result_ready = 1;
}

static void
vl_api_mactime_details_t_handler (vl_api_mactime_details_t * mp)
{
  mt_main_t *mm = &mt_main;
  mactime_device_t *dev;
  int i;
  clib_timebase_range_t *rp;
  uword *p;

  if (PREDICT_FALSE (mm->device_by_device_name == 0))
    mm->device_by_device_name = hash_create_string (0, sizeof (uword));

  p = hash_get_mem (mm->device_by_device_name, mp->device_name);
  if (p)
    dev = pool_elt_at_index (mm->devices, p[0]);
  else
    {
      u8 *hash_name_copy = format (0, "%s%c", mp->device_name, 0);
      pool_get (mm->devices, dev);
      memset (dev, 0, sizeof (*dev));
      dev->device_name = vec_dup (hash_name_copy);
      hash_set_mem (mm->device_by_device_name, hash_name_copy,
		    dev - mm->devices);
    }

  clib_memcpy_fast (dev->mac_address, mp->mac_address,
		    sizeof (dev->mac_address));
  dev->data_quota = clib_net_to_host_u64 (mp->data_quota);
  dev->data_used_in_range = clib_net_to_host_u64 (mp->data_used_in_range);
  dev->flags = clib_net_to_host_u32 (mp->flags);
  dev->pool_index = clib_net_to_host_u32 (mp->pool_index);
  vec_reset_length (dev->ranges);
  for (i = 0; i < clib_net_to_host_u32 (mp->nranges); i++)
    {
      vec_add2 (dev->ranges, rp, 1);
      rp->start = mp->ranges[i].start;
      rp->end = mp->ranges[i].end;
    }
}

#define vl_print(handle, ...) fformat(handle, __VA_ARGS__)
#define vl_endianfun		/* define message structures */
#include <mactime/mactime.api.h>
#undef vl_endianfun

/* instantiate all the print functions we know about */
#define vl_printfun
#include <mactime/mactime.api.h>
#undef vl_printfun

#define vl_api_version(n,v) static u32 api_version = v;
#include <mactime/mactime.api.h>
#undef vl_api_version

static int
connect_to_vpp (char *name)
{
  api_main_t *am = vlibapi_get_main ();
  mt_main_t *mm = &mt_main;
  u8 *msg_base_lookup_name;

  if (vl_client_connect_to_vlib ("/vpe-api", name, 32) < 0)
    return -1;

  mm->vl_input_queue = am->shmem_hdr->vl_input_queue;
  mm->my_client_index = am->my_client_index;

  msg_base_lookup_name = format (0, "mactime_%08x%c", api_version, 0);

  mm->msg_id_base = vl_client_get_first_plugin_msg_id
    ((char *) msg_base_lookup_name);

  vec_free (msg_base_lookup_name);

  if (mm->msg_id_base == (u16) ~ 0)
    return -1;

#define _(N,n)                                                  \
    vl_msg_api_set_handlers((VL_API_##N + mm->msg_id_base),     \
                           #n,					\
                           vl_api_##n##_t_handler,              \
                           vl_noop_handler,                     \
                           vl_api_##n##_t_endian,               \
                           vl_api_##n##_t_print,                \
                           sizeof(vl_api_##n##_t), 1);
  foreach_mactime_api_msg;
#undef _

  return 0;
}

static void
dump_mactime_table (mt_main_t * mm)
{
  vl_api_mactime_dump_t *mp;
  u32 deadman_counter = 1000;

  /* Send the dump request */
  mp = vl_msg_api_alloc (sizeof (*mp));
  memset (mp, 0, sizeof (*mp));
  mp->_vl_msg_id =
    clib_host_to_net_u16 (VL_API_MACTIME_DUMP + mm->msg_id_base);
  mp->client_index = mm->my_client_index;
  mp->my_table_epoch = mm->my_table_epoch;
  vl_msg_api_send_shmem (mm->vl_input_queue, (u8 *) & mp);

  /* Wait up to 1 second for vpp to reply */
  while (deadman_counter-- && mm->result_ready == 0)
    unix_sleep (1e-3);

  if (mm->retval && (mm->retval != VNET_API_ERROR_NO_CHANGE))
    clib_warning ("dump reply %d", mm->retval);

}

static void
scrape_stats_segment (mt_main_t * mm)
{
  vlib_counter_t **counters_by_thread;
  vlib_counter_t *counters;
  mactime_device_t *dev;
  stat_segment_access_t sa;
  stat_client_main_t *sm = mm->stat_client_main;
  stat_segment_directory_entry_t *ep;
  int need_update2 = 0;
  static u32 *pool_indices;
  int i, j;

  vec_reset_length (pool_indices);
  /* *INDENT-OFF* */
  pool_foreach (dev, mm->devices)
   {
    vec_add1 (pool_indices, dev->pool_index);
  }
  /* *INDENT-ON* */

  /* Nothing to do... */
  if (vec_len (pool_indices) == 0)
    return;

again1:

  /* Has directory been updated? */
  if (mm->ls_result1 == 0 || (sm->shared_header->epoch != sm->current_epoch))
    {
      need_update2 = 1;
      vec_free (mm->ls_result1);
      mm->ls_result1 = stat_segment_ls (mm->pattern1);
    }

  stat_segment_access_start (&sa, sm);

  ep = vec_elt_at_index (sm->directory_vector, mm->ls_result1[0]);
  counters_by_thread = stat_segment_adjust (sm, ep->data);

  for (i = 0; i < vec_len (pool_indices); i++)
    {
      u32 index = pool_indices[i];

      vec_validate (mm->allow_counters, index);
      mm->allow_counters[index].packets = 0;
      mm->allow_counters[index].bytes = 0;

      for (j = 0; j < vec_len (counters_by_thread); j++)
	{
	  counters = stat_segment_adjust (sm, counters_by_thread[j]);
	  mm->allow_counters[index].packets += counters[index].packets;
	  mm->allow_counters[index].bytes += counters[index].bytes;
	}
    }

  /* Ugh, segment changed during access. Try again */
  if (stat_segment_access_end (&sa, sm))
    goto again1;

  /* Has directory been updated? */
  if (mm->ls_result2 == 0 || need_update2)
    {
      vec_free (mm->ls_result2);
      mm->ls_result2 = stat_segment_ls (mm->pattern2);
    }

again2:
  stat_segment_access_start (&sa, sm);

  ep = vec_elt_at_index (sm->directory_vector, mm->ls_result2[0]);
  counters_by_thread = stat_segment_adjust (sm, ep->data);

  for (i = 0; i < vec_len (pool_indices); i++)
    {
      u32 index = pool_indices[i];

      vec_validate (mm->drop_counters, index);
      mm->drop_counters[index].packets = 0;
      mm->drop_counters[index].bytes = 0;

      for (j = 0; j < vec_len (counters_by_thread); j++)
	{
	  counters = stat_segment_adjust (sm, counters_by_thread[j]);
	  mm->drop_counters[index].packets += counters[index].packets;
	  mm->drop_counters[index].bytes += counters[index].bytes;
	}
    }
  /* Ugh, segment changed during access. Try again */
  if (stat_segment_access_end (&sa, sm))
    goto again2;
}

static u8 *
format_mac_address (u8 * s, va_list * args)
{
  u8 *a = va_arg (*args, u8 *);

  return format (s, "%02x:%02x:%02x:%02x:%02x:%02x",
		 a[0], a[1], a[2], a[3], a[4], a[5]);
}

static u8 *
format_bytes_with_width (u8 * s, va_list * va)
{
  uword nbytes = va_arg (*va, u64);
  int width = va_arg (*va, int);
  f64 nbytes_f64;
  u8 *fmt;
  char *suffix = "";

  if (width > 0)
    fmt = format (0, "%%%d.3f%%s%c", width, 0);
  else
    fmt = format (0, "%%.3f%%s%c", 0);

  if (nbytes > (1024ULL * 1024ULL * 1024ULL))
    {
      nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0 * 1024.0);
      suffix = "G";
    }
  else if (nbytes > (1024ULL * 1024ULL))
    {
      nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0);
      suffix = "M";
    }
  else if (nbytes > 1024ULL)
    {
      nbytes_f64 = ((f64) nbytes) / (1024.0);
      suffix = "K";
    }
  else
    {
      nbytes_f64 = (f64) nbytes;
      suffix = "B";
    }

  s = format (s, (char *) fmt, nbytes_f64, suffix);
  vec_free (fmt);
  return s;
}

static u8 *
format_device (u8 * s, va_list * args)
{
  mactime_device_t *dp = va_arg (*args, mactime_device_t *);
  mt_main_t *mm = &mt_main;
  int verbose = va_arg (*args, int);
  int current_status = 99;
  char *status_string;
  u8 *macstring = 0;
  f64 now;
  int j;

  if (dp == 0)
    {
      s = format (s, "%-15s %5s %18s %14s %10s %11s %13s",
		  "Device Name", "Index", "Addresses", "Status",
		  "AllowPkt", "AllowByte", "DropPkt");
      vec_add1 (s, '\n');
      return s;
    }

  now = clib_timebase_now (&mm->timebase);

  if (PREDICT_FALSE ((now - mm->sunday_midnight) > 86400.0 * 7.0))
    mm->sunday_midnight = clib_timebase_find_sunday_midnight (now);

  /* Check dynamic ranges */
  for (j = 0; j < vec_len (dp->ranges); j++)
    {
      clib_timebase_range_t *r = dp->ranges + j;
      f64 start0, end0;

      start0 = r->start + mm->sunday_midnight;
      end0 = r->end + mm->sunday_midnight;
      if (verbose)
	s = format (s, "  Range %d: %U - %U\n", j,
		    format_clib_timebase_time, start0,
		    format_clib_timebase_time, end0);

      if (now >= start0 && now <= end0)
	{
	  if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW)
	    current_status = 3;
	  else if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA)
	    current_status = 5;
	  else
	    current_status = 2;
	  if (verbose)
	    {
	      s = format (s, "  Time in range %d:", j);
	      s = format (s, "     %U - %U\n",
			  format_clib_timebase_time, start0,
			  format_clib_timebase_time, end0);
	    }
	  goto print;
	}
    }
  if (verbose && j)
    s = format (s, "  No range match.\n");
  if (dp->flags & MACTIME_DEVICE_FLAG_STATIC_DROP)
    current_status = 0;
  if (dp->flags & MACTIME_DEVICE_FLAG_STATIC_ALLOW)
    current_status = 1;
  if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW)
    current_status = 2;
  if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_DROP)
    current_status = 3;
  if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA)
    current_status = 4;

print:
  macstring = format (0, "%U", format_mac_address, dp->mac_address);
  switch (current_status)
    {
    case 0:
      status_string = "static drop";
      break;
    case 1:
      status_string = "static allow";
      break;
    case 2:
      status_string = "dynamic drop";
      break;
    case 3:
      status_string = "dynamic allow";
      break;
    case 4:
      status_string = "d-quota inact";
      break;
    case 5:
      status_string = "d-quota activ";
      break;
    default:
      status_string = "code bug!";
      break;
    }

  s = format (s, "%-15s %5d %18s %14s %10lld %U %13lld\n",
	      dp->device_name, dp->pool_index, macstring, status_string,
	      mm->allow_counters[dp->pool_index].packets,
	      format_bytes_with_width,
	      mm->allow_counters[dp->pool_index].bytes, 10,
	      mm->drop_counters[dp->pool_index].packets);
  vec_free (macstring);

  if (dp->data_quota > 0)
    {
      s = format (s, "%-59s %s%U %s%U", " ", "Quota ",
		  format_bytes_with_width, dp->data_quota, 10,
		  "Use ", format_bytes_with_width, dp->data_used_in_range, 8);
      vec_add1 (s, '\n');
    }
  return s;
}

static void
print_device_table (mt_main_t * mm)
{
  mactime_device_t *dev;

  fformat (stdout, "%U", format_device, 0 /* header */ , 0 /* verbose */ );
  /* *INDENT-OFF* */
  pool_foreach (dev, mm->devices)
   {
    fformat (stdout, "%U", format_device, dev, 0 /* verbose */);
  }
  /* *INDENT-ON* */
}

int
main (int argc, char **argv)
{
  mt_main_t *mm = &mt_main;
  extern stat_client_main_t stat_client_main;

  clib_mem_init (0, 64 << 20);

  if (connect_to_vpp ("mactime_top") < 0)
    {
      fformat (stderr, "vpp api client connect error\n");
      exit (1);
    }

  if (stat_segment_connect (argv[1]) < 0)
    {
      fformat (stderr, "stat segment connect error");
      exit (1);
    }

  mm->stat_client_main = (stat_client_main_t *) & stat_client_main;

  /* US EDT - $$$ FIXME */
  clib_time_init (&mm->clib_time);
  mm->timezone_offset = -5.0;
  clib_timebase_init (&mm->timebase, mm->timezone_offset,
		      CLIB_TIMEBASE_DAYLIGHT_USA,
		      0 /* allocate a clib_time_t */ );

  vec_add1 (mm->pattern1, (u8 *) "^/mactime/allow");
  vec_add1 (mm->pattern2, (u8 *) "^/mactime/drop");

  while (1)
    {
      dump_mactime_table (mm);
      scrape_stats_segment (mm);
      print_device_table (mm);
      unix_sleep (5.0);
    }
  return 0;
}


/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */