diff options
author | Dave Barach <dave@barachs.net> | 2019-10-07 12:04:31 -0400 |
---|---|---|
committer | Florin Coras <florin.coras@gmail.com> | 2019-10-07 17:46:16 +0000 |
commit | 2c41a61d5fc87737b9b46b88cb9271d0f987721e (patch) | |
tree | 6ef31361cdc9d55f1be3c9d939f2a2a8c0f6a586 /src/plugins/mactime | |
parent | 0eb75d0e9c7624a4e8ac69fea7dbe12d39b75096 (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/plugins/mactime')
-rw-r--r-- | src/plugins/mactime/CMakeLists.txt | 18 | ||||
-rw-r--r-- | src/plugins/mactime/mactime.api | 66 | ||||
-rw-r--r-- | src/plugins/mactime/mactime.c | 79 | ||||
-rw-r--r-- | src/plugins/mactime/mactime.h | 22 | ||||
-rw-r--r-- | src/plugins/mactime/mactime_device.h | 53 | ||||
-rw-r--r-- | src/plugins/mactime/mactime_test.c | 220 | ||||
-rw-r--r-- | src/plugins/mactime/mactime_top.c | 505 |
7 files changed, 938 insertions, 25 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: + */ |