aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDave Barach <dave@barachs.net>2019-10-07 12:04:31 -0400
committerFlorin Coras <florin.coras@gmail.com>2019-10-07 17:46:16 +0000
commit2c41a61d5fc87737b9b46b88cb9271d0f987721e (patch)
tree6ef31361cdc9d55f1be3c9d939f2a2a8c0f6a586 /src
parent0eb75d0e9c7624a4e8ac69fea7dbe12d39b75096 (diff)
mactime: add a "top" command to watch device stats
Include a binary API change NOT suitable for cherry-picking into 19.08 Type: feature Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: Id369514a3085f5e4bcee34819c55c4636df9b518
Diffstat (limited to 'src')
-rw-r--r--src/plugins/mactime/CMakeLists.txt18
-rw-r--r--src/plugins/mactime/mactime.api66
-rw-r--r--src/plugins/mactime/mactime.c79
-rw-r--r--src/plugins/mactime/mactime.h22
-rw-r--r--src/plugins/mactime/mactime_device.h53
-rw-r--r--src/plugins/mactime/mactime_test.c220
-rw-r--r--src/plugins/mactime/mactime_top.c505
-rw-r--r--src/vnet/api_errno.h4
8 files changed, 941 insertions, 26 deletions
diff --git a/src/plugins/mactime/CMakeLists.txt b/src/plugins/mactime/CMakeLists.txt
index ac5077d1860..5477674f22a 100644
--- a/src/plugins/mactime/CMakeLists.txt
+++ b/src/plugins/mactime/CMakeLists.txt
@@ -22,3 +22,21 @@ add_vpp_plugin(mactime
API_TEST_SOURCES
mactime_test.c
)
+
+option(VPP_BUILD_MACTIME_TOP "Build mactime plugin 'top' tool" OFF)
+if(VPP_BUILD_MACTIME_TOP)
+ add_vpp_executable(mactime_top ENABLE_EXPORTS
+ SOURCES
+ mactime_top.c
+
+ DEPENDS api_headers
+
+ LINK_LIBRARIES
+ vlibmemoryclient
+ vppapiclient
+ svm
+ vppinfra
+ Threads::Threads
+ rt m dl crypto
+ )
+endif()
diff --git a/src/plugins/mactime/mactime.api b/src/plugins/mactime/mactime.api
index edad2c48362..224f07101c6 100644
--- a/src/plugins/mactime/mactime.api
+++ b/src/plugins/mactime/mactime.api
@@ -13,12 +13,12 @@
* limitations under the License.
*/
-/** \file
+/** @file
This file defines vpp mactime control-plane API messages
*/
-option version = "1.1.1";
+option version = "1.2.1";
-/** \brief api to enable or disable the time-based src mac filter on
+/** @brief api to enable or disable the time-based src mac filter on
an interface
*/
@@ -31,7 +31,7 @@ autoreply define mactime_enable_disable
option vat_help = "<intfc> [disable]";
};
-/** \brief a time range structure
+/** @brief a time range structure
* times are in double-precision fp seconds since 1/1/1970,
* which was a Thursday.
*/
@@ -41,7 +41,7 @@ typedef time_range
f64 end; /**< end of the time range */
};
-/** \brief configure per src-mac time ranges
+/** @brief configure per src-mac time ranges
*
* Usage:
* to create a static allow entry:
@@ -56,6 +56,10 @@ typedef time_range
* set each range start/end in seconds since Sunday began
* As in: start/end >= 0.0 && start/end < 7.0 *86400.0
*
+ * to create a (time-range-based) dynamic allow entry with quota:
+ * Outside of stated time ranges, such entries revert to allow with no quota.
+ * previous setup, s/allow=1/allow_quota=1/
+ *
* to create a (time-range-based) dynamic drop entry:
* Same procedure to create a dynamic allow entry,
* set drop=1 instead of allow=1
@@ -86,6 +90,58 @@ autoreply define mactime_add_del_range
option vat_help = "name <devname> mac <mac-addr> allow drop allow-range Mon - Fri 9:00 - 17:00";
};
+/** @brief a time range, in fp seconds since Sunday midnight
+ */
+
+typedef mactime_time_range
+{
+ f64 start;
+ f64 end;
+};
+
+/** @brief dump mactime table
+ *
+ * Request a mactime client pool dump
+ * Sequence:
+ * client send vl_api_mactime_dump to vpp
+ * vpp replies with zero or more vl_api_mactime_entry_t's
+ * vpp replies with a vl_api_mactime_dump_reply_t
+ * @param my_table_epoch dump table only if update needed, 0 => full dump
+ */
+
+define mactime_dump
+{
+ u32 client_index; /**< client index, from api_main */
+ u32 context; /**< application context */
+ u32 my_table_epoch; /**< to suppress dump if no changes */
+};
+
+/** @brief mactime table entry details
+ */
+
+define mactime_details
+{
+ u32 context;
+ u32 pool_index;
+ u8 mac_address[6];
+ u64 data_quota;
+ u64 data_used_in_range;
+ u32 flags;
+ u8 device_name[64];
+ u32 nranges;
+ vl_api_mactime_time_range_t ranges[nranges];
+};
+
+/** @brief dump mactime table reply
+ * Includes the vpp table epoch, needed to optimize API traffic
+ */
+define mactime_dump_reply
+{
+ u32 context;
+ i32 retval;
+ u32 table_epoch;
+};
+
/*
* Local Variables:
* eval: (c-set-style "gnu")
diff --git a/src/plugins/mactime/mactime.c b/src/plugins/mactime/mactime.c
index e346171bd42..502ac98b723 100644
--- a/src/plugins/mactime/mactime.c
+++ b/src/plugins/mactime/mactime.c
@@ -158,6 +158,77 @@ static void vl_api_mactime_enable_disable_t_handler
REPLY_MACRO (VL_API_MACTIME_ENABLE_DISABLE_REPLY);
}
+static void
+vl_api_mactime_dump_t_handler (vl_api_mactime_dump_t * mp)
+{
+ vl_api_mactime_details_t *ep;
+ vl_api_mactime_dump_reply_t *rmp;
+ mactime_device_t *dev;
+ mactime_main_t *mm = &mactime_main;
+ vl_api_registration_t *rp;
+ int rv = 0, i;
+ u32 his_table_epoch = clib_net_to_host_u32 (mp->my_table_epoch);
+ u32 message_size;
+ u32 name_len;
+ u32 nranges;
+
+ rp = vl_api_client_index_to_registration (mp->client_index);
+ if (rp == 0)
+ return;
+
+ if (his_table_epoch == mm->device_table_epoch)
+ {
+ rv = VNET_API_ERROR_NO_CHANGE;
+ goto send_reply;
+ }
+
+ /* *INDENT-OFF* */
+ pool_foreach (dev, mm->devices,
+ ({
+ message_size = sizeof(*ep) + vec_len(dev->device_name) +
+ vec_len(dev->ranges) * sizeof(ep->ranges[0]);
+
+ ep = vl_msg_api_alloc (message_size);
+ memset (ep, 0, message_size);
+ ep->_vl_msg_id = clib_host_to_net_u16 (VL_API_MACTIME_DETAILS
+ + mm->msg_id_base);
+ /* Index is the key for the stats segment combined counters */
+ ep->pool_index = clib_host_to_net_u32 (dev - mm->devices);
+
+ clib_memcpy_fast (ep->mac_address, dev->mac_address,
+ sizeof (ep->mac_address));
+ ep->data_quota = clib_host_to_net_u64 (dev->data_quota);
+ ep->data_used_in_range = clib_host_to_net_u64 (dev->data_used_in_range);
+ ep->flags = clib_host_to_net_u32 (dev->flags);
+ nranges = vec_len (dev->ranges);
+ ep->nranges = clib_host_to_net_u32 (nranges);
+
+ for (i = 0; i < vec_len (dev->ranges); i++)
+ {
+ ep->ranges[i].start = dev->ranges[i].start;
+ ep->ranges[i].end = dev->ranges[i].end;
+ }
+
+ name_len = vec_len (dev->device_name);
+ name_len = (name_len < ARRAY_LEN(ep->device_name)) ?
+ name_len : ARRAY_LEN(ep->device_name) - 1;
+
+ clib_memcpy_fast (ep->device_name, dev->device_name,
+ name_len);
+ ep->device_name [ARRAY_LEN(ep->device_name) -1] = 0;
+ vl_api_send_msg (rp, (u8 *)ep);
+ }));
+ /* *INDENT-OFF* */
+
+ send_reply:
+ /* *INDENT-OFF* */
+ REPLY_MACRO2 (VL_API_MACTIME_DUMP_REPLY,
+ ({
+ rmp->table_epoch = clib_host_to_net_u32 (mm->device_table_epoch);
+ }));
+ /* *INDENT-ON* */
+}
+
/** Create a lookup table entry for the indicated mac address
*/
void
@@ -201,6 +272,14 @@ static void vl_api_mactime_add_del_range_t_handler
feature_init (mm);
+ /*
+ * Change the table epoch. Skip 0 so clients can code my_table_epoch = 0
+ * to receive a full dump.
+ */
+ mm->device_table_epoch++;
+ if (PREDICT_FALSE (mm->device_table_epoch == 0))
+ mm->device_table_epoch++;
+
data_quota = clib_net_to_host_u64 (mp->data_quota);
clib_memset (&kv, 0, sizeof (kv));
diff --git a/src/plugins/mactime/mactime.h b/src/plugins/mactime/mactime.h
index 11e33c1122b..2ce1cf97c72 100644
--- a/src/plugins/mactime/mactime.h
+++ b/src/plugins/mactime/mactime.h
@@ -29,26 +29,7 @@
#include <vppinfra/time_range.h>
#include <vppinfra/bihash_8_8.h>
-#define MACTIME_RANGE_TYPE_DROP 0
-#define MACTIME_RANGE_TYPE_ALLOW 1
-
-typedef struct
-{
- u8 *device_name;
- u8 mac_address[6];
- u64 data_quota;
- u64 data_used_in_range;
- u32 flags;
- clib_timebase_range_t *ranges;
-} mactime_device_t;
-
-/** Always drop packets from this device */
-#define MACTIME_DEVICE_FLAG_STATIC_DROP (1<<0)
-#define MACTIME_DEVICE_FLAG_STATIC_ALLOW (1<<1)
-#define MACTIME_DEVICE_FLAG_DYNAMIC_DROP (1<<2)
-#define MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW (1<<3)
-#define MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA (1<<4)
-#define MACTIME_DEVICE_FLAG_DROP_UDP_10001 (1<<5)
+#include <mactime/mactime_device.h>
typedef struct
{
@@ -75,6 +56,7 @@ typedef struct
/* Device table */
mactime_device_t *devices;
+ u32 device_table_epoch;
/* Counters */
vlib_combined_counter_main_t allow_counters;
diff --git a/src/plugins/mactime/mactime_device.h b/src/plugins/mactime/mactime_device.h
new file mode 100644
index 00000000000..d747f096dc1
--- /dev/null
+++ b/src/plugins/mactime/mactime_device.h
@@ -0,0 +1,53 @@
+/*
+ * mactime_device.h - device table entry
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef included_mactime_device_h
+#define included_mactime_device_h
+#include <vppinfra/time_range.h>
+
+#define MACTIME_RANGE_TYPE_DROP 0
+#define MACTIME_RANGE_TYPE_ALLOW 1
+
+typedef struct
+{
+ u8 *device_name;
+ u8 mac_address[6];
+ u64 data_quota;
+ u64 data_used_in_range;
+ u32 flags;
+ u32 pool_index;
+ f64 last_seen;
+ clib_timebase_range_t *ranges;
+} mactime_device_t;
+
+/** Always drop packets from this device */
+#define MACTIME_DEVICE_FLAG_STATIC_DROP (1<<0)
+#define MACTIME_DEVICE_FLAG_STATIC_ALLOW (1<<1)
+#define MACTIME_DEVICE_FLAG_DYNAMIC_DROP (1<<2)
+#define MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW (1<<3)
+#define MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA (1<<4)
+#define MACTIME_DEVICE_FLAG_DROP_UDP_10001 (1<<5)
+
+#endif /* included_mactime_device_h */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/mactime/mactime_test.c b/src/plugins/mactime/mactime_test.c
index c13ae8c6182..f7f4600efad 100644
--- a/src/plugins/mactime/mactime_test.c
+++ b/src/plugins/mactime/mactime_test.c
@@ -20,6 +20,8 @@
#include <vppinfra/error.h>
#include <vppinfra/time_range.h>
#include <vnet/ethernet/ethernet.h>
+#include <mactime/mactime_device.h>
+#include <vpp-api/client/stat_client.h>
/* Declare message IDs */
#include <mactime/mactime.api_enum.h>
@@ -27,6 +29,16 @@
typedef struct
{
+ /* device table */
+ mactime_device_t *devices;
+ uword *device_by_device_name;
+ u32 vpp_table_epoch;
+
+ /* time range setup */
+ f64 sunday_midnight;
+ clib_timebase_t timebase;
+ f64 timezone_offset;
+
/* API message ID base */
u16 msg_id_base;
vat_main_t *vat_main;
@@ -78,6 +90,206 @@ api_mactime_enable_disable (vat_main_t * vam)
return ret;
}
+#if VPP_API_TEST_BUILTIN
+extern u8 *format_bytes_with_width (u8 * s, va_list * va);
+#else
+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;
+}
+#endif
+
+static u8 *
+format_device (u8 * s, va_list * args)
+{
+ mactime_device_t *dp = va_arg (*args, mactime_device_t *);
+ mactime_test_main_t *mm = &mactime_test_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);
+
+ /* 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\n",
+ dp->device_name, dp->pool_index, macstring, status_string);
+ 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 int
+api_mactime_dump (vat_main_t * vam)
+{
+ mactime_test_main_t *tm = &mactime_test_main;
+ unformat_input_t *i = vam->input;
+ vl_api_mactime_dump_t *mp;
+ int verbose = 0;
+ int ret;
+ f64 now;
+ mactime_device_t *dev;
+
+ now = clib_timebase_now (&tm->timebase);
+
+ if (PREDICT_FALSE ((now - tm->sunday_midnight) > 86400.0 * 7.0))
+ tm->sunday_midnight = clib_timebase_find_sunday_midnight (now);
+
+ /* Parse args required to build the message */
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "force"))
+ tm->vpp_table_epoch = 0;
+ else if (unformat (i, "verbose"))
+ verbose = 1;
+ else
+ break;
+ }
+
+ /* Construct the API message */
+ M (MACTIME_DUMP, mp);
+ mp->my_table_epoch = clib_host_to_net_u32 (tm->vpp_table_epoch);
+
+ /* send it... */
+ S (mp);
+
+ /* Wait for a reply... */
+ W (ret);
+
+ fformat (vam->ofp, "%U", format_device, 0 /* header */ , 0 /* verbose */ );
+ /* *INDENT-OFF* */
+ pool_foreach (dev, tm->devices,
+ ({
+ fformat (vam->ofp, "%U", format_device, dev, verbose);
+ }));
+ /* *INDENT-ON* */
+
+ return ret;
+}
+
/* These two ought to be in a library somewhere but they aren't */
static uword
my_unformat_mac_address (unformat_input_t * input, va_list * args)
@@ -212,6 +424,14 @@ api_mactime_add_del_range (vat_main_t * vam)
return ret;
}
+/* We shouldn't get these... */
+static void
+vl_api_mactime_details_t_handler (vl_api_mactime_details_t * mp)
+{
+ clib_warning ("WARNING: stub called...");
+}
+
+
#include <mactime/mactime.api_test.c>
/*
diff --git a/src/plugins/mactime/mactime_top.c b/src/plugins/mactime/mactime_top.c
new file mode 100644
index 00000000000..df7c755111f
--- /dev/null
+++ b/src/plugins/mactime/mactime_top.c
@@ -0,0 +1,505 @@
+#include <vppinfra/time.h>
+#include <vppinfra/hash.h>
+#include <vppinfra/pool.h>
+#include <vpp/stats/stat_segment.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>
+
+/* define message IDs */
+#include <mactime/mactime.api_enum.h>
+#include <mactime/mactime.api_types.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 vl_printfun
+#define vl_api_version(n,v) static u32 api_version = v;
+#include <mactime/mactime.api.h>
+#undef vl_api_version
+#undef vl_printfun
+#undef vl_endianfun
+
+static int
+connect_to_vpp (char *name)
+{
+ api_main_t *am = &api_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 == ~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;
+ u64 *offset_vector;
+ 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_pointer (sm->shared_header, ep->offset);
+ offset_vector = stat_segment_pointer (sm->shared_header, ep->offset_vector);
+
+ 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_pointer (sm->shared_header,
+ offset_vector[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_pointer (sm->shared_header, ep->offset);
+ offset_vector = stat_segment_pointer (sm->shared_header, ep->offset_vector);
+
+ 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_pointer (sm->shared_header,
+ offset_vector[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);
+
+ 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:
+ */
diff --git a/src/vnet/api_errno.h b/src/vnet/api_errno.h
index 0702fd7ec9e..a3451421c50 100644
--- a/src/vnet/api_errno.h
+++ b/src/vnet/api_errno.h
@@ -150,7 +150,9 @@ _(INVALID_ALGORITHM, -154, "Invalid Algorithm") \
_(RSRC_IN_USE, -155, "Resource In Use") \
_(KEY_LENGTH, -156, "invalid Key Length") \
_(FIB_PATH_UNSUPPORTED_NH_PROTO, -157, "Unsupported FIB Path protocol") \
-_(API_ENDIAN_FAILED, -159, "Endian mismatch detected")
+_(API_ENDIAN_FAILED, -159, "Endian mismatch detected") \
+_(NO_CHANGE, -160, "No change in table")
+
typedef enum
{
#define _(a,b,c) VNET_API_ERROR_##a = (b),