diff mbox

[ovs-dev] ofproto-dpif-ipfix: add support for per-flow drop counters

Message ID 20170613132515.18280-1-przemyslawx.szczerbik@intel.com
State Changes Requested
Headers show

Commit Message

Przemyslaw Szczerbik June 13, 2017, 1:25 p.m. UTC
Patch based on RFC 5102, section 5.10. It implements per-flow drop counters:
- droppedPacketDeltaCount
- droppedPacketTotalCount
- droppedOctetDeltaCount
- droppedOctetTotalCount

In order to determine if packet is going to be dropped, flow actions associated
with packet are read. If there is no OVS_ACTION_ATTR_OUTPUT action, packet will
be dropped.

Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
---
 ofproto/ofproto-dpif-ipfix.c  | 110 +++++++++++++++++++++++++++++++++++++-----
 ofproto/ofproto-dpif-ipfix.h  |  16 +++++-
 ofproto/ofproto-dpif-upcall.c |  95 +++++++++++++++++++++++++++---------
 3 files changed, 185 insertions(+), 36 deletions(-)

Comments

Ben Pfaff June 14, 2017, 9:22 p.m. UTC | #1
On Tue, Jun 13, 2017 at 02:25:15PM +0100, Przemyslaw Szczerbik wrote:
> Patch based on RFC 5102, section 5.10. It implements per-flow drop counters:
> - droppedPacketDeltaCount
> - droppedPacketTotalCount
> - droppedOctetDeltaCount
> - droppedOctetTotalCount
> 
> In order to determine if packet is going to be dropped, flow actions associated
> with packet are read. If there is no OVS_ACTION_ATTR_OUTPUT action, packet will
> be dropped.
> 
> Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>

Thanks for working on making the IPFIX support more complete.

The code in this patch that detects whether an output action is present
is incomplete, for a couple of reasons.  First, it skips over "clone"
and "sample" actions that might contain output actions.  An output
action inside "clone" is truly an output.  An output action inside
"sample" is truly an output if the sampling probability is 100% (and the
datapath sometimes uses such a probability for technical reasons).
Whether an output action inside "sample" with a lower probability should
be considered an output is more questionable, but I don't think OVS ever
actually does that.

