From patchwork Tue Jun 13 13:25:15 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Przemyslaw Szczerbik X-Patchwork-Id: 775166 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3wn9WW0FNZz9s8N for ; Tue, 13 Jun 2017 23:25:58 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id A5E56B6B; Tue, 13 Jun 2017 13:25:55 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 11842B68 for ; Tue, 13 Jun 2017 13:25:55 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mga06.intel.com (mga06.intel.com [134.134.136.31]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id C77AE213 for ; Tue, 13 Jun 2017 13:25:42 +0000 (UTC) Received: from orsmga002.jf.intel.com ([10.7.209.21]) by orsmga104.jf.intel.com with ESMTP; 13 Jun 2017 06:25:41 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.39,338,1493708400"; d="scan'208";a="98913862" Received: from pszczerx-mobl.ger.corp.intel.com ([10.104.68.122]) by orsmga002.jf.intel.com with ESMTP; 13 Jun 2017 06:25:40 -0700 From: Przemyslaw Szczerbik To: dev@openvswitch.org Date: Tue, 13 Jun 2017 14:25:15 +0100 Message-Id: <20170613132515.18280-1-przemyslawx.szczerbik@intel.com> X-Mailer: git-send-email 2.12.3 X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH] ofproto-dpif-ipfix: add support for per-flow drop counters X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org 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 --- 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(-) 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;