@@ -1,5 +1,6 @@
openflowincludedir = $(includedir)/openflow
openflowinclude_HEADERS = \
+ include/openflow/intel-ext.h \
include/openflow/netronome-ext.h \
include/openflow/nicira-ext.h \
include/openflow/openflow-1.0.h \
new file mode 100644
@@ -0,0 +1,63 @@
+/*
+ * Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *
+ * 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 OPENFLOW_INTEL_EXT_H
+#define OPENFLOW_INTEL_EXT_H 1
+
+
+/* This file presents Intel vendor extension. It is not anyhow
+ * standardized, so all those definitions are not part of
+ * official openflow headers (openflow.h). Nevertheless below
+ * features introduces real value so it could be suitable for
+ * standardization */
+
+
+
+/* Intel extended statistics type */
+
+enum intel_port_stats_subtype {
+ INTEL_PORT_STATS_RFC2819 = 1,
+};
+
+#define INTEL_PORT_STATS_RFC2819_SIZE 64
+
+struct intel_port_stats_rfc2819 {
+ ovs_be16 type; /* OFPPSPT14_EXPERIMENTER. */
+ ovs_be16 length; /* Length in bytes of this property
+ * excluding trailing padding. */
+ ovs_be32 experimenter; /* INTEL_VENDOR_ID. */
+ ovs_be32 exp_type; /* INTEL_PORT_STATS_*. */
+
+ /* Followed by:
+ * - Exactly (length - 12) bytes containing the experimenter data:
+ * Below counters are just example used in RFC patch based on RFC2819.
+ * This list can be extended during final patch implementation. */
+ uint8_t pad[4];
+
+ ovs_be64 rx_64_packets;
+ ovs_be64 rx_65_to_127_packets;
+ ovs_be64 rx_128_to_255_packets;
+ ovs_be64 rx_256_to_511_packets;
+ ovs_be64 rx_512_to_1023_packets;
+ ovs_be64 rx_1024_to_1518_packets;
+
+ /*, then
+ * - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+ * bytes of all-zero bytes */
+};
+OFP_ASSERT(sizeof(struct intel_port_stats_rfc2819) == INTEL_PORT_STATS_RFC2819_SIZE);
+
+#endif /* openflow/intel-ext.h */
@@ -896,6 +896,18 @@ netdev_bsd_get_carrier(const struct netdev *netdev_, bool *carrier)
return error;
}
+/* Initializes unused statistics to default values. */
+static void
+netdev_bsd_set_unused_stats(struct netdev_stats *dst)
+{
+ dst->rx_1024_to_1518_packets = UINT64_MAX;
+ dst->rx_512_to_1023_packets = UINT64_MAX;
+ dst->rx_256_to_511_packets = UINT64_MAX;
+ dst->rx_128_to_255_packets = UINT64_MAX;
+ dst->rx_65_to_127_packets = UINT64_MAX;
+ dst->rx_64_packets = UINT64_MAX;
+}
+
static void
convert_stats_system(struct netdev_stats *stats, const struct if_data *ifd)
{
@@ -923,6 +935,7 @@ convert_stats_system(struct netdev_stats *stats, const struct if_data *ifd)
stats->tx_fifo_errors = UINT64_MAX;
stats->tx_heartbeat_errors = UINT64_MAX;
stats->tx_window_errors = UINT64_MAX;
+ netdev_bsd_set_unused_stats(stats);
}
static void
@@ -957,6 +970,7 @@ convert_stats_tap(struct netdev_stats *stats, const struct if_data *ifd)
stats->tx_fifo_errors = UINT64_MAX;
stats->tx_heartbeat_errors = UINT64_MAX;
stats->tx_window_errors = UINT64_MAX;
+ netdev_bsd_set_unused_stats(stats);
}
static void
@@ -954,13 +954,15 @@ netdev_dpdk_vhost_update_rx_counters(struct netdev_stats *stats,
struct dp_packet **packets, int count)
{
int i;
+ unsigned size;
struct dp_packet *packet;
stats->rx_packets += count;
for (i = 0; i < count; i++) {
packet = packets[i];
+ size = dp_packet_size(packet);
- if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
+ if (OVS_UNLIKELY(size < ETH_HEADER_LEN)) {
/* This only protects the following multicast counting from
* too short packets, but it does not stop the packet from
* further processing. */
@@ -969,12 +971,32 @@ netdev_dpdk_vhost_update_rx_counters(struct netdev_stats *stats,
continue;
}
+ /* Hard-coded binary search for the size bucket. */
+ if (size < 256) {
+ if (size >= 128) {
+ stats->rx_128_to_255_packets++;
+ } else if (size <= 64) {
+ stats->rx_64_packets++;
+ } else {
+ stats->rx_65_to_127_packets++;
+ }
+ } else {
+ if (size >= 1024) {
+ stats->rx_1024_to_1518_packets++;
+ } else if (size < 512) {
+ stats->rx_256_to_511_packets++;
+ } else {
+ stats->rx_512_to_1023_packets++;
+ }
+ }
+
struct eth_header *eh = (struct eth_header *) dp_packet_data(packet);
+
if (OVS_UNLIKELY(eth_addr_is_multicast(eh->eth_dst))) {
stats->multicast++;
}
- stats->rx_bytes += dp_packet_size(packet);
+ stats->rx_bytes += size;
}
}
@@ -1415,8 +1437,9 @@ netdev_dpdk_vhost_get_stats(const struct netdev *netdev,
{
struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
+ memset(stats, 0xFF, sizeof (*stats));
+
ovs_mutex_lock(&dev->mutex);
- memset(stats, 0, sizeof(*stats));
/* Unsupported Stats */
stats->collisions = UINT64_MAX;
stats->rx_crc_errors = UINT64_MAX;
@@ -1442,6 +1465,14 @@ netdev_dpdk_vhost_get_stats(const struct netdev *netdev,
stats->tx_bytes = dev->stats.tx_bytes;
stats->rx_errors = dev->stats.rx_errors;
stats->rx_length_errors = dev->stats.rx_length_errors;
+
+ stats->rx_64_packets = dev->stats.rx_64_packets;
+ stats->rx_65_to_127_packets = dev->stats.rx_65_to_127_packets;
+ stats->rx_128_to_255_packets = dev->stats.rx_128_to_255_packets;
+ stats->rx_256_to_511_packets = dev->stats.rx_256_to_511_packets;
+ stats->rx_512_to_1023_packets = dev->stats.rx_512_to_1023_packets;
+ stats->rx_1024_to_1518_packets = dev->stats.rx_1024_to_1518_packets;
+
rte_spinlock_unlock(&dev->stats_lock);
ovs_mutex_unlock(&dev->mutex);
@@ -1456,12 +1487,12 @@ netdev_dpdk_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
struct rte_eth_stats rte_stats;
bool gg;
+ memset(stats, 0xFF, sizeof (*stats));
+
netdev_dpdk_get_carrier(netdev, &gg);
ovs_mutex_lock(&dev->mutex);
rte_eth_stats_get(dev->port_id, &rte_stats);
- memset(stats, 0, sizeof(*stats));
-
stats->rx_packets = rte_stats.ipackets;
stats->tx_packets = rte_stats.opackets;
stats->rx_bytes = rte_stats.ibytes;
@@ -1495,6 +1526,13 @@ netdev_dpdk_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
ovs_mutex_unlock(&dev->mutex);
+ stats->rx_64_packets = UINT64_MAX;
+ stats->rx_65_to_127_packets = UINT64_MAX;
+ stats->rx_128_to_255_packets = UINT64_MAX;
+ stats->rx_256_to_511_packets = UINT64_MAX;
+ stats->rx_512_to_1023_packets = UINT64_MAX;
+ stats->rx_1024_to_1518_packets = UINT64_MAX;
+
return 0;
}
@@ -1020,6 +1020,18 @@ netdev_dummy_set_mtu(const struct netdev *netdev, int mtu)
return 0;
}
+/* Initializes unused statistics to default values. */
+static void
+netdev_dummy_set_unused_stats(struct netdev_stats *dst)
+{
+ dst->rx_1024_to_1518_packets = UINT64_MAX;
+ dst->rx_512_to_1023_packets = UINT64_MAX;
+ dst->rx_256_to_511_packets = UINT64_MAX;
+ dst->rx_128_to_255_packets = UINT64_MAX;
+ dst->rx_65_to_127_packets = UINT64_MAX;
+ dst->rx_64_packets = UINT64_MAX;
+}
+
static int
netdev_dummy_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
{
@@ -1028,7 +1040,7 @@ netdev_dummy_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
ovs_mutex_lock(&dev->mutex);
*stats = dev->stats;
ovs_mutex_unlock(&dev->mutex);
-
+ netdev_dummy_set_unused_stats(stats);
return 0;
}
@@ -1550,6 +1550,18 @@ swap_uint64(uint64_t *a, uint64_t *b)
*b = tmp;
}
+/* Initializes unused statistics to default values. */
+static void
+netdev_linux_set_unused_stats(struct netdev_stats *dst)
+{
+ dst->rx_1024_to_1518_packets = UINT64_MAX;
+ dst->rx_512_to_1023_packets = UINT64_MAX;
+ dst->rx_256_to_511_packets = UINT64_MAX;
+ dst->rx_128_to_255_packets = UINT64_MAX;
+ dst->rx_65_to_127_packets = UINT64_MAX;
+ dst->rx_64_packets = UINT64_MAX;
+}
+
/* Copies 'src' into 'dst', performing format conversion in the process.
*
* 'src' is allowed to be misaligned. */
@@ -1671,6 +1683,8 @@ netdev_linux_get_stats(const struct netdev *netdev_,
}
ovs_mutex_unlock(&netdev->mutex);
+ netdev_linux_set_unused_stats(stats);
+
return error;
}
@@ -1733,6 +1747,8 @@ netdev_tap_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
}
ovs_mutex_unlock(&netdev->mutex);
+ netdev_linux_set_unused_stats(stats);
+
return error;
}
@@ -1748,6 +1764,8 @@ netdev_internal_get_stats(const struct netdev *netdev_,
error = netdev->vport_stats_error;
ovs_mutex_unlock(&netdev->mutex);
+ netdev_linux_set_unused_stats(stats);
+
return error;
}
@@ -812,6 +812,18 @@ set_patch_config(struct netdev *dev_, const struct smap *args)
return 0;
}
+/* Initializes unused statistics to default values. */
+static void
+netdev_vport_set_unused_stats(struct netdev_stats *dst)
+{
+ dst->rx_1024_to_1518_packets = UINT64_MAX;
+ dst->rx_512_to_1023_packets = UINT64_MAX;
+ dst->rx_256_to_511_packets = UINT64_MAX;
+ dst->rx_128_to_255_packets = UINT64_MAX;
+ dst->rx_65_to_127_packets = UINT64_MAX;
+ dst->rx_64_packets = UINT64_MAX;
+}
+
static int
get_stats(const struct netdev *netdev, struct netdev_stats *stats)
{
@@ -820,6 +832,7 @@ get_stats(const struct netdev *netdev, struct netdev_stats *stats)
ovs_mutex_lock(&dev->mutex);
*stats = dev->stats;
ovs_mutex_unlock(&dev->mutex);
+ netdev_vport_set_unused_stats(stats);
return 0;
}
@@ -102,6 +102,14 @@ struct netdev_stats {
uint64_t tx_fifo_errors;
uint64_t tx_heartbeat_errors;
uint64_t tx_window_errors;
+
+ /* Size bucket statistics. Based on RFC2819. */
+ uint64_t rx_64_packets;
+ uint64_t rx_65_to_127_packets;
+ uint64_t rx_128_to_255_packets;
+ uint64_t rx_256_to_511_packets;
+ uint64_t rx_512_to_1023_packets;
+ uint64_t rx_1024_to_1518_packets;
};
/* Configuration specific to tunnels. */
@@ -1611,6 +1611,28 @@ ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
ofp_print_duration(string, ps.duration_sec, ps.duration_nsec);
ds_put_char(string, '\n');
}
+ /* Present extended statistics only if anything is reported */
+ if ((ps.stats.rx_64_packets != UINT64_MAX) ||
+ (ps.stats.rx_65_to_127_packets != UINT64_MAX) ||
+ (ps.stats.rx_128_to_255_packets != UINT64_MAX) ||
+ (ps.stats.rx_256_to_511_packets != UINT64_MAX) ||
+ (ps.stats.rx_512_to_1023_packets != UINT64_MAX) ||
+ (ps.stats.rx_1024_to_1518_packets != UINT64_MAX)) {
+
+ ds_put_cstr(string, " rx rfc2819 ");
+ print_port_stat(string, "64_packets=",
+ ps.stats.rx_64_packets, 1);
+ print_port_stat(string, "65_to_127_packets=",
+ ps.stats.rx_65_to_127_packets, 1);
+ print_port_stat(string, "128_to_255_packets=",
+ ps.stats.rx_128_to_255_packets, 1);
+ print_port_stat(string, "256_to_511_packets=",
+ ps.stats.rx_256_to_511_packets, 1);
+ print_port_stat(string, "512_to_1023_packets=",
+ ps.stats.rx_512_to_1023_packets, 1);
+ print_port_stat(string, "1024_to_1518_packets=",
+ ps.stats.rx_1024_to_1518_packets, 0);
+ }
}
}
@@ -39,6 +39,7 @@
#include "ofp-util.h"
#include "ofpbuf.h"
#include "openflow/netronome-ext.h"
+#include "openflow/intel-ext.h"
#include "packets.h"
#include "random.h"
#include "tun-metadata.h"
@@ -47,6 +48,13 @@
#include "openvswitch/vlog.h"
#include "bitmap.h"
+/*
+ * This vendor ID is created only for RFC patch,
+ * it will be changed when requested ID will be
+ * added to openflow-common.h.
+ */
+#define INTEL_VENDOR_ID 0x0000151d
+
VLOG_DEFINE_THIS_MODULE(ofp_util);
/* Rate limit for OpenFlow message parse errors. These always indicate a bug
@@ -6852,13 +6860,16 @@ ofputil_append_ofp14_port_stats(const struct ofputil_port_stats *ops,
struct ovs_list *replies)
{
struct ofp14_port_stats_prop_ethernet *eth;
+ struct intel_port_stats_rfc2819 *stats_rfc2819;
struct ofp14_port_stats *ps14;
struct ofpbuf *reply;
- reply = ofpmp_reserve(replies, sizeof *ps14 + sizeof *eth);
+ reply =
+ ofpmp_reserve(replies,
+ sizeof *ps14 + sizeof *eth + sizeof *stats_rfc2819);
ps14 = ofpbuf_put_uninit(reply, sizeof *ps14);
- ps14->length = htons(sizeof *ps14 + sizeof *eth);
+ ps14->length = htons(sizeof *ps14 + sizeof *eth + sizeof *stats_rfc2819);
memset(ps14->pad, 0, sizeof ps14->pad);
ps14->port_no = ofputil_port_to_ofp11(ops->port_no);
ps14->duration_sec = htonl(ops->duration_sec);
@@ -6880,6 +6891,24 @@ ofputil_append_ofp14_port_stats(const struct ofputil_port_stats *ops,
eth->rx_over_err = htonll(ops->stats.rx_over_errors);
eth->rx_crc_err = htonll(ops->stats.rx_crc_errors);
eth->collisions = htonll(ops->stats.collisions);
+
+ stats_rfc2819 = ofpbuf_put_uninit(reply, sizeof *stats_rfc2819);
+ stats_rfc2819->type = htons(OFPPSPT14_EXPERIMENTER);
+ stats_rfc2819->length = htons(sizeof *stats_rfc2819);
+ stats_rfc2819->experimenter = htonl(INTEL_VENDOR_ID);
+ stats_rfc2819->exp_type = htonl(INTEL_PORT_STATS_RFC2819);
+ memset(stats_rfc2819->pad, 0, sizeof stats_rfc2819->pad);
+ stats_rfc2819->rx_64_packets = htonll(ops->stats.rx_64_packets);
+ stats_rfc2819->rx_65_to_127_packets =
+ htonll(ops->stats.rx_65_to_127_packets);
+ stats_rfc2819->rx_128_to_255_packets =
+ htonll(ops->stats.rx_128_to_255_packets);
+ stats_rfc2819->rx_256_to_511_packets =
+ htonll(ops->stats.rx_256_to_511_packets);
+ stats_rfc2819->rx_512_to_1023_packets =
+ htonll(ops->stats.rx_512_to_1023_packets);
+ stats_rfc2819->rx_1024_to_1518_packets =
+ htonll(ops->stats.rx_1024_to_1518_packets);
}
/* Encode a ports stat for 'ops' and append it to 'replies'. */
@@ -6920,7 +6949,8 @@ static enum ofperr
ofputil_port_stats_from_ofp10(struct ofputil_port_stats *ops,
const struct ofp10_port_stats *ps10)
{
- memset(ops, 0, sizeof *ops);
+ /* Initialize with all ones to filter everything out */
+ memset(ops, 0xFF, sizeof *ops);
ops->port_no = u16_to_ofp(ntohs(ps10->port_no));
ops->stats.rx_packets = ntohll(get_32aligned_be64(&ps10->rx_packets));
@@ -6947,7 +6977,8 @@ ofputil_port_stats_from_ofp11(struct ofputil_port_stats *ops,
{
enum ofperr error;
- memset(ops, 0, sizeof *ops);
+ /* Initialize with all ones to filter everything out */
+ memset(ops, 0xFF, sizeof *ops);
error = ofputil_port_from_ofp11(ps11->port_no, &ops->port_no);
if (error) {
return error;
@@ -7001,6 +7032,79 @@ parse_ofp14_port_stats_ethernet_property(const struct ofpbuf *payload,
}
static enum ofperr
+parse_intel_port_stats_rfc2819_property(const struct ofpbuf *payload,
+ struct ofputil_port_stats *ops)
+{
+ const struct intel_port_stats_rfc2819 *rfc2819 = payload->data;
+
+ if (payload->size != sizeof *rfc2819) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+ ops->stats.rx_64_packets = ntohll(rfc2819->rx_64_packets);
+ ops->stats.rx_65_to_127_packets = ntohll(rfc2819->rx_65_to_127_packets);
+ ops->stats.rx_128_to_255_packets = ntohll(rfc2819->rx_128_to_255_packets);
+ ops->stats.rx_256_to_511_packets = ntohll(rfc2819->rx_256_to_511_packets);
+ ops->stats.rx_512_to_1023_packets =
+ ntohll(rfc2819->rx_512_to_1023_packets);
+ ops->stats.rx_1024_to_1518_packets =
+ ntohll(rfc2819->rx_1024_to_1518_packets);
+
+ return 0;
+}
+
+static enum ofperr
+parse_intel_port_stats_property(const struct ofpbuf *payload, uint32_t exp_type,
+ struct ofputil_port_stats *ops)
+{
+ enum ofperr error;
+
+ switch (exp_type) {
+ case INTEL_PORT_STATS_RFC2819:
+ error = parse_intel_port_stats_rfc2819_property(payload, ops);
+ break;
+ default:
+ log_property(false, "unknown port stats experimenter type %" PRIu32,
+ exp_type);
+ error = OFPERR_OFPBPC_BAD_EXP_TYPE;
+ break;
+ }
+
+ return error;
+}
+
+static enum ofperr
+parse_ofp14_port_stats_exp(struct ofpbuf *payload,
+ struct ofputil_port_stats *ops)
+{
+ struct ofp_prop_experimenter *prop = payload->data;
+ uint16_t experimenter;
+ uint32_t exp_type;
+ enum ofperr error;
+
+ if (payload->size < sizeof *prop) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+
+ experimenter = ntohl(prop->experimenter);
+ exp_type = ntohl(prop->exp_type);
+
+ switch (experimenter) {
+ case INTEL_VENDOR_ID:
+ error = parse_intel_port_stats_property(payload, exp_type, ops);
+ break;
+
+ default:
+ log_property(false, "unknown group property experimenter %" PRIu16,
+ experimenter);
+ error = OFPERR_OFPBPC_BAD_EXPERIMENTER;
+ break;
+ }
+
+ return error;
+}
+
+
+static enum ofperr
ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops,
struct ofpbuf *msg)
{
@@ -7040,6 +7144,12 @@ ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops,
ops->stats.rx_over_errors = UINT64_MAX;
ops->stats.rx_crc_errors = UINT64_MAX;
ops->stats.collisions = UINT64_MAX;
+ ops->stats.rx_64_packets = UINT64_MAX;
+ ops->stats.rx_65_to_127_packets = UINT64_MAX;
+ ops->stats.rx_128_to_255_packets = UINT64_MAX;
+ ops->stats.rx_256_to_511_packets = UINT64_MAX;
+ ops->stats.rx_512_to_1023_packets = UINT64_MAX;
+ ops->stats.rx_1024_to_1518_packets = UINT64_MAX;
while (properties.size > 0) {
struct ofpbuf payload;
@@ -7055,7 +7165,9 @@ ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops,
case OFPPSPT14_ETHERNET:
error = parse_ofp14_port_stats_ethernet_property(&payload, ops);
break;
-
+ case OFPPSPT14_EXPERIMENTER:
+ error = parse_ofp14_port_stats_exp(&payload, ops);
+ break;
default:
log_property(true, "unknown port stats property %"PRIu16, type);
error = 0;
@@ -2336,21 +2336,28 @@ static void
iface_refresh_stats(struct iface *iface)
{
#define IFACE_STATS \
- IFACE_STAT(rx_packets, "rx_packets") \
- IFACE_STAT(tx_packets, "tx_packets") \
- IFACE_STAT(rx_bytes, "rx_bytes") \
- IFACE_STAT(tx_bytes, "tx_bytes") \
- IFACE_STAT(rx_dropped, "rx_dropped") \
- IFACE_STAT(tx_dropped, "tx_dropped") \
- IFACE_STAT(rx_errors, "rx_errors") \
- IFACE_STAT(tx_errors, "tx_errors") \
- IFACE_STAT(rx_frame_errors, "rx_frame_err") \
- IFACE_STAT(rx_over_errors, "rx_over_err") \
- IFACE_STAT(rx_crc_errors, "rx_crc_err") \
- IFACE_STAT(collisions, "collisions")
+ IFACE_STAT(rx_packets, "rx_packets") \
+ IFACE_STAT(tx_packets, "tx_packets") \
+ IFACE_STAT(rx_bytes, "rx_bytes") \
+ IFACE_STAT(tx_bytes, "tx_bytes") \
+ IFACE_STAT(rx_dropped, "rx_dropped") \
+ IFACE_STAT(tx_dropped, "tx_dropped") \
+ IFACE_STAT(rx_errors, "rx_errors") \
+ IFACE_STAT(tx_errors, "tx_errors") \
+ IFACE_STAT(rx_frame_errors, "rx_frame_err") \
+ IFACE_STAT(rx_over_errors, "rx_over_err") \
+ IFACE_STAT(rx_crc_errors, "rx_crc_err") \
+ IFACE_STAT(collisions, "collisions") \
+ IFACE_STAT(rx_64_packets, "rx_64_packets") \
+ IFACE_STAT(rx_65_to_127_packets, "rx_65_to_127_packets") \
+ IFACE_STAT(rx_128_to_255_packets, "rx_128_to_255_packets") \
+ IFACE_STAT(rx_256_to_511_packets, "rx_256_to_511_packets") \
+ IFACE_STAT(rx_512_to_1023_packets, "rx_512_to_1023_packets") \
+ IFACE_STAT(rx_1024_to_1518_packets, "rx_1024_to_1518_packets")
#define IFACE_STAT(MEMBER, NAME) + 1
enum { N_IFACE_STATS = IFACE_STATS };
+
#undef IFACE_STAT
int64_t values[N_IFACE_STATS];
const char *keys[N_IFACE_STATS];