The "userspace" and "recirc" actions raise bigger questions.  Either of
these can yield absolutely any kind of behavior, since userspace or the
recirculated flow table lookup's actions can do anything.  There's not
an easy way to decide whether a flow that executes one of these actions
truly drops anything.  Without taking a different approach, when one of
these actions is seen (and there isn't another clear output), the most
one can say is that the flow "might" drop the packet.

I am not sure the best way forward.

Thanks,

Ben.
Przemyslaw Szczerbik June 20, 2017, 7:46 a.m. UTC | #2
Hi Ben,

Thanks for your feedback. I can start working on implementing your suggestions.

Please keep in mind that IPFIX counters are based on probability, which means they won't necessarily reflect precise values.
As you can see it's difficult to determine, with 100% accuracy, at sampling stage whether packet is going to be dropped at some point.
I suppose there always is a possibility that next action might drop the packet and it we won't detect it.

I think that this patch (with addition of your requested changes) covers majority of cases which is good enough for IPFIX protocol.

Regards,
Przemek

> -----Original Message-----
> From: Ben Pfaff [mailto:blp@ovn.org]
> Sent: Wednesday, June 14, 2017 10:23 PM
> To: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> Cc: dev@openvswitch.org
> Subject: Re: [ovs-dev] [PATCH] ofproto-dpif-ipfix: add support for per-flow drop
> counters
> 
> On Tue, Jun 13, 2017 at 02:25:15PM +0100, Przemyslaw Szczerbik wrote:
> > Patch based on RFC 5102, section 5.10. It implements per-flow drop counters:
> > - droppedPacketDeltaCount
> > - droppedPacketTotalCount
> > - droppedOctetDeltaCount
> > - droppedOctetTotalCount
> >
> > In order to determine if packet is going to be dropped, flow actions associated
> > with packet are read. If there is no OVS_ACTION_ATTR_OUTPUT action, packet
> will
> > be dropped.
> >
> > Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
> 
> Thanks for working on making the IPFIX support more complete.
> 
> The code in this patch that detects whether an output action is present
> is incomplete, for a couple of reasons.  First, it skips over "clone"
> and "sample" actions that might contain output actions.  An output
> action inside "clone" is truly an output.  An output action inside
> "sample" is truly an output if the sampling probability is 100% (and the
> datapath sometimes uses such a probability for technical reasons).
> Whether an output action inside "sample" with a lower probability should
> be considered an output is more questionable, but I don't think OVS ever
> actually does that.
> 
> The "userspace" and "recirc" actions raise bigger questions.  Either of
> these can yield absolutely any kind of behavior, since userspace or the
> recirculated flow table lookup's actions can do anything.  There's not
> an easy way to decide whether a flow that executes one of these actions
> truly drops anything.  Without taking a different approach, when one of
> these actions is seen (and there isn't another clear output), the most
> one can say is that the flow "might" drop the packet.
> 
> I am not sure the best way forward.
> 
> Thanks,
> 
> Ben.
--------------------------------------------------------------
Intel Research and Development Ireland Limited
Registered in Ireland
Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
Registered Number: 308263


This e-mail and any attachments may contain confidential material for the sole
use of the intended recipient(s). Any review or distribution by others is
strictly prohibited. If you are not the intended recipient, please contact the
sender and delete all copies.
Przemyslaw Szczerbik June 22, 2017, 8:19 a.m. UTC | #3
Hi Ben,

I'm having issues with configuring flows to see "sample" or "clone" actions during IPFIX upcall.
Most of the time I only see "userspace" and "output" actions, which surprises me quite a bit.
Even when I configure per-bridge IPFIX sampling there is no "sample" action.

I'd appreciate any suggestions how to configure flows to see those actions during IPFIX upcall.

Regards,
Przemek

> -----Original Message-----
> From: Szczerbik, PrzemyslawX
> Sent: Tuesday, June 20, 2017 8:46 AM
> To: Ben Pfaff <blp@ovn.org>
> Cc: dev@openvswitch.org
> Subject: RE: [ovs-dev] [PATCH] ofproto-dpif-ipfix: add support for per-flow drop
> counters
> 
> Hi Ben,
> 
> Thanks for your feedback. I can start working on implementing your suggestions.
> 
> Please keep in mind that IPFIX counters are based on probability, which means
> they won't necessarily reflect precise values.
> As you can see it's difficult to determine, with 100% accuracy, at sampling stage
> whether packet is going to be dropped at some point.
> I suppose there always is a possibility that next action might drop the packet and
> it we won't detect it.
> 
> I think that this patch (with addition of your requested changes) covers majority
> of cases which is good enough for IPFIX protocol.
> 
> Regards,
> Przemek
> 
> > -----Original Message-----
> > From: Ben Pfaff [mailto:blp@ovn.org]
> > Sent: Wednesday, June 14, 2017 10:23 PM
> > To: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> > Cc: dev@openvswitch.org
> > Subject: Re: [ovs-dev] [PATCH] ofproto-dpif-ipfix: add support for per-flow
> drop
> > counters
> >
> > On Tue, Jun 13, 2017 at 02:25:15PM +0100, Przemyslaw Szczerbik wrote:
> > > Patch based on RFC 5102, section 5.10. It implements per-flow drop counters:
> > > - droppedPacketDeltaCount
> > > - droppedPacketTotalCount
> > > - droppedOctetDeltaCount
> > > - droppedOctetTotalCount
> > >
> > > In order to determine if packet is going to be dropped, flow actions associated
> > > with packet are read. If there is no OVS_ACTION_ATTR_OUTPUT action,
> packet
> > will
> > > be dropped.
> > >
> > > Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
> >
> > Thanks for working on making the IPFIX support more complete.
> >
> > The code in this patch that detects whether an output action is present
> > is incomplete, for a couple of reasons.  First, it skips over "clone"
> > and "sample" actions that might contain output actions.  An output
> > action inside "clone" is truly an output.  An output action inside
> > "sample" is truly an output if the sampling probability is 100% (and the
> > datapath sometimes uses such a probability for technical reasons).
> > Whether an output action inside "sample" with a lower probability should
> > be considered an output is more questionable, but I don't think OVS ever
> > actually does that.
> >
> > The "userspace" and "recirc" actions raise bigger questions.  Either of
> > these can yield absolutely any kind of behavior, since userspace or the
> > recirculated flow table lookup's actions can do anything.  There's not
> > an easy way to decide whether a flow that executes one of these actions
> > truly drops anything.  Without taking a different approach, when one of
> > these actions is seen (and there isn't another clear output), the most
> > one can say is that the flow "might" drop the packet.
> >
> > I am not sure the best way forward.
> >
> > Thanks,
> >
> > Ben.
--------------------------------------------------------------
Intel Research and Development Ireland Limited
Registered in Ireland
Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
Registered Number: 308263


This e-mail and any attachments may contain confidential material for the sole
use of the intended recipient(s). Any review or distribution by others is
strictly prohibited. If you are not the intended recipient, please contact the
sender and delete all copies.
Przemyslaw Szczerbik June 30, 2017, 9 a.m. UTC | #4
Hi Ben,

Let me provide more details.
I configured per-bridge IPFIX sampling with following command:

$ ovs-vsctl -- set Bridge br1 ipfix=@i -- --id=@i create IPFIX 'targets="127.0.0.1:4739"' \
	obs_domain_id=123 obs_point_id=1 cache_active_timeout=10 \
	cache_max_flows=13 other_config:enable-tunnel-sampling=false sampling=1

When I generate traffic and dump flows I get this output:

$ ovs-appctl dpif/dump-flows br1

recirc_id(0),in_port(7),eth(src=00:00:00:00:00:01,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0800),
ipv4(frag=no), packets:3, bytes:102, used:3.128s,
actions:userspace(pid=4292743264,ipfix(output_port=4294967295)),userspace(pid=4292743264,
		      ipfix(output_port=2)),2,userspace(pid=4292743264,ipfix(output_port=5)),5

As you can see, "userspace" action is present, rather than "sample" action.
I also tried configuring per-flow IPFIX sampling and adding clone action:

$ ovs-vsctl -- --id=@br1 get Bridge br1 -- --id=@cs create Flow_Sample_Collector_Set id=1 \
	bridge=@br1 ipfix=@i -- --id=@i create IPFIX 'targets="127.0.0.1:4739"' \
	obs_domain_id=124 obs_point_id=456

$ ovs-ofctl add-flow br1 'table=0,priority=12, \
	actions=sample(probability=65535,collector_set_id=1,obs_domain_id=124, \
			 obs_point_id=456),resubmit(,1)'
$ ovs-ofctl add-flow br1 'table=1,priority=10,actions=clone(output:2,output:3)'

In this case I also couldn't see "sample" or "clone" action, even though traffic was hitting rules added by me.

$ ovs-ofctl dump-flows br1
 cookie=0x0, duration=7.375s, table=0, n_packets=0, n_bytes=0, priority=0 actions=NORMAL
 cookie=0x0, duration=7.113s, table=0, n_packets=3, n_bytes=102, priority=12
	actions=sample(probability=65535,collector_set_id=1,obs_domain_id=124,obs_point_id=456, resubmit(,1)
 cookie=0x0, duration=7.103s, table=1, n_packets=3, n_bytes=102, priority=10 actions=clone(output:2,output:3)

$ ovs-appctl dpif/dump-flows br1
  recirc_id(0),in_port(7),eth_type(0x0800),ipv4(frag=no),packets:2,bytes:68,used:5.976s,
  actions:userspace(pid=4292741676,flow_sample(probability=65535,collector_set_id=1,
  obs_domain_id=124,obs_point_id=456,output_port=4294967295))

Any ideas how to trigger a "sample" or "clone" action during IPFIX upcall?
I suppose I could somehow force OvS to add those actions with ovs-appctl commands.
However, I'm looking for a usual use case where those actions would be present.
Are you absolutely sure that those actions need to be handled?

Thanks,
Przemek

> -----Original Message-----
> From: Szczerbik, PrzemyslawX
> Sent: Thursday, June 22, 2017 9:20 AM
> To: 'Ben Pfaff' <blp@ovn.org>
> Cc: 'dev@openvswitch.org' <dev@openvswitch.org>
> Subject: RE: [ovs-dev] [PATCH] ofproto-dpif-ipfix: add support for per-flow drop
> counters
> 
> Hi Ben,
> 
> I'm having issues with configuring flows to see "sample" or "clone" actions during
> IPFIX upcall.
> Most of the time I only see "userspace" and "output" actions, which surprises me
> quite a bit.
> Even when I configure per-bridge IPFIX sampling there is no "sample" action.
> 
> I'd appreciate any suggestions how to configure flows to see those actions during
> IPFIX upcall.
> 
> Regards,
> Przemek
> 
> > -----Original Message-----
> > From: Szczerbik, PrzemyslawX
> > Sent: Tuesday, June 20, 2017 8:46 AM
> > To: Ben Pfaff <blp@ovn.org>
> > Cc: dev@openvswitch.org
> > Subject: RE: [ovs-dev] [PATCH] ofproto-dpif-ipfix: add support for per-flow
> drop
> > counters
> >
> > Hi Ben,
> >
> > Thanks for your feedback. I can start working on implementing your
> suggestions.
> >
> > Please keep in mind that IPFIX counters are based on probability, which means
> > they won't necessarily reflect precise values.
> > As you can see it's difficult to determine, with 100% accuracy, at sampling stage
> > whether packet is going to be dropped at some point.
> > I suppose there always is a possibility that next action might drop the packet
> and
> > it we won't detect it.
> >
> > I think that this patch (with addition of your requested changes) covers majority
> > of cases which is good enough for IPFIX protocol.
> >
> > Regards,
> > Przemek
> >
> > > -----Original Message-----
> > > From: Ben Pfaff [mailto:blp@ovn.org]
> > > Sent: Wednesday, June 14, 2017 10:23 PM
> > > To: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> > > Cc: dev@openvswitch.org
> > > Subject: Re: [ovs-dev] [PATCH] ofproto-dpif-ipfix: add support for per-flow
> > drop
> > > counters
> > >
> > > On Tue, Jun 13, 2017 at 02:25:15PM +0100, Przemyslaw Szczerbik wrote:
> > > > Patch based on RFC 5102, section 5.10. It implements per-flow drop
> counters:
> > > > - droppedPacketDeltaCount
> > > > - droppedPacketTotalCount
> > > > - droppedOctetDeltaCount
> > > > - droppedOctetTotalCount
> > > >
> > > > In order to determine if packet is going to be dropped, flow actions
> associated
> > > > with packet are read. If there is no OVS_ACTION_ATTR_OUTPUT action,
> > packet
> > > will
> > > > be dropped.
> > > >
> > > > Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
> > >
> > > Thanks for working on making the IPFIX support more complete.
> > >
> > > The code in this patch that detects whether an output action is present
> > > is incomplete, for a couple of reasons.  First, it skips over "clone"
> > > and "sample" actions that might contain output actions.  An output
> > > action inside "clone" is truly an output.  An output action inside
> > > "sample" is truly an output if the sampling probability is 100% (and the
> > > datapath sometimes uses such a probability for technical reasons).
> > > Whether an output action inside "sample" with a lower probability should
> > > be considered an output is more questionable, but I don't think OVS ever
> > > actually does that.
> > >
> > > The "userspace" and "recirc" actions raise bigger questions.  Either of
> > > these can yield absolutely any kind of behavior, since userspace or the
> > > recirculated flow table lookup's actions can do anything.  There's not
> > > an easy way to decide whether a flow that executes one of these actions
> > > truly drops anything.  Without taking a different approach, when one of
> > > these actions is seen (and there isn't another clear output), the most
> > > one can say is that the flow "might" drop the packet.
> > >
> > > I am not sure the best way forward.
> > >
> > > Thanks,
> > >
> > > Ben.
--------------------------------------------------------------
Intel Research and Development Ireland Limited
Registered in Ireland
Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
Registered Number: 308263


This e-mail and any attachments may contain confidential material for the sole
use of the intended recipient(s). Any review or distribution by others is
strictly prohibited. If you are not the intended recipient, please contact the
sender and delete all copies.
diff mbox

Patch

diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
index 23fc51b..24ad2b9 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -85,6 +85,8 @@  enum dpif_ipfix_tunnel_type {
 typedef struct ofputil_ipfix_stats ofproto_ipfix_stats;
 
 struct dpif_ipfix_global_stats {
+    uint64_t dropped_packet_total_count;
+    uint64_t dropped_octet_total_count;
     uint64_t packet_total_count;
     uint64_t octet_total_count;
     uint64_t octet_total_sum_of_squares;
@@ -363,17 +365,21 @@  OVS_PACKED(
 struct ipfix_data_record_aggregated_common {
     ovs_be32 flow_start_delta_microseconds; /* FLOW_START_DELTA_MICROSECONDS */
     ovs_be32 flow_end_delta_microseconds; /* FLOW_END_DELTA_MICROSECONDS */
+    ovs_be64 dropped_packet_delta_count;  /* DROPPED_PACKET_DELTA_COUNT */
+    ovs_be64 dropped_packet_total_count;  /* DROPPED_PACKET_TOTAL_COUNT */
     ovs_be64 packet_delta_count;  /* PACKET_DELTA_COUNT */
     ovs_be64 packet_total_count;  /* PACKET_TOTAL_COUNT */
     ovs_be64 layer2_octet_delta_count;  /* LAYER2_OCTET_DELTA_COUNT */
     ovs_be64 layer2_octet_total_count;  /* LAYER2_OCTET_TOTAL_COUNT */
     uint8_t flow_end_reason;  /* FLOW_END_REASON */
 });
-BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_common) == 41);
+BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_common) == 57);
 
 /* Part of data record for IP aggregated elements. */
 OVS_PACKED(
 struct ipfix_data_record_aggregated_ip {
+    ovs_be64 dropped_octet_delta_count;  /* DROPPED_OCTET_DELTA_COUNT */
+    ovs_be64 dropped_octet_total_count;  /* DROPPED_OCTET_TOTAL_COUNT */
     ovs_be64 octet_delta_count;  /* OCTET_DELTA_COUNT */
     ovs_be64 octet_total_count;  /* OCTET_TOTAL_COUNT */
     ovs_be64 octet_delta_sum_of_squares;  /* OCTET_DELTA_SUM_OF_SQUARES */
@@ -381,7 +387,7 @@  struct ipfix_data_record_aggregated_ip {
     ovs_be64 minimum_ip_total_length;  /* MINIMUM_IP_TOTAL_LENGTH */
     ovs_be64 maximum_ip_total_length;  /* MAXIMUM_IP_TOTAL_LENGTH */
 });
-BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_ip) == 48);
+BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_ip) == 64);
 
 /* Part of data record for TCP aggregated elements. */
 OVS_PACKED(
@@ -476,10 +482,14 @@  struct ipfix_flow_cache_entry {
     /* Common aggregated elements. */
     uint64_t flow_start_timestamp_usec;
     uint64_t flow_end_timestamp_usec;
+    uint64_t dropped_packet_delta_count;
+    uint64_t dropped_packet_total_count;
     uint64_t packet_delta_count;
     uint64_t packet_total_count;
     uint64_t layer2_octet_delta_count;
     uint64_t layer2_octet_total_count;
+    uint64_t dropped_octet_delta_count;
+    uint64_t dropped_octet_total_count;
     uint64_t octet_delta_count;
     uint64_t octet_total_count;
     uint64_t octet_delta_sum_of_squares;  /* 0 if not IP. */
@@ -1252,6 +1262,8 @@  ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
 
     DEF(FLOW_START_DELTA_MICROSECONDS);
     DEF(FLOW_END_DELTA_MICROSECONDS);
+    DEF(DROPPED_PACKET_DELTA_COUNT);
+    DEF(DROPPED_PACKET_TOTAL_COUNT);
     DEF(PACKET_DELTA_COUNT);
     DEF(PACKET_TOTAL_COUNT);
     DEF(LAYER2_OCTET_DELTA_COUNT);
@@ -1259,6 +1271,8 @@  ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
     DEF(FLOW_END_REASON);
 
     if (l3 != IPFIX_PROTO_L3_UNKNOWN) {
+        DEF(DROPPED_OCTET_DELTA_COUNT);
+        DEF(DROPPED_OCTET_TOTAL_COUNT);
         DEF(OCTET_DELTA_COUNT);
         DEF(OCTET_TOTAL_COUNT);
         DEF(OCTET_DELTA_SUM_OF_SQUARES);
@@ -1466,16 +1480,25 @@  ipfix_cache_aggregate_entries(struct ipfix_flow_cache_entry *from_entry,
         *to_end = *from_end;
     }
 
+
+    to_entry->dropped_packet_delta_count +=
+        from_entry->dropped_packet_delta_count;
     to_entry->packet_delta_count += from_entry->packet_delta_count;
     to_entry->layer2_octet_delta_count += from_entry->layer2_octet_delta_count;
 
+    to_entry->dropped_packet_total_count =
+        from_entry->dropped_packet_total_count;
     to_entry->packet_total_count = from_entry->packet_total_count;
     to_entry->layer2_octet_total_count = from_entry->layer2_octet_total_count;
 
+    to_entry->dropped_octet_delta_count +=
+        from_entry->dropped_octet_delta_count;
     to_entry->octet_delta_count += from_entry->octet_delta_count;
     to_entry->octet_delta_sum_of_squares +=
         from_entry->octet_delta_sum_of_squares;
 
+    to_entry->dropped_octet_total_count =
+        from_entry->dropped_octet_total_count;
     to_entry->octet_total_count = from_entry->octet_total_count;
     to_entry->octet_total_sum_of_squares =
         from_entry->octet_total_sum_of_squares;
@@ -1646,7 +1669,8 @@  ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
                        enum nx_action_sample_direction direction,
                        const struct dpif_ipfix_port *tunnel_port,
                        const struct flow_tnl *tunnel_key,
-                       struct dpif_ipfix_global_stats *stats)
+                       struct dpif_ipfix_global_stats *stats,
+                       const struct dpif_ipfix_actions *ipfix_actions)
 {
     struct ipfix_flow_key *flow_key;
     struct dp_packet msg;
@@ -1840,14 +1864,17 @@  ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
         xgettimeofday(&now);
         entry->flow_end_timestamp_usec = now.tv_usec + 1000000LL * now.tv_sec;
         entry->flow_start_timestamp_usec = entry->flow_end_timestamp_usec;
+        entry->dropped_packet_delta_count = ipfix_actions->output_action ?
+                                            0 : packet_delta_count;
         entry->packet_delta_count = packet_delta_count;
         entry->layer2_octet_delta_count = layer2_octet_delta_count;
 
+        stats->dropped_packet_total_count += entry->dropped_packet_delta_count;
         stats->packet_total_count += packet_delta_count;
         stats->layer2_octet_total_count += layer2_octet_delta_count;
+        entry->dropped_packet_total_count = stats->dropped_packet_total_count;
         entry->packet_total_count = stats->packet_total_count;
         entry->layer2_octet_total_count = stats->layer2_octet_total_count;
-
     }
 
     if (l3 != IPFIX_PROTO_L3_UNKNOWN) {
@@ -1860,11 +1887,14 @@  ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
          * length. */
         octet_delta_count = packet_delta_count * ip_total_length;
 
+        entry->dropped_octet_delta_count = ipfix_actions->output_action ?
+                                           0 : octet_delta_count;
         entry->octet_delta_count = octet_delta_count;
         entry->octet_delta_sum_of_squares = octet_delta_count * ip_total_length;
         entry->minimum_ip_total_length = ip_total_length;
         entry->maximum_ip_total_length = ip_total_length;
 
+        stats->dropped_octet_total_count += entry->dropped_octet_delta_count;
         stats->octet_total_count += octet_delta_count;
         stats->octet_total_sum_of_squares += entry->octet_delta_sum_of_squares;
 
@@ -1874,8 +1904,9 @@  ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
         entry->maximum_ip_total_length = 0;
     }
 
-    entry->octet_total_sum_of_squares = stats->octet_total_sum_of_squares;
+    entry->dropped_octet_total_count = stats->dropped_octet_total_count;
     entry->octet_total_count = stats->octet_total_count;
+    entry->octet_total_sum_of_squares = stats->octet_total_sum_of_squares;
 
     if (l4 == IPFIX_PROTO_L4_TCP) {
         uint16_t tcp_flags = ntohs(flow->tcp_flags);
@@ -1965,6 +1996,10 @@  ipfix_put_data_set(uint32_t export_time_sec,
             flow_start_delta_usec);
         data_aggregated_common->flow_end_delta_microseconds = htonl(
             flow_end_delta_usec);
+        data_aggregated_common->dropped_packet_delta_count = htonll(
+            entry->dropped_packet_delta_count);
+        data_aggregated_common->dropped_packet_total_count = htonll(
+            entry->dropped_packet_total_count);
         data_aggregated_common->packet_delta_count = htonll(
             entry->packet_delta_count);
         data_aggregated_common->packet_total_count = htonll(
@@ -1981,6 +2016,10 @@  ipfix_put_data_set(uint32_t export_time_sec,
 
         data_aggregated_ip = dp_packet_put_zeros(
             msg, sizeof *data_aggregated_ip);
+        data_aggregated_ip->dropped_octet_delta_count = htonll(
+            entry->dropped_octet_delta_count);
+        data_aggregated_ip->dropped_octet_total_count = htonll(
+            entry->dropped_octet_total_count);
         data_aggregated_ip->octet_delta_count = htonll(
             entry->octet_delta_count);
         data_aggregated_ip->octet_total_count = htonll(
@@ -2053,7 +2092,8 @@  dpif_ipfix_sample(struct dpif_ipfix_exporter *exporter,
                   uint32_t obs_point_id, odp_port_t output_odp_port,
                   enum nx_action_sample_direction direction,
                   const struct dpif_ipfix_port *tunnel_port,
-                  const struct flow_tnl *tunnel_key)
+                  const struct flow_tnl *tunnel_key,
+                  const struct dpif_ipfix_actions *ipfix_actions)
 {
     struct ipfix_flow_cache_entry *entry;
     enum ipfix_sampled_packet_type sampled_packet_type;
@@ -2066,7 +2106,8 @@  dpif_ipfix_sample(struct dpif_ipfix_exporter *exporter,
                                    obs_domain_id, obs_point_id,
                                    output_odp_port, direction,
                                    tunnel_port, tunnel_key,
-                                   &exporter->ipfix_global_stats);
+                                   &exporter->ipfix_global_stats,
+                                   ipfix_actions);
 
     ipfix_cache_update(exporter, entry, sampled_packet_type);
 }
@@ -2081,7 +2122,8 @@  void
 dpif_ipfix_bridge_sample(struct dpif_ipfix *di, const struct dp_packet *packet,
                          const struct flow *flow,
                          odp_port_t input_odp_port, odp_port_t output_odp_port,
-                         const struct flow_tnl *output_tunnel_key)
+                         const struct flow_tnl *output_tunnel_key,
+                         const struct dpif_ipfix_actions *ipfix_actions)
     OVS_EXCLUDED(mutex)
 {
     uint64_t packet_delta_count;
@@ -2131,7 +2173,7 @@  dpif_ipfix_bridge_sample(struct dpif_ipfix *di, const struct dp_packet *packet,
                       di->bridge_exporter.options->obs_domain_id,
                       di->bridge_exporter.options->obs_point_id,
                       output_odp_port, NX_ACTION_SAMPLE_DEFAULT,
-                      tunnel_port, tunnel_key);
+                      tunnel_port, tunnel_key, ipfix_actions);
     ovs_mutex_unlock(&mutex);
 }
 
@@ -2140,7 +2182,8 @@  dpif_ipfix_flow_sample(struct dpif_ipfix *di, const struct dp_packet *packet,
                        const struct flow *flow,
                        const union user_action_cookie *cookie,
                        odp_port_t input_odp_port,
-                       const struct flow_tnl *output_tunnel_key)
+                       const struct flow_tnl *output_tunnel_key,
+                       const struct dpif_ipfix_actions *ipfix_actions)
     OVS_EXCLUDED(mutex)
 {
     struct dpif_ipfix_flow_exporter_map_node *node;
@@ -2175,7 +2218,7 @@  dpif_ipfix_flow_sample(struct dpif_ipfix *di, const struct dp_packet *packet,
                           cookie->flow_sample.obs_domain_id,
                           cookie->flow_sample.obs_point_id,
                           output_odp_port, cookie->flow_sample.direction,
-                          tunnel_port, tunnel_key);
+                          tunnel_port, tunnel_key, ipfix_actions);
     }
     ovs_mutex_unlock(&mutex);
 }
