+
+static void
+dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter,
+ bool forced_end, const uint64_t export_time_usec,
+ const uint32_t export_time_sec)
+{
+ struct ipfix_flow_cache_entry *entry, *next_entry;
+ uint64_t max_flow_start_timestamp_usec;
+ bool template_msg_sent = false;
+ enum ipfix_flow_end_reason flow_end_reason;
+
+ if (list_is_empty(&exporter->cache_flow_start_timestamp_list)) {
+ return;
+ }
+
+ max_flow_start_timestamp_usec = export_time_usec -
+ 1000000LL * exporter->cache_active_timeout;
+
+ LIST_FOR_EACH_SAFE (entry, next_entry, cache_flow_start_timestamp_list_node,
+ &exporter->cache_flow_start_timestamp_list) {
+ if (forced_end) {
+ flow_end_reason = FORCED_END;
+ } else if (entry->flow_start_timestamp_usec
+ <= max_flow_start_timestamp_usec) {
+ flow_end_reason = ACTIVE_TIMEOUT;
+ } else if (hmap_count(&exporter->cache_flow_key_map)
+ > exporter->cache_max_flows) {
+ /* Enforce exporter->cache_max_flows. */
+ flow_end_reason = LACK_OF_RESOURCES;
+ } else {
+ /* Remaining flows haven't expired yet. */
+ break;
+ }
+
+ list_remove(&entry->cache_flow_start_timestamp_list_node);
+ hmap_remove(&exporter->cache_flow_key_map,
+ &entry->flow_key_map_node);
+
+ if (!template_msg_sent
+ && (exporter->last_template_set_time + IPFIX_TEMPLATE_INTERVAL)
+ <= export_time_sec) {
+ ipfix_send_template_msg(exporter, export_time_sec,
+ entry->flow_key.obs_domain_id);
+ exporter->last_template_set_time = export_time_sec;
+ template_msg_sent = true;
+ }
+
+ /* XXX: Group multiple data records for the same obs domain id
+ * into the same message. */
+ ipfix_send_data_msg(exporter, export_time_sec, entry, flow_end_reason);
+ free(entry);
+ }
+}
+
+static void
+get_export_time_now(uint64_t *export_time_usec, uint32_t *export_time_sec)
+{
+ struct timeval export_time;
+ xgettimeofday(&export_time);
+
+ *export_time_usec = export_time.tv_usec + 1000000LL * export_time.tv_sec;
+
+ /* The IPFIX start and end deltas are negative deltas relative to
+ * the export time, so set the export time 1 second off to
+ * calculate those deltas. */
+ if (export_time.tv_usec == 0) {
+ *export_time_sec = export_time.tv_sec;
+ } else {
+ *export_time_sec = export_time.tv_sec + 1;
+ }
+}
+
+static void
+dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *exporter,
+ bool forced_end)
+{
+ uint64_t export_time_usec;
+ uint32_t export_time_sec;
+
+ get_export_time_now(&export_time_usec, &export_time_sec);
+ dpif_ipfix_cache_expire(exporter, forced_end, export_time_usec,
+ export_time_sec);
+}
+
+void
+dpif_ipfix_run(struct dpif_ipfix *di) OVS_EXCLUDED(mutex)
+{
+ uint64_t export_time_usec;
+ uint32_t export_time_sec;
+ struct dpif_ipfix_flow_exporter_map_node *flow_exporter_node;
+
+ ovs_mutex_lock(&mutex);
+ get_export_time_now(&export_time_usec, &export_time_sec);
+ if (di->bridge_exporter.probability > 0) { /* Bridge exporter enabled. */
+ dpif_ipfix_cache_expire(
+ &di->bridge_exporter.exporter, false, export_time_usec,
+ export_time_sec);
+ }
+ HMAP_FOR_EACH (flow_exporter_node, node, &di->flow_exporter_map) {
+ dpif_ipfix_cache_expire(
+ &flow_exporter_node->exporter.exporter, false, export_time_usec,
+ export_time_sec);
+ }
+ ovs_mutex_unlock(&mutex);
+}
+
+void
+dpif_ipfix_wait(struct dpif_ipfix *di) OVS_EXCLUDED(mutex)
+{
+ long long int next_timeout_msec = LLONG_MAX;
+ struct dpif_ipfix_flow_exporter_map_node *flow_exporter_node;
+
+ ovs_mutex_lock(&mutex);
+ if (di->bridge_exporter.probability > 0) { /* Bridge exporter enabled. */
+ if (ipfix_cache_next_timeout_msec(
+ &di->bridge_exporter.exporter, &next_timeout_msec)) {
+ poll_timer_wait_until(next_timeout_msec);
+ }
+ }
+ HMAP_FOR_EACH (flow_exporter_node, node, &di->flow_exporter_map) {
+ if (ipfix_cache_next_timeout_msec(
+ &flow_exporter_node->exporter.exporter, &next_timeout_msec)) {
+ poll_timer_wait_until(next_timeout_msec);
+ }
+ }
+ ovs_mutex_unlock(&mutex);
+}