summaryrefslogtreecommitdiff
path: root/ofproto/ofproto-dpif-ipfix.c
diff options
context:
space:
mode:
authorBenli Ye <daniely@vmware.com>2016-06-13 14:44:09 -0700
committerBen Pfaff <blp@ovn.org>2016-06-14 08:27:22 -0700
commitfb8f22c186b89cd36059c37908f940a1aa5e1569 (patch)
treed599a56309b50bdf941cc96689a74a910f3c7e37 /ofproto/ofproto-dpif-ipfix.c
parent4ba9c6bf4775d49732990f649e027733c832bb6a (diff)
downloadopenvswitch-fb8f22c186b89cd36059c37908f940a1aa5e1569.tar.gz
ipfix: Add support for exporting ipfix statistics.
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>
Diffstat (limited to 'ofproto/ofproto-dpif-ipfix.c')
-rw-r--r--ofproto/ofproto-dpif-ipfix.c190
1 files changed, 171 insertions, 19 deletions
diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
index 79ba234ca..d80ff2d8e 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -33,7 +33,6 @@
#include "sset.h"
#include "util.h"
#include "timeval.h"
-#include "util.h"
#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(ipfix);
@@ -48,6 +47,15 @@ static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
#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
@@ -75,6 +83,8 @@ enum dpif_ipfix_tunnel_type {
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. */
@@ -91,6 +101,8 @@ struct dpif_ipfix_exporter {
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 {
@@ -984,17 +996,21 @@ ipfix_init_header(uint32_t export_time_sec, uint32_t seq_number,
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
@@ -1150,20 +1166,23 @@ ipfix_init_template_msg(void *msg_stub, uint32_t export_time_sec,
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
@@ -1172,9 +1191,11 @@ ipfix_send_template_msgs(struct dpif_ipfix_exporter *exporter,
{
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;
@@ -1199,8 +1220,10 @@ ipfix_send_template_msgs(struct dpif_ipfix_exporter *exporter,
*/
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,
@@ -1224,7 +1247,12 @@ ipfix_send_template_msgs(struct dpif_ipfix_exporter *exporter,
}
/* 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. */
@@ -1326,14 +1354,117 @@ ipfix_cache_aggregate_entries(struct ipfix_flow_cache_entry *from_entry,
}
}
+/* 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);
@@ -1348,17 +1479,19 @@ ipfix_cache_update(struct dpif_ipfix_exporter *exporter,
&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,
@@ -1372,6 +1505,7 @@ ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
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;
@@ -1391,12 +1525,15 @@ ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
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:
@@ -1406,17 +1543,21 @@ ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
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) {
@@ -1569,6 +1710,8 @@ ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
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
@@ -1650,14 +1793,20 @@ ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter,
{
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
@@ -1669,13 +1818,16 @@ dpif_ipfix_sample(struct dpif_ipfix_exporter *exporter,
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