@@ -2306,3 +2349,48 @@  dpif_ipfix_wait(struct dpif_ipfix *di) OVS_EXCLUDED(mutex)
     }
     ovs_mutex_unlock(&mutex);
 }
+
+void
+dpif_ipfix_read_actions(const struct flow *flow OVS_UNUSED,
+                        const struct nlattr *actions,
+                        size_t actions_len,
+                        struct dpif_ipfix_actions *ipfix_actions)
+{
+    const struct nlattr *a;
+    unsigned int left;
+
+    if (actions_len == 0) {
+        return;
+    }
+
+    NL_ATTR_FOR_EACH (a, left, actions, actions_len) {
+        enum ovs_action_attr type = nl_attr_type(a);
+        switch (type) {
+        case OVS_ACTION_ATTR_OUTPUT:
+            ipfix_actions->output_action = true;
+            break;
+        case OVS_ACTION_ATTR_TUNNEL_POP:
+        case OVS_ACTION_ATTR_TUNNEL_PUSH:
+        case OVS_ACTION_ATTR_TRUNC:
+        case OVS_ACTION_ATTR_USERSPACE:
+        case OVS_ACTION_ATTR_RECIRC:
+        case OVS_ACTION_ATTR_HASH:
+        case OVS_ACTION_ATTR_CT:
+        case OVS_ACTION_ATTR_METER:
+        case OVS_ACTION_ATTR_SET_MASKED:
+        case OVS_ACTION_ATTR_SET:
+        case OVS_ACTION_ATTR_PUSH_VLAN:
+        case OVS_ACTION_ATTR_POP_VLAN:
+        case OVS_ACTION_ATTR_PUSH_MPLS:
+        case OVS_ACTION_ATTR_POP_MPLS:
+        case OVS_ACTION_ATTR_PUSH_ETH:
+        case OVS_ACTION_ATTR_POP_ETH:
+        case OVS_ACTION_ATTR_SAMPLE:
+        case OVS_ACTION_ATTR_CLONE:
+        case OVS_ACTION_ATTR_UNSPEC:
+        case __OVS_ACTION_ATTR_MAX:
+        default:
+            break;
+        }
+    }
+}
diff --git a/ofproto/ofproto-dpif-ipfix.h b/ofproto/ofproto-dpif-ipfix.h
index 0808ede..f91d041 100644
--- a/ofproto/ofproto-dpif-ipfix.h
+++ b/ofproto/ofproto-dpif-ipfix.h
@@ -29,6 +29,11 @@  struct ofproto_ipfix_flow_exporter_options;
 struct flow_tnl;
 struct ofport;
 
