It is meaningful for user to check the stats of IPFIX.
Using IPFIX stats, user can know how much flows the system
can support. It is also can be used for performance check
of IPFIX.
IPFIX stats is added for per IPFIX exporter. If bridge IPFIX is
enabled on the bridge, the whole bridge will have one exporter.
For flow IPFIX, the system keeps per id (column in
Flow_Sample_Collector_Set) per exporter.
1) Add 'ovs-ofctl dump-ipfix-bridge SWITCH' to export IPFIX stats of
the bridge which enable bridge IPFIX. The output format:
NXST_IPFIX_BRIDGE reply (xid=0x2):
bridge ipfix: flows=0, current flows=0, sampled pkts=0, \
ipv4 ok=0, ipv6 ok=0, tx pkts=0
pkts errs=0, ipv4 errs=0, ipv6 errs=0, tx errs=0
2) Add 'ovs-ofctl dump-ipfix-flow SWITCH' to export IPFIX stats of
the bridge which enable flow IPFIX. The output format:
NXST_IPFIX_FLOW reply (xid=0x2): 2 ids
id 1: flows=4, current flows=4, sampled pkts=14, ipv4 ok=13, \
ipv6 ok=0, tx pkts=0
pkts errs=0, ipv4 errs=0, ipv6 errs=0, tx errs=0
id 2: flows=0, current flows=0, sampled pkts=0, ipv4 ok=0, \
ipv6 ok=0, tx pkts=0
pkts errs=0, ipv4 errs=0, ipv6 errs=0, tx errs=0
flows: the number of total flow records, including those exported.
current flows: the number of current flow records cached.
sampled pkts: Successfully sampled packet count.
ipv4 ok: successfully sampled IPv4 flow packet count.
ipv6 ok: Successfully sampled IPv6 flow packet count.
tx pkts: the count of IPFIX exported packets sent to the collector(s).
pkts errs: count of packets failed when sampling, maybe not supported or other error.
ipv4 errs: Count of IPV4 flow packet in the error packets.
ipv6 errs: Count of IPV6 flow packet in the error packets.
tx errs: the count of IPFIX exported packets failed when sending to the collector(s).
Signed-off-by: Benli Ye <daniely@vmware.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
* queue-get-config command now allows a queue ID to be specified.
* '--bundle' option can now be used with OpenFlow 1.3.
* New option "--color" to produce colorized output for some commands.
+ * New commands "dump-ipfix-bridge" and "dump-ipfix-flow" to dump bridge
+ IPFIX statistics and flow based IPFIX statistics.
- DPDK:
* New option "n_rxq" for PMD interfaces.
Old 'other_config:n-dpdk-rxqs' is no longer supported.
*/
};
OFP_ASSERT(sizeof(struct nx_aggregate_stats_request) == 8);
+
+struct nx_ipfix_stats_reply {
+ ovs_be64 total_flows;
+ ovs_be64 current_flows;
+ ovs_be64 pkts;
+ ovs_be64 ipv4_pkts;
+ ovs_be64 ipv6_pkts;
+ ovs_be64 error_pkts;
+ ovs_be64 ipv4_error_pkts;
+ ovs_be64 ipv6_error_pkts;
+ ovs_be64 tx_pkts;
+ ovs_be64 tx_errors;
+ ovs_be32 collector_set_id; /* Range 0 to 4,294,967,295. */
+ uint8_t pad[4]; /* Pad to a multiple of 8 bytes. */
+};
+OFP_ASSERT(sizeof(struct nx_ipfix_stats_reply) == 88);
+
\f
/* NXT_SET_CONTROLLER_ID.
*
* continuation was generated, or continuation was not generated by this
* Open vSwitch instance. */
OFPERR_NXR_STALE,
+
+/* ## ---------- ## */
+/* ## NXT_STATS ## */
+/* ## ---------- ## */
+
+ /* NX1.0-1.1(1,535), NX1.2+(36). Protocol is not configured on this
+ * Open vSwitch instance. */
+ OFPERR_NXST_NOT_CONFIGURED,
};
const char *ofperr_domain_get_name(enum ofp_version);
/* NXT 1.0+ (28): uint8_t[8][]. */
OFPRAW_NXT_RESUME,
+
+ /* NXST 1.0+ (3): void. */
+ OFPRAW_NXST_IPFIX_BRIDGE_REQUEST,
+
+ /* NXST 1.0+ (3): struct nx_ipfix_stats_reply. */
+ OFPRAW_NXST_IPFIX_BRIDGE_REPLY,
+
+ /* NXST 1.0+ (4): void. */
+ OFPRAW_NXST_IPFIX_FLOW_REQUEST,
+
+ /* NXST 1.0+ (4): struct nx_ipfix_stats_reply[]. */
+ OFPRAW_NXST_IPFIX_FLOW_REPLY,
};
/* Decoding messages into OFPRAW_* values. */
OFPTYPE_NXT_TLV_TABLE_REQUEST, /* OFPRAW_NXT_TLV_TABLE_REQUEST. */
OFPTYPE_NXT_TLV_TABLE_REPLY, /* OFPRAW_NXT_TLV_TABLE_REPLY. */
OFPTYPE_NXT_RESUME, /* OFPRAW_NXT_RESUME. */
+ OFPTYPE_IPFIX_BRIDGE_STATS_REQUEST, /* OFPRAW_NXST_IPFIX_BRIDGE_REQUEST */
+ OFPTYPE_IPFIX_BRIDGE_STATS_REPLY, /* OFPRAW_NXST_IPFIX_BRIDGE_REPLY */
+ OFPTYPE_IPFIX_FLOW_STATS_REQUEST, /* OFPRAW_NXST_IPFIX_FLOW_REQUEST */
+ OFPTYPE_IPFIX_FLOW_STATS_REPLY, /* OFPRAW_NXST_IPFIX_FLOW_REPLY */
/* Flow monitor extension. */
OFPTYPE_FLOW_MONITOR_CANCEL, /* OFPRAW_NXT_FLOW_MONITOR_CANCEL. */
enum ofperr ofputil_decode_port_stats_request(const struct ofp_header *request,
ofp_port_t *ofp10_port);
+struct ofputil_ipfix_stats {
+ uint32_t collector_set_id; /* Used only for flow-based IPFIX statistics. */
+ uint64_t total_flows; /* Totabl flows of this IPFIX exporter. */
+ uint64_t current_flows; /* Current flows of this IPFIX exporter. */
+ uint64_t pkts; /* Successfully sampled packets. */
+ uint64_t ipv4_pkts; /* Successfully sampled IPV4 packets. */
+ uint64_t ipv6_pkts; /* Successfully sampled IPV6 packets. */
+ uint64_t error_pkts; /* Error packets when sampling. */
+ uint64_t ipv4_error_pkts; /* Error IPV4 packets when sampling. */
+ uint64_t ipv6_error_pkts; /* Error IPV6 packets when sampling. */
+ uint64_t tx_pkts; /* TX IPFIX packets. */
+ uint64_t tx_errors; /* IPFIX packets TX errors. */
+};
+
+void ofputil_append_ipfix_stat(struct ovs_list *replies,
+ const struct ofputil_ipfix_stats *ois);
+size_t ofputil_count_ipfix_stats(const struct ofp_header *);
+int ofputil_pull_ipfix_stats(struct ofputil_ipfix_stats *, struct ofpbuf *msg);
+
struct ofputil_queue_stats_request {
ofp_port_t port_no; /* OFPP_ANY means "all ports". */
uint32_t queue_id;
ofputil_destroy_requestforward(&rf);
}
+static void
+print_ipfix_stat(struct ds *string, const char *leader, uint64_t stat, int more)
+{
+ ds_put_cstr(string, leader);
+ if (stat != UINT64_MAX) {
+ ds_put_format(string, "%"PRIu64, stat);
+ } else {
+ ds_put_char(string, '?');
+ }
+ if (more) {
+ ds_put_cstr(string, ", ");
+ } else {
+ ds_put_cstr(string, "\n");
+ }
+}
+
+static void
+ofp_print_nxst_ipfix_bridge_reply(struct ds *string, const struct ofp_header *oh)
+{
+ struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+ for (;;) {
+ struct ofputil_ipfix_stats is;
+ int retval;
+
+ retval = ofputil_pull_ipfix_stats(&is, &b);
+ if (retval) {
+ if (retval != EOF) {
+ ds_put_cstr(string, " ***parse error***");
+ }
+ return;
+ }
+
+ ds_put_cstr(string, "\n bridge ipfix: ");
+ print_ipfix_stat(string, "flows=", is.total_flows, 1);
+ print_ipfix_stat(string, "current flows=", is.current_flows, 1);
+ print_ipfix_stat(string, "sampled pkts=", is.pkts, 1);
+ print_ipfix_stat(string, "ipv4 ok=", is.ipv4_pkts, 1);
+ print_ipfix_stat(string, "ipv6 ok=", is.ipv6_pkts, 1);
+ print_ipfix_stat(string, "tx pkts=", is.tx_pkts, 0);
+ ds_put_cstr(string, " ");
+ print_ipfix_stat(string, "pkts errs=", is.error_pkts, 1);
+ print_ipfix_stat(string, "ipv4 errs=", is.ipv4_error_pkts, 1);
+ print_ipfix_stat(string, "ipv6 errs=", is.ipv6_error_pkts, 1);
+ print_ipfix_stat(string, "tx errs=", is.tx_errors, 0);
+ }
+}
+
+static void
+ofp_print_nxst_ipfix_flow_reply(struct ds *string, const struct ofp_header *oh)
+{
+ ds_put_format(string, " %"PRIuSIZE" ids\n", ofputil_count_ipfix_stats(oh));
+
+ struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+ for (;;) {
+ struct ofputil_ipfix_stats is;
+ int retval;
+
+ retval = ofputil_pull_ipfix_stats(&is, &b);
+ if (retval) {
+ if (retval != EOF) {
+ ds_put_cstr(string, " ***parse error***");
+ }
+ return;
+ }
+
+ ds_put_cstr(string, " id");
+ ds_put_format(string, " %3"PRIuSIZE": ", (size_t) is.collector_set_id);
+ print_ipfix_stat(string, "flows=", is.total_flows, 1);
+ print_ipfix_stat(string, "current flows=", is.current_flows, 1);
+ print_ipfix_stat(string, "sampled pkts=", is.pkts, 1);
+ print_ipfix_stat(string, "ipv4 ok=", is.ipv4_pkts, 1);
+ print_ipfix_stat(string, "ipv6 ok=", is.ipv6_pkts, 1);
+ print_ipfix_stat(string, "tx pkts=", is.tx_pkts, 0);
+ ds_put_cstr(string, " ");
+ print_ipfix_stat(string, "pkts errs=", is.error_pkts, 1);
+ print_ipfix_stat(string, "ipv4 errs=", is.ipv4_error_pkts, 1);
+ print_ipfix_stat(string, "ipv6 errs=", is.ipv6_error_pkts, 1);
+ print_ipfix_stat(string, "tx errs=", is.tx_errors, 0);
+ }
+}
+
+
static void
ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
struct ds *string, int verbosity)
case OFPTYPE_NXT_RESUME:
ofp_print_packet_in(string, msg, verbosity);
break;
+ case OFPTYPE_IPFIX_BRIDGE_STATS_REQUEST:
+ break;
+ case OFPTYPE_IPFIX_BRIDGE_STATS_REPLY:
+ ofp_print_nxst_ipfix_bridge_reply(string, oh);
+ break;
+ case OFPTYPE_IPFIX_FLOW_STATS_REQUEST:
+ break;
+ case OFPTYPE_IPFIX_FLOW_STATS_REPLY:
+ ofp_print_nxst_ipfix_flow_reply(string, oh);
+ break;
}
}
}
}
+static void
+ofputil_ipfix_stats_to_reply(const struct ofputil_ipfix_stats *ois,
+ struct nx_ipfix_stats_reply *reply)
+{
+ reply->collector_set_id = htonl(ois->collector_set_id);
+ reply->total_flows = htonll(ois->total_flows);
+ reply->current_flows = htonll(ois->current_flows);
+ reply->pkts = htonll(ois->pkts);
+ reply->ipv4_pkts = htonll(ois->ipv4_pkts);
+ reply->ipv6_pkts = htonll(ois->ipv6_pkts);
+ reply->error_pkts = htonll(ois->error_pkts);
+ reply->ipv4_error_pkts = htonll(ois->ipv4_error_pkts);
+ reply->ipv6_error_pkts = htonll(ois->ipv6_error_pkts);
+ reply->tx_pkts = htonll(ois->tx_pkts);
+ reply->tx_errors = htonll(ois->tx_errors);
+}
+
+/* Encode a ipfix stat for 'ois' and append it to 'replies'. */
+void
+ofputil_append_ipfix_stat(struct ovs_list *replies,
+ const struct ofputil_ipfix_stats *ois)
+{
+ struct nx_ipfix_stats_reply *reply = ofpmp_append(replies, sizeof *reply);
+ ofputil_ipfix_stats_to_reply(ois, reply);
+}
+
+static enum ofperr
+ofputil_ipfix_stats_from_nx(struct ofputil_ipfix_stats *is,
+ const struct nx_ipfix_stats_reply *reply)
+{
+ is->collector_set_id = ntohl(reply->collector_set_id);
+ is->total_flows = ntohll(reply->total_flows);
+ is->current_flows = ntohll(reply->current_flows);
+ is->pkts = ntohll(reply->pkts);
+ is->ipv4_pkts = ntohll(reply->ipv4_pkts);
+ is->ipv6_pkts = ntohll(reply->ipv6_pkts);
+ is->error_pkts = ntohll(reply->error_pkts);
+ is->ipv4_error_pkts = ntohll(reply->ipv4_error_pkts);
+ is->ipv6_error_pkts = ntohll(reply->ipv6_error_pkts);
+ is->tx_pkts = ntohll(reply->tx_pkts);
+ is->tx_errors = ntohll(reply->tx_errors);
+
+ return 0;
+}
+
+int
+ofputil_pull_ipfix_stats(struct ofputil_ipfix_stats *is, struct ofpbuf *msg)
+{
+ enum ofperr error;
+ enum ofpraw raw;
+
+ memset(is, 0xFF, sizeof (*is));
+
+ error = (msg->header ? ofpraw_decode(&raw, msg->header)
+ : ofpraw_pull(&raw, msg));
+ if (error) {
+ return error;
+ }
+
+ if (!msg->size) {
+ return EOF;
+ } else if (raw == OFPRAW_NXST_IPFIX_BRIDGE_REPLY ||
+ raw == OFPRAW_NXST_IPFIX_FLOW_REPLY) {
+ struct nx_ipfix_stats_reply *reply;
+
+ reply = ofpbuf_try_pull(msg, sizeof *reply);
+ return ofputil_ipfix_stats_from_nx(is, reply);
+ } else {
+ OVS_NOT_REACHED();
+ }
+}
+
+
+/* Returns the number of ipfix stats elements in
+ * OFPTYPE_IPFIX_BRIDGE_STATS_REPLY or OFPTYPE_IPFIX_FLOW_STATS_REPLY
+ * message 'oh'. */
+size_t
+ofputil_count_ipfix_stats(const struct ofp_header *oh)
+{
+ struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+
+ return b.size / sizeof(struct ofputil_ipfix_stats);
+}
+
/* Frees all of the "struct ofputil_bucket"s in the 'buckets' list. */
void
ofputil_bucket_list_destroy(struct ovs_list *buckets)
case OFPTYPE_NXT_TLV_TABLE_REQUEST:
case OFPTYPE_NXT_TLV_TABLE_REPLY:
case OFPTYPE_NXT_RESUME:
+ case OFPTYPE_IPFIX_BRIDGE_STATS_REQUEST:
+ case OFPTYPE_IPFIX_BRIDGE_STATS_REPLY:
+ case OFPTYPE_IPFIX_FLOW_STATS_REQUEST:
+ case OFPTYPE_IPFIX_FLOW_STATS_REPLY:
break;
}
case OFPTYPE_NXT_TLV_TABLE_REQUEST:
case OFPTYPE_NXT_TLV_TABLE_REPLY:
case OFPTYPE_NXT_RESUME:
+ case OFPTYPE_IPFIX_BRIDGE_STATS_REQUEST:
+ case OFPTYPE_IPFIX_BRIDGE_STATS_REPLY:
+ case OFPTYPE_IPFIX_FLOW_STATS_REQUEST:
+ case OFPTYPE_IPFIX_FLOW_STATS_REPLY:
default:
return true;
}
}
}
-/* Sends the 'n'-byte 'payload' to each of the collectors in 'c'. */
-void
+/* Sends the 'n'-byte 'payload' to each of the collectors in 'c'.
+ * Return the number of IPFIX packets which were sent unsuccessfully*/
+size_t
collectors_send(const struct collectors *c, const void *payload, size_t n)
{
+ size_t errors = 0;
+
if (c) {
size_t i;
VLOG_WARN_RL(&rl, "%s: sending to collector failed (%s)",
s, ovs_strerror(errno));
free(s);
+ errors++;
}
}
}
+
+ return errors;
}
int
struct collectors **);
void collectors_destroy(struct collectors *);
-void collectors_send(const struct collectors *, const void *, size_t);
+size_t collectors_send(const struct collectors *, const void *, size_t);
int collectors_count(const struct collectors *);
#include "sset.h"
#include "util.h"
#include "timeval.h"
-#include "util.h"
#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(ipfix);
#define BFD_CONTROL_DEST_PORT 3784
#define BFD_ECHO_DEST_PORT 3785
+enum ipfix_sampled_packet_type {
+ IPFIX_SAMPLED_PKT_UNKNOWN = 0x00,
+ IPFIX_SAMPLED_PKT_IPV4_OK = 0x01,
+ IPFIX_SAMPLED_PKT_IPV6_OK = 0x02,
+ IPFIX_SAMPLED_PKT_IPV4_ERROR = 0x03,
+ IPFIX_SAMPLED_PKT_IPV6_ERROR = 0x04,
+ IPFIX_SAMPLED_PKT_OTHERS = 0x05
+};
+
/* The standard layer2SegmentId (ID 351) element is included in vDS to send
* the VxLAN tunnel's VNI. It is 64-bit long, the most significant byte is
* used to indicate the type of tunnel (0x01 = VxLAN, 0x02 = GRE) and the three
NUM_DPIF_IPFIX_TUNNEL
};
+typedef struct ofputil_ipfix_stats ofproto_ipfix_stats;
+
struct dpif_ipfix_port {
struct hmap_node hmap_node; /* In struct dpif_ipfix's "tunnel_ports" hmap. */
struct ofport *ofport; /* To retrieve port stats. */
struct ovs_list cache_flow_start_timestamp_list; /* ipfix_flow_cache_entry. */
uint32_t cache_active_timeout; /* In seconds. */
uint32_t cache_max_flows;
+
+ ofproto_ipfix_stats stats;
};
struct dpif_ipfix_bridge_exporter {
hdr->obs_domain_id = htonl(obs_domain_id);
}
-static void
+static size_t
ipfix_send_msg(const struct collectors *collectors, struct dp_packet *msg)
{
struct ipfix_header *hdr;
+ size_t tx_errors;
/* Adjust the length in the header. */
hdr = dp_packet_data(msg);
hdr->length = htons(dp_packet_size(msg));
- collectors_send(collectors, dp_packet_data(msg), dp_packet_size(msg));
+ tx_errors = collectors_send(collectors,
+ dp_packet_data(msg), dp_packet_size(msg));
dp_packet_set_size(msg, 0);
+
+ return tx_errors;
}
static uint16_t
set_hdr->set_id = htons(IPFIX_SET_ID_TEMPLATE);
}
-static void
+static size_t
ipfix_send_template_msg(const struct collectors *collectors,
struct dp_packet *msg, size_t set_hdr_offset)
{
struct ipfix_set_header *set_hdr;
+ size_t tx_errors;
/* Send template message. */
set_hdr = (struct ipfix_set_header*)
((uint8_t*)dp_packet_data(msg) + set_hdr_offset);
set_hdr->length = htons(dp_packet_size(msg) - set_hdr_offset);
- ipfix_send_msg(collectors, msg);
+ tx_errors = ipfix_send_msg(collectors, msg);
dp_packet_uninit(msg);
+
+ return tx_errors;
}
static void
{
uint64_t msg_stub[DIV_ROUND_UP(MAX_MESSAGE_LEN, 8)];
struct dp_packet msg;
- size_t set_hdr_offset, tmpl_hdr_offset;
+ size_t set_hdr_offset, tmpl_hdr_offset, error_pkts;
struct ipfix_template_record_header *tmpl_hdr;
uint16_t field_count;
+ size_t tx_packets = 0;
+ size_t tx_errors = 0;
enum ipfix_proto_l2 l2;
enum ipfix_proto_l3 l3;
enum ipfix_proto_l4 l4;
*/
if (dp_packet_size(&msg) >= MAX_MESSAGE_LEN) {
/* Send template message. */
- ipfix_send_template_msg(exporter->collectors,
- &msg, set_hdr_offset);
+ error_pkts = ipfix_send_template_msg(exporter->collectors,
+ &msg, set_hdr_offset);
+ tx_errors += error_pkts;
+ tx_packets += collectors_count(exporter->collectors) - error_pkts;
/* Reinitialize the template msg. */
ipfix_init_template_msg(msg_stub, export_time_sec,
}
/* Send template message. */
- ipfix_send_template_msg(exporter->collectors, &msg, set_hdr_offset);
+ error_pkts = ipfix_send_template_msg(exporter->collectors, &msg, set_hdr_offset);
+ tx_errors += error_pkts;
+ tx_packets += collectors_count(exporter->collectors) - error_pkts;
+
+ exporter->stats.tx_pkts += tx_packets;
+ exporter->stats.tx_errors += tx_errors;
/* XXX: Add Options Template Sets, at least to define a Flow Keys
* Option Template. */
}
}
+/* Get statistics */
+static void
+ipfix_get_stats__(const struct dpif_ipfix_exporter *exporter,
+ ofproto_ipfix_stats *stats)
+{
+ memset(stats, 0xff, sizeof *stats);
+
+ if (!exporter) {
+ return;
+ }
+
+ *stats = exporter->stats;
+}
+
+static void
+ipfix_get_bridge_stats(const struct dpif_ipfix_bridge_exporter *exporter,
+ ofproto_ipfix_stats *stats)
+{
+ ipfix_get_stats__(&exporter->exporter, stats);
+}
+
+static void
+ipfix_get_flow_stats(const struct dpif_ipfix_flow_exporter *exporter,
+ ofproto_ipfix_stats *stats)
+{
+ ipfix_get_stats__(&exporter->exporter, stats);
+ stats->collector_set_id = exporter->options->collector_set_id;
+}
+
+int
+dpif_ipfix_get_stats(const struct dpif_ipfix *di,
+ bool bridge_ipfix,
+ struct ovs_list *replies)
+ OVS_EXCLUDED(mutex)
+{
+ struct dpif_ipfix_flow_exporter_map_node *flow_exporter_node;
+ struct ofputil_ipfix_stats ois;
+
+ ovs_mutex_lock(&mutex);
+ if (bridge_ipfix) {
+ if (!di->bridge_exporter.options) {
+ ovs_mutex_unlock(&mutex);
+ return OFPERR_NXST_NOT_CONFIGURED;
+ }
+
+ ipfix_get_bridge_stats(&di->bridge_exporter, &ois);
+ ofputil_append_ipfix_stat(replies, &ois);
+ } else {
+ if (hmap_count(&di->flow_exporter_map) == 0) {
+ ovs_mutex_unlock(&mutex);
+ return OFPERR_NXST_NOT_CONFIGURED;
+ }
+
+ HMAP_FOR_EACH (flow_exporter_node, node,
+ &di->flow_exporter_map) {
+ ipfix_get_flow_stats(&flow_exporter_node->exporter, &ois);
+ ofputil_append_ipfix_stat(replies, &ois);
+ }
+ }
+ ovs_mutex_unlock(&mutex);
+
+ return 0;
+}
+
+/* Update partial ipfix stats */
+static void
+ipfix_update_stats(struct dpif_ipfix_exporter *exporter,
+ bool new_flow,
+ size_t current_flows,
+ enum ipfix_sampled_packet_type sampled_pkt_type)
+{
+ if (new_flow) {
+ exporter->stats.total_flows++;
+ exporter->stats.current_flows = current_flows;
+ }
+ exporter->stats.pkts++;
+
+ switch (sampled_pkt_type) {
+ case IPFIX_SAMPLED_PKT_IPV4_OK:
+ exporter->stats.ipv4_pkts++;
+ break;
+ case IPFIX_SAMPLED_PKT_IPV6_OK:
+ exporter->stats.ipv6_pkts++;
+ break;
+ case IPFIX_SAMPLED_PKT_IPV4_ERROR:
+ exporter->stats.ipv4_error_pkts++;
+ exporter->stats.error_pkts++;
+ break;
+ case IPFIX_SAMPLED_PKT_IPV6_ERROR:
+ exporter->stats.ipv6_error_pkts++;
+ exporter->stats.error_pkts++;
+ break;
+ case IPFIX_SAMPLED_PKT_UNKNOWN:
+ exporter->stats.error_pkts++;
+ break;
+ case IPFIX_SAMPLED_PKT_OTHERS:
+ default:
+ break;
+ }
+}
+
/* Add an entry into a flow cache. The entry is either aggregated into
* an existing entry with the same flow key and free()d, or it is
- * inserted into the cache. */
+ * inserted into the cache. And IPFIX stats will be updated */
static void
ipfix_cache_update(struct dpif_ipfix_exporter *exporter,
- struct ipfix_flow_cache_entry *entry)
+ struct ipfix_flow_cache_entry *entry,
+ enum ipfix_sampled_packet_type sampled_pkt_type)
{
struct ipfix_flow_cache_entry *old_entry;
+ size_t current_flows = 0;
old_entry = ipfix_cache_find_entry(exporter, &entry->flow_key);
&entry->cache_flow_start_timestamp_list_node);
/* Enforce exporter->cache_max_flows limit. */
- if (hmap_count(&exporter->cache_flow_key_map)
- > exporter->cache_max_flows) {
+ current_flows = hmap_count(&exporter->cache_flow_key_map);
+ ipfix_update_stats(exporter, true, current_flows, sampled_pkt_type);
+ if (current_flows > exporter->cache_max_flows) {
dpif_ipfix_cache_expire_now(exporter, false);
}
} else {
ipfix_cache_aggregate_entries(entry, old_entry);
free(entry);
+ ipfix_update_stats(exporter, false, current_flows, sampled_pkt_type);
}
}
-static void
+static enum ipfix_sampled_packet_type
ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
const struct dp_packet *packet, const struct flow *flow,
uint64_t packet_delta_count, uint32_t obs_domain_id,
enum ipfix_proto_l3 l3;
enum ipfix_proto_l4 l4;
enum ipfix_proto_tunnel tunnel = IPFIX_PROTO_NOT_TUNNELED;
+ enum ipfix_sampled_packet_type sampled_pkt_type = IPFIX_SAMPLED_PKT_UNKNOWN;
uint8_t ethernet_header_length;
uint16_t ethernet_total_length;
case IPPROTO_UDP:
case IPPROTO_SCTP:
l4 = IPFIX_PROTO_L4_TCP_UDP_SCTP;
+ sampled_pkt_type = IPFIX_SAMPLED_PKT_IPV4_OK;
break;
case IPPROTO_ICMP:
l4 = IPFIX_PROTO_L4_ICMP;
+ sampled_pkt_type = IPFIX_SAMPLED_PKT_IPV4_OK;
break;
default:
l4 = IPFIX_PROTO_L4_UNKNOWN;
+ sampled_pkt_type = IPFIX_SAMPLED_PKT_IPV4_ERROR;
}
break;
case ETH_TYPE_IPV6:
case IPPROTO_UDP:
case IPPROTO_SCTP:
l4 = IPFIX_PROTO_L4_TCP_UDP_SCTP;
+ sampled_pkt_type = IPFIX_SAMPLED_PKT_IPV6_OK;
break;
case IPPROTO_ICMPV6:
l4 = IPFIX_PROTO_L4_ICMP;
+ sampled_pkt_type = IPFIX_SAMPLED_PKT_IPV6_OK;
break;
default:
l4 = IPFIX_PROTO_L4_UNKNOWN;
+ sampled_pkt_type = IPFIX_SAMPLED_PKT_IPV6_ERROR;
}
break;
default:
l3 = IPFIX_PROTO_L3_UNKNOWN;
l4 = IPFIX_PROTO_L4_UNKNOWN;
+ sampled_pkt_type = IPFIX_SAMPLED_PKT_OTHERS;
}
if (tunnel_port && tunnel_key) {
entry->minimum_ip_total_length = 0;
entry->maximum_ip_total_length = 0;
}
+
+ return sampled_pkt_type;
}
/* Send each single data record in its own data set, to simplify the
{
uint64_t msg_stub[DIV_ROUND_UP(MAX_MESSAGE_LEN, 8)];
struct dp_packet msg;
+ size_t tx_errors;
+
dp_packet_use_stub(&msg, msg_stub, sizeof msg_stub);
ipfix_init_header(export_time_sec, exporter->seq_number++,
entry->flow_key.obs_domain_id, &msg);
ipfix_put_data_set(export_time_sec, entry, flow_end_reason, &msg);
- ipfix_send_msg(exporter->collectors, &msg);
+ tx_errors = ipfix_send_msg(exporter->collectors, &msg);
dp_packet_uninit(&msg);
+
+ exporter->stats.current_flows--;
+ exporter->stats.tx_pkts += collectors_count(exporter->collectors) - tx_errors;
+ exporter->stats.tx_errors += tx_errors;
}
static void
const struct flow_tnl *tunnel_key)
{
struct ipfix_flow_cache_entry *entry;
+ enum ipfix_sampled_packet_type sampled_packet_type;
/* Create a flow cache entry from the sample. */
entry = xmalloc(sizeof *entry);
- ipfix_cache_entry_init(entry, packet, flow, packet_delta_count,
- obs_domain_id, obs_point_id,
- output_odp_port, tunnel_port, tunnel_key);
- ipfix_cache_update(exporter, entry);
+ sampled_packet_type = ipfix_cache_entry_init(entry, packet,
+ flow, packet_delta_count,
+ obs_domain_id, obs_point_id,
+ output_odp_port, tunnel_port,
+ tunnel_key);
+ ipfix_cache_update(exporter, entry, sampled_packet_type);
}
static bool
const struct ofproto_ipfix_bridge_exporter_options *,
const struct ofproto_ipfix_flow_exporter_options *, size_t);
+int dpif_ipfix_get_stats(const struct dpif_ipfix *, bool, struct ovs_list *);
+
void dpif_ipfix_bridge_sample(struct dpif_ipfix *, const struct dp_packet *,
const struct flow *,
odp_port_t, odp_port_t, const struct flow_tnl *);
return 0;
}
+static int
+get_ipfix_stats(const struct ofproto *ofproto_,
+ bool bridge_ipfix,
+ struct ovs_list *replies)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct dpif_ipfix *di = ofproto->ipfix;
+
+ if (!di) {
+ return OFPERR_NXST_NOT_CONFIGURED;
+ }
+
+ return dpif_ipfix_get_stats(di, bridge_ipfix, replies);
+}
+
static int
set_cfm(struct ofport *ofport_, const struct cfm_settings *s)
{
get_netflow_ids,
set_sflow,
set_ipfix,
+ get_ipfix_stats,
set_cfm,
cfm_status_changed,
get_cfm_status,
const struct ofproto_ipfix_flow_exporter_options
*flow_exporters_options, size_t n_flow_exporters_options);
+ /* Gets IPFIX stats on 'ofproto' according to the exporter of birdge
+ * IPFIX or flow-based IPFIX.
+ *
+ * OFPERR_NXST_NOT_CONFIGURED as a return value indicates that bridge
+ * IPFIX or flow-based IPFIX is not configured. */
+ int (*get_ipfix_stats)(
+ const struct ofproto *ofproto,
+ bool bridge_ipfix, struct ovs_list *replies
+ );
+
/* Configures connectivity fault management on 'ofport'.
*
* If 'cfm_settings' is nonnull, configures CFM according to its members.
}
}
+static int
+ofproto_get_ipfix_stats(struct ofproto *ofproto,
+ bool bridge_ipfix,
+ struct ovs_list *replies)
+{
+ int error;
+
+ if (ofproto->ofproto_class->get_ipfix_stats) {
+ error = ofproto->ofproto_class->get_ipfix_stats(ofproto,
+ bridge_ipfix,
+ replies);
+ } else {
+ error = EOPNOTSUPP;
+ }
+
+ return error;
+}
+
+static enum ofperr
+handle_ipfix_bridge_stats_request(struct ofconn *ofconn,
+ const struct ofp_header *request)
+{
+ struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+ struct ovs_list replies;
+ enum ofperr error;
+
+ ofpmp_init(&replies, request);
+ error = ofproto_get_ipfix_stats(ofproto, true, &replies);
+
+ if (!error) {
+ ofconn_send_replies(ofconn, &replies);
+ } else {
+ ofpbuf_list_delete(&replies);
+ }
+
+ return error;
+}
+
+static enum ofperr
+handle_ipfix_flow_stats_request(struct ofconn *ofconn,
+ const struct ofp_header *request)
+{
+ struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+ struct ovs_list replies;
+ enum ofperr error;
+
+ ofpmp_init(&replies, request);
+ error = ofproto_get_ipfix_stats(ofproto, false, &replies);
+
+ if (!error) {
+ ofconn_send_replies(ofconn, &replies);
+ } else {
+ ofpbuf_list_delete(&replies);
+ }
+
+ return error;
+}
+
void
ofproto_set_flow_restore_wait(bool flow_restore_wait_db)
{
case OFPTYPE_NXT_TLV_TABLE_REQUEST:
return handle_tlv_table_request(ofconn, oh);
+ case OFPTYPE_IPFIX_BRIDGE_STATS_REQUEST:
+ return handle_ipfix_bridge_stats_request(ofconn, oh);
+
+ case OFPTYPE_IPFIX_FLOW_STATS_REQUEST:
+ return handle_ipfix_flow_stats_request(ofconn, oh);
+
case OFPTYPE_HELLO:
case OFPTYPE_ERROR:
case OFPTYPE_FEATURES_REPLY:
case OFPTYPE_REQUESTFORWARD:
case OFPTYPE_TABLE_STATUS:
case OFPTYPE_NXT_TLV_TABLE_REPLY:
+ case OFPTYPE_IPFIX_BRIDGE_STATS_REPLY:
+ case OFPTYPE_IPFIX_FLOW_STATS_REPLY:
default:
if (ofpmsg_is_stat_request(oh)) {
return OFPERR_OFPBRC_BAD_STAT;
advertise: 10MB-HD
])
AT_CLEANUP
+
+AT_SETUP([NXST_IPFIX_BRIDGE - request])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 10 00 18 00 00 00 02 \
+ff ff 00 00 00 00 23 20 00 00 00 03 00 00 00 00 \
+"], [0], [dnl
+NXST_IPFIX_BRIDGE request (xid=0x2):
+])
+AT_CLEANUP
+
+AT_SETUP([NXST_IPFIX_BRIDGE - reply])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 11 00 70 00 00 00 02 \
+ff ff 00 00 00 00 23 20 00 00 00 03 00 00 00 00\
+00 00 00 00 00 00 00 01 \
+00 00 00 00 00 00 00 10 \
+00 00 00 00 00 00 00 78 \
+00 00 00 00 00 00 00 f0 \
+00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 a0 \
+00 00 00 00 00 00 00 02 \
+00 00 00 00 00 00 00 03 \
+00 00 00 00 00 00 00 04 \
+00 00 00 00 00 00 00 05 \
+00 00 00 00 00 00 00 00 \
+"], [0], [dnl
+NXST_IPFIX_BRIDGE reply (xid=0x2):
+ bridge ipfix: flows=1, current flows=16, sampled pkts=120, ipv4 ok=240, ipv6 ok=0, tx pkts=4
+ pkts errs=160, ipv4 errs=2, ipv6 errs=3, tx errs=5
+])
+AT_CLEANUP
+
+AT_SETUP([NXST_IPFIX_FLOW - request])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 10 00 18 00 00 00 02 \
+ff ff 00 00 00 00 23 20 00 00 00 04 00 00 00 00 \
+"], [0], [dnl
+NXST_IPFIX_FLOW request (xid=0x2):
+])
+AT_CLEANUP
+
+AT_SETUP([NXST_IPFIX_FLOW - reply])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 11 00 C8 00 00 00 02 \
+ff ff 00 00 00 00 23 20 00 00 00 04 00 00 00 00\
+00 00 00 00 00 00 00 01 \
+00 00 00 00 00 00 00 10 \
+00 00 00 00 00 00 00 78 \
+00 00 00 00 00 00 00 f0 \
+00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 a0 \
+00 00 00 10 00 00 00 02 \
+00 00 00 00 00 00 00 03 \
+00 00 00 00 00 00 00 04 \
+00 00 00 00 00 00 00 05 \
+00 00 00 01 00 00 00 00 \
+00 00 00 00 00 00 00 01 \
+00 00 00 00 00 00 00 10 \
+00 00 00 00 00 00 00 78 \
+00 00 00 00 00 00 00 f0 \
+00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 a0 \
+00 00 00 10 00 00 00 02 \
+00 00 00 00 00 00 00 03 \
+00 00 00 00 00 00 00 04 \
+00 00 00 00 00 00 00 05 \
+00 00 00 02 00 00 00 00 \
+"], [0], [dnl
+NXST_IPFIX_FLOW reply (xid=0x2): 2 ids
+ id 1: flows=1, current flows=16, sampled pkts=120, ipv4 ok=240, ipv6 ok=0, tx pkts=4
+ pkts errs=160, ipv4 errs=68719476738, ipv6 errs=3, tx errs=5
+ id 2: flows=1, current flows=16, sampled pkts=120, ipv4 ok=240, ipv6 ok=0, tx pkts=4
+ pkts errs=160, ipv4 errs=68719476738, ipv6 errs=3, tx errs=5
+])
+AT_CLEANUP
AT_CLEANUP
dnl In the absence of an IPFIX collector to verify protocol correctness, simply
-dnl configure IPFIX and ensure that sample action generation works at the
+dnl configure bridge IPFIX and ensure that sample action generation works at the
dnl datapath level.
-AT_SETUP([ofproto-dpif - Basic IPFIX sanity check])
+AT_SETUP([ofproto-dpif - Bridge IPFIX sanity check])
OVS_VSWITCHD_START
add_of_ports br0 1 2
-dnl Sample every packet using bridge-based sampling
+dnl Sample every packet using bridge-based sampling.
AT_CHECK([ovs-vsctl -- set bridge br0 ipfix=@fix -- \
--id=@fix create ipfix targets=\"127.0.0.1:4739\" \
sampling=1], [0], [ignore])
-dnl Send some packets that should be sampled
+dnl Send some packets that should be sampled.
for i in `seq 1 3`; do
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)'])
done
packets:2, bytes:120, used:0.001s, actions:sample(sample=100.0%,actions(userspace(pid=0,ipfix(output_port=4294967295))))
])
-dnl Remove the IPFIX configuration
+dnl Remove the IPFIX configuration.
AT_CHECK([ovs-vsctl clear bridge br0 ipfix])
AT_CHECK([ovs-appctl revalidator/purge])
OVS_VSWITCHD_STOP(["/sending to collector failed/d"])
AT_CLEANUP
+dnl Bridge IPFIX statistics check
+AT_SETUP([ofproto-dpif - Bridge IPFIX statistics check])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2
+
+dnl Negative test check.
+AT_CHECK([ovs-ofctl dump-ipfix-bridge br0], [0], [dnl
+OFPT_ERROR (xid=0x2): NXST_NOT_CONFIGURED
+NXST_IPFIX_BRIDGE request (xid=0x2):
+])
+
+dnl Sample every packet using bridge-based sampling.
+AT_CHECK([ovs-vsctl -- set bridge br0 ipfix=@fix -- \
+ --id=@fix create ipfix targets=\"127.0.0.1:4739\" \
+ sampling=1], [0], [ignore])
+
+dnl Send some packets that should be sampled.
+for i in `seq 1 20`; do
+ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)'])
+done
+
+dnl There are 4 extra IPFIX template packets.
+AT_CHECK([ovs-ofctl dump-ipfix-bridge br0], [0], [dnl
+NXST_IPFIX_BRIDGE reply (xid=0x2):
+ bridge ipfix: flows=20, current flows=0, sampled pkts=20, ipv4 ok=0, ipv6 ok=0, tx pkts=12
+ pkts errs=20, ipv4 errs=20, ipv6 errs=0, tx errs=12
+])
+
+dnl Remove the IPFIX configuration.
+AT_CHECK([ovs-vsctl clear bridge br0 ipfix])
+AT_CHECK([ovs-appctl revalidator/purge])
+
+dnl Send some more packets, to ensure that these are not sampled.
+for i in `seq 1 2`; do
+ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)'])
+done
+AT_CHECK([ovs-ofctl dump-ipfix-bridge br0], [0], [dnl
+OFPT_ERROR (xid=0x2): NXST_NOT_CONFIGURED
+NXST_IPFIX_BRIDGE request (xid=0x2):
+])
+
+OVS_VSWITCHD_STOP(["/sending to collector failed/d"])
+AT_CLEANUP
+
+dnl Flow IPFIX sanity check
+AT_SETUP([ofproto-dpif - Flow IPFIX sanity check])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2
+
+AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \
+ -- --id=@ipfix create IPFIX targets=\"127.0.0.1:4739\" \
+ -- --id=@cs create Flow_Sample_Collector_Set id=1 bridge=@br0 ipfix=@ipfix],
+ [0], [ignore])
+
+AT_DATA([flows.txt], [dnl
+in_port=1, actions=sample(probability=65535,collector_set_id=1),output:2
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt], [0], [ignore])
+
+dnl Send some packets that should be sampled.
+for i in `seq 1 3`; do
+ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)'])
+done
+AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*\(packets:\)/\1/' | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl
+flow-dump from non-dpdk interfaces:
+packets:2, bytes:120, used:0.001s, actions:sample(sample=100.0%,actions(userspace(pid=0,flow_sample(probability=65535,collector_set_id=1,obs_domain_id=0,obs_point_id=0)))),2
+])
+
+dnl Remove the flow which contains sample action.
+AT_CHECK([ovs-ofctl del-flows br0 in_port=1], [0], [ignore])
+AT_CHECK([ovs-appctl revalidator/purge])
+
+dnl Send some more packets, to ensure that these are not sampled.
+for i in `seq 1 3`; do
+ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)'])
+done
+AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*\(packets:\)/\1/' | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl
+flow-dump from non-dpdk interfaces:
+packets:2, bytes:120, used:0.001s, actions:drop
+])
+
+OVS_VSWITCHD_STOP(["/sending to collector failed/d"])
+AT_CLEANUP
+
+dnl Flow based IPFIX statistics check
+AT_SETUP([ofproto-dpif - Flow IPFIX statistics check])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2
+
+dnl Negative test check.
+AT_CHECK([ovs-ofctl dump-ipfix-flow br0], [0], [dnl
+OFPT_ERROR (xid=0x2): NXST_NOT_CONFIGURED
+NXST_IPFIX_FLOW request (xid=0x2):
+])
+
+AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \
+ -- --id=@ipfix create IPFIX targets=\"127.0.0.1:4739\" \
+ -- --id=@cs create Flow_Sample_Collector_Set id=1 bridge=@br0 ipfix=@ipfix],
+ [0], [ignore])
+
+AT_DATA([flows.txt], [dnl
+in_port=1, actions=sample(probability=65535,collector_set_id=1),output:2
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt], [0], [ignore])
+
+dnl Send some packets that should be sampled.
+for i in `seq 1 20`; do
+ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)'])
+done
+
+dnl There are 4 extra IPFIX template packets.
+AT_CHECK([ovs-ofctl dump-ipfix-flow br0], [0], [dnl
+NXST_IPFIX_FLOW reply (xid=0x2): 1 ids
+ id 1: flows=20, current flows=0, sampled pkts=20, ipv4 ok=0, ipv6 ok=0, tx pkts=12
+ pkts errs=20, ipv4 errs=20, ipv6 errs=0, tx errs=12
+])
+
+dnl Remove the flow which contains sample action.
+AT_CHECK([ovs-ofctl del-flows br0 in_port=1], [0], [ignore])
+AT_CHECK([ovs-vsctl destroy Flow_Sample_Collector_Set 1], [0], [ignore])
+AT_CHECK([ovs-appctl revalidator/purge])
+
+dnl Send some more packets, to ensure that these are not sampled.
+for i in `seq 1 3`; do
+ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)'])
+done
+AT_CHECK([ovs-ofctl dump-ipfix-flow br0], [0], [dnl
+OFPT_ERROR (xid=0x2): NXST_NOT_CONFIGURED
+NXST_IPFIX_FLOW request (xid=0x2):
+])
+
+OVS_VSWITCHD_STOP(["/sending to collector failed/d"])
+AT_CLEANUP
+
AT_SETUP([ofproto-dpif - flow stats])
OVS_VSWITCHD_START
AT_CHECK([ovs-ofctl add-flow br0 "ip,actions=NORMAL"])
configured queues and because the OpenFlow protocol provides only very
limited information about the configuration of a queue.
.
+.IP "\fBdump\-ipfix\-bridge \fIswitch
+Prints to the console the statistics of bridge IPFIX for \fIswitch\fR.
+If bridge IPFIX is configured on the \fIswitch\fR, IPFIX statistics
+can be retrieved. Otherwise, error message will be printed.
+.IP
+This command uses an Open vSwitch extension that is only in Open
+vSwitch 2.6 and later.
+.
+.IP "\fBdump\-ipfix\-flow \fIswitch
+Prints to the console the statistics of flow-based IPFIX for
+\fIswitch\fR. If flow-based IPFIX is configured on the \fIswitch\fR,
+statistics of all the collector set ids on the \fIswitch\fR will be
+printed. Otherwise, print error message.
+.IP
+Refer to \fBovs-vswitchd.conf.db\fR(5) for more details on configuring
+flow based IPFIX and collector set ids.
+.IP
+This command uses an Open vSwitch extension that is only in Open
+vSwitch 2.6 and later.
+.
.SS "OpenFlow 1.1+ Group Table Commands"
.
The following commands work only with switches that support OpenFlow
Observation Point ID sent in every IPFIX flow record. Defaults to 0.
.RE
.IP
-Refer to \fBovs\-vswitchd.conf.db\fR(8) for more details on
+Refer to \fBovs\-vswitchd.conf.db\fR(5) for more details on
configuring sample collector sets.
.IP
This action was added in Open vSwitch 1.10.90.
" add-tlv-map SWITCH MAP add TLV option MAPpings\n"
" del-tlv-map SWITCH [MAP] delete TLV option MAPpings\n"
" dump-tlv-map SWITCH print TLV option mappings\n"
+ " dump-ipfix-bridge SWITCH print ipfix stats of bridge\n"
+ " dump-ipfix-flow SWITCH print flow ipfix of a bridge\n"
"\nFor OpenFlow switches and controllers:\n"
" probe TARGET probe whether TARGET is up\n"
" ping TARGET [N] latency of N-byte echos\n"
count * message_size / (duration / 1000.0));
}
+static void
+ofctl_dump_ipfix_bridge(struct ovs_cmdl_context *ctx)
+{
+ dump_trivial_transaction(ctx->argv[1], OFPRAW_NXST_IPFIX_BRIDGE_REQUEST);
+}
+
+static void
+ofctl_dump_ipfix_flow(struct ovs_cmdl_context *ctx)
+{
+ dump_trivial_transaction(ctx->argv[1], OFPRAW_NXST_IPFIX_FLOW_REQUEST);
+}
+
static void
ofctl_group_mod__(const char *remote, struct ofputil_group_mod *gms,
size_t n_gms, enum ofputil_protocol usable_protocols)
{ "benchmark", "target n count",
3, 3, ofctl_benchmark },
+ { "dump-ipfix-bridge", "switch",
+ 1, 1, ofctl_dump_ipfix_bridge},
+ { "dump-ipfix-flow", "switch",
+ 1, 1, ofctl_dump_ipfix_flow},
+
{ "ofp-parse", "file",
1, 1, ofctl_ofp_parse },
{ "ofp-parse-pcap", "pcap",