+ struct netflow_flow *nf_flow;
+ long long int used;
+
+ /* NetFlow only reports on IP packets. */
+ if (flow->dl_type != htons(ETH_TYPE_IP)) {
+ return;
+ }
+
+ ovs_mutex_lock(&mutex);
+ nf_flow = netflow_flow_lookup(nf, flow);
+ if (!nf_flow) {
+ nf_flow = xzalloc(sizeof *nf_flow);
+ nf_flow->in_port = flow->in_port.ofp_port;
+ nf_flow->nw_src = flow->nw_src;
+ nf_flow->nw_dst = flow->nw_dst;
+ nf_flow->nw_tos = flow->nw_tos;
+ nf_flow->nw_proto = flow->nw_proto;
+ nf_flow->tp_src = flow->tp_src;
+ nf_flow->tp_dst = flow->tp_dst;
+ nf_flow->created = stats->used;
+ nf_flow->output_iface = output_iface;
+ hmap_insert(&nf->flows, &nf_flow->hmap_node, netflow_flow_hash(flow));
+ }
+
+ if (nf_flow->output_iface != output_iface) {
+ netflow_expire__(nf, nf_flow);
+ nf_flow->created = stats->used;
+ nf_flow->output_iface = output_iface;
+ }
+
+ nf_flow->packet_count += stats->n_packets;
+ nf_flow->byte_count += stats->n_bytes;
+ nf_flow->tcp_flags |= stats->tcp_flags;
+
+ used = MAX(nf_flow->used, stats->used);
+ if (nf_flow->used != used) {
+ nf_flow->used = used;
+ if (!nf->active_timeout || !nf_flow->last_expired
+ || nf->reconfig_time > nf_flow->last_expired) {
+ /* Keep the time updated to prevent a flood of expiration in
+ * the future. */
+ nf_flow->last_expired = time_msec();
+ }
+ }
+
+ ovs_mutex_unlock(&mutex);
+}
+
+static void
+netflow_expire__(struct netflow *nf, struct netflow_flow *nf_flow)
+ OVS_REQUIRES(mutex)
+{
+ uint64_t pkts, bytes;
+
+ pkts = nf_flow->packet_count;
+ bytes = nf_flow->byte_count;