+struct dpif_ipfix_actions {
+    bool output_action;     /* Set to true if packet has at least one
+                               OVS_ACTION_ATTR_OUTPUT action. */
+};
+
 struct dpif_ipfix *dpif_ipfix_create(void);
 struct dpif_ipfix *dpif_ipfix_ref(const struct dpif_ipfix *);
 void dpif_ipfix_unref(struct dpif_ipfix *);
@@ -52,12 +57,19 @@  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 *);
+                              odp_port_t, odp_port_t, const struct flow_tnl *,
+                              const struct dpif_ipfix_actions *);
 void dpif_ipfix_flow_sample(struct dpif_ipfix *, const struct dp_packet *,
                             const struct flow *, const union user_action_cookie *,
-                            odp_port_t, const struct flow_tnl *);
+                            odp_port_t, const struct flow_tnl *,
+                            const struct dpif_ipfix_actions *);
 
 void dpif_ipfix_run(struct dpif_ipfix *);
 void dpif_ipfix_wait(struct dpif_ipfix *);
 
+void dpif_ipfix_read_actions(const struct flow *flow,
+                             const struct nlattr *actions,
+                             size_t actions_len,
+                             struct dpif_ipfix_actions *ipfix_actions);
+
 #endif /* ofproto/ofproto-dpif-ipfix.h */
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index caa3288..ed9f891 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -1278,6 +1278,59 @@  out:
     return error;
 }
 
+static size_t
+dpif_get_actions(struct udpif *udpif, struct upcall *upcall,
+                 const struct nlattr **actions)
+{
+    size_t actions_len = 0;
+
+    if (upcall->actions) {
+        /* Actions were passed up from datapath. */
+        *actions = nl_attr_get(upcall->actions);
+        actions_len = nl_attr_get_size(upcall->actions);
+    }
+
+    if (actions_len == 0) {
+        /* Lookup actions in userspace cache. */
+        struct udpif_key *ukey = ukey_lookup(udpif, upcall->ufid,
+                                             upcall->pmd_id);
+        if (ukey) {
+            ukey_get_actions(ukey, actions, &actions_len);
+        }
+    }
+
+    return actions_len;
+}
+
+static size_t
+dpif_read_actions(struct udpif *udpif, struct upcall *upcall,
+                  const struct flow *flow, enum upcall_type type,
+                  void *upcall_data)
+{
+    const struct nlattr *actions = NULL;
+    size_t actions_len = dpif_get_actions(udpif, upcall, &actions);
+
+    if (!actions || !actions_len) {
+        return 0;
+    }
+
+    switch (type) {
+    case SFLOW_UPCALL:
+        dpif_sflow_read_actions(flow, actions, actions_len, upcall_data);
+        break;
+    case FLOW_SAMPLE_UPCALL:
+    case IPFIX_UPCALL:
+        dpif_ipfix_read_actions(flow, actions, actions_len, upcall_data);
+        break;
+    case BAD_UPCALL:
+    case MISS_UPCALL:
+    default:
+        break;
+    }
+
+    return actions_len;
+}
+
 static int
 process_upcall(struct udpif *udpif, struct upcall *upcall,
                struct ofpbuf *odp_actions, struct flow_wildcards *wc)
@@ -1285,8 +1338,10 @@  process_upcall(struct udpif *udpif, struct upcall *upcall,
     const struct nlattr *userdata = upcall->userdata;
     const struct dp_packet *packet = upcall->packet;
     const struct flow *flow = upcall->flow;
+    size_t actions_len = 0;
+    enum upcall_type upcall_type = classify_upcall(upcall->type, userdata);
 
-    switch (classify_upcall(upcall->type, userdata)) {
+    switch (upcall_type) {
     case MISS_UPCALL:
         upcall_xlate(udpif, upcall, odp_actions, wc);
         return 0;
@@ -1294,31 +1349,14 @@  process_upcall(struct udpif *udpif, struct upcall *upcall,
     case SFLOW_UPCALL:
         if (upcall->sflow) {
             union user_action_cookie cookie;
-            const struct nlattr *actions;
-            size_t actions_len = 0;
             struct dpif_sflow_actions sflow_actions;
+
             memset(&sflow_actions, 0, sizeof sflow_actions);
             memset(&cookie, 0, sizeof cookie);
             memcpy(&cookie, nl_attr_get(userdata), sizeof cookie.sflow);
-            if (upcall->actions) {
-                /* Actions were passed up from datapath. */
-                actions = nl_attr_get(upcall->actions);
-                actions_len = nl_attr_get_size(upcall->actions);
-                if (actions && actions_len) {
-                    dpif_sflow_read_actions(flow, actions, actions_len,
-                                            &sflow_actions);
-                }
-            }
-            if (actions_len == 0) {
-                /* Lookup actions in userspace cache. */
-                struct udpif_key *ukey = ukey_lookup(udpif, upcall->ufid,
-                                                     upcall->pmd_id);
-                if (ukey) {
-                    ukey_get_actions(ukey, &actions, &actions_len);
-                    dpif_sflow_read_actions(flow, actions, actions_len,
+
+            actions_len = dpif_read_actions(udpif, upcall, flow, upcall_type,
                                             &sflow_actions);
-                }
-            }
             dpif_sflow_received(upcall->sflow, packet, flow,
                                 flow->in_port.odp_port, &cookie,
                                 actions_len > 0 ? &sflow_actions : NULL);
@@ -1329,18 +1367,24 @@  process_upcall(struct udpif *udpif, struct upcall *upcall,
         if (upcall->ipfix) {
             union user_action_cookie cookie;
             struct flow_tnl output_tunnel_key;
+            struct dpif_ipfix_actions ipfix_actions;
 
             memset(&cookie, 0, sizeof cookie);
             memcpy(&cookie, nl_attr_get(userdata), sizeof cookie.ipfix);
+            memset(&ipfix_actions, 0, sizeof ipfix_actions);
 
             if (upcall->out_tun_key) {
                 odp_tun_key_from_attr(upcall->out_tun_key, &output_tunnel_key);
             }
+
+            actions_len = dpif_read_actions(udpif, upcall, flow, upcall_type,
+                                            &ipfix_actions);
             dpif_ipfix_bridge_sample(upcall->ipfix, packet, flow,
                                      flow->in_port.odp_port,
                                      cookie.ipfix.output_odp_port,
                                      upcall->out_tun_key ?
-                                         &output_tunnel_key : NULL);
+                                         &output_tunnel_key : NULL,
+                                     actions_len > 0 ? &ipfix_actions: NULL);
         }
         break;
 
@@ -1348,20 +1392,25 @@  process_upcall(struct udpif *udpif, struct upcall *upcall,
         if (upcall->ipfix) {
             union user_action_cookie cookie;
             struct flow_tnl output_tunnel_key;
+            struct dpif_ipfix_actions ipfix_actions;
 
             memset(&cookie, 0, sizeof cookie);
             memcpy(&cookie, nl_attr_get(userdata), sizeof cookie.flow_sample);
+            memset(&ipfix_actions, 0, sizeof ipfix_actions);
 
             if (upcall->out_tun_key) {
                 odp_tun_key_from_attr(upcall->out_tun_key, &output_tunnel_key);
             }
 
+            actions_len = dpif_read_actions(udpif, upcall, flow, upcall_type,
+                                            &ipfix_actions);
             /* The flow reflects exactly the contents of the packet.
              * Sample the packet using it. */
             dpif_ipfix_flow_sample(upcall->ipfix, packet, flow,
                                    &cookie, flow->in_port.odp_port,
                                    upcall->out_tun_key ?
-                                       &output_tunnel_key : NULL);
+                                       &output_tunnel_key : NULL,
+                                   actions_len > 0 ? &ipfix_actions: NULL);
         }
         break;