diff --git a/upf/flowtable.h b/upf/flowtable.h
index ee797d8..5211246 100644
--- a/upf/flowtable.h
+++ b/upf/flowtable.h
@@ -144,7 +144,7 @@ typedef struct flow_side_tcp_t_
 
 typedef struct flow_ipfix_t_
 {
-  u32 next_export_at;
+  u32 next_export_at; // in seconds
   u16 context_index;
   u16 forwarding_policy_index;
   // up_dst means "upload destination"
@@ -179,14 +179,18 @@ typedef struct flow_entry
   u8 is_redirect : 1;
   u8 is_l3_proxy : 1;
   u8 is_spliced : 1;
+
   u8 spliced_dirty : 1;
   u8 dont_splice : 1;
   u8 app_detection_done : 1;
-  u8 exported : 1;
+  u8 ipfix_exported : 1; // exported at least once
+
   u8 tcp_state : 4; // TODO: needs only 3 bits?
 
   // should be updated in classify and based on PDR during flow creation
   u8 uplink_direction : 2;
+  // do not need to perform ipfix operations for this flow anymore
+  u8 ipfix_disabled : 1;
 
   // use macro since unsigned will not expand to ~0
   // direction impossible to detect or not yet detected
diff --git a/upf/upf.h b/upf/upf.h
index 1196d40..1e97f8d 100644
--- a/upf/upf.h
+++ b/upf/upf.h
@@ -402,11 +402,20 @@ typedef struct
   upf_pdi_t pdi;
   u8 outer_header_removal;
   u16 far_id;
-  u16 ipfix_cached_context_id;
   u16 *urr_ids;
   u32 *qer_ids;
 } upf_pdr_t;
 
+typedef enum
+{
+  UPF_IPFIX_POLICY_NONE,
+  UPF_IPFIX_POLICY_DEFAULT,
+  UPF_IPFIX_POLICY_DEST,
+  UPF_IPFIX_N_POLICIES,
+  // used only in FAR to indicate "do not override"
+  UPF_IPFIX_POLICY_UNSPECIFIED = UPF_IPFIX_N_POLICIES
+} __clib_packed upf_ipfix_policy_t;
+
 /* Forward Action Rules - Forwarding Parameters */
 typedef struct
 {
@@ -462,15 +471,6 @@ typedef struct
   fib_route_path_t *rpaths;
 } upf_forwarding_policy_t;
 
-typedef enum
-{
-  UPF_IPFIX_POLICY_NONE,
-  UPF_IPFIX_POLICY_DEFAULT,
-  UPF_IPFIX_POLICY_DEST,
-  UPF_IPFIX_N_POLICIES,
-  UPF_IPFIX_POLICY_UNSPECIFIED = UPF_IPFIX_N_POLICIES
-} __clib_packed upf_ipfix_policy_t;
-
 /* Forward Action Rules */
 typedef struct
 {
@@ -805,6 +805,21 @@ typedef struct
   u32 mask;
 } upf_upip_res_t;
 
+typedef struct
+{
+  /* TODO: this contexts can be used in far instead of
+  own bihash lookup */
+  u32 contexts[FIB_PROTOCOL_IP_MAX][UPF_IPFIX_N_POLICIES];
+
+  upf_ipfix_policy_t default_policy;
+  ip_address_t collector_ip;
+  u32 report_interval;
+
+  u32 observation_domain_id;
+  u64 observation_point_id;
+  u8 *observation_domain_name;
+} upf_nwi_ipfix_t;
+
 typedef struct
 {
   u8 *name;
@@ -814,14 +829,7 @@ typedef struct
   u32 sw_if_index;
   u32 hw_if_index;
 
-  upf_ipfix_policy_t ipfix_policy;
-  ip_address_t ipfix_collector_ip;
-  u32 ipfix_report_interval;
-  u32 ipfix_cached_context_id;
-
-  u32 observation_domain_id;
-  u64 observation_point_id;
-  u8 *observation_domain_name;
+  upf_nwi_ipfix_t ipfix;
 } upf_nwi_t;
 
 typedef struct
diff --git a/upf/upf_api.c b/upf/upf_api.c
index 55b9cc0..f4a0057 100644
--- a/upf/upf_api.c
+++ b/upf/upf_api.c
@@ -576,7 +576,7 @@ send_upf_nwi_details (vl_api_registration_t *reg, upf_nwi_t *nwi, u32 context)
   upf_main_t *sm = &upf_main;
   u32 name_len, ipfix_policy_len, observation_domain_name_len;
   u8 *ipfix_policy =
-    format (0, "%U", format_upf_ipfix_policy, nwi->ipfix_policy);
+    format (0, "%U", format_upf_ipfix_policy, nwi->ipfix.default_policy);
 
   name_len = vec_len (nwi->name);
   mp = vl_msg_api_alloc (sizeof (*mp) + name_len * sizeof (u8));
@@ -595,21 +595,22 @@ send_upf_nwi_details (vl_api_registration_t *reg, upf_nwi_t *nwi, u32 context)
   mp->ipfix_policy[ipfix_policy_len] = 0;
 
   mp->ipfix_report_interval =
-    clib_host_to_net_u32 (nwi->ipfix_report_interval);
+    clib_host_to_net_u32 (nwi->ipfix.report_interval);
   mp->observation_domain_id =
-    clib_host_to_net_u32 (nwi->observation_domain_id);
+    clib_host_to_net_u32 (nwi->ipfix.observation_domain_id);
   observation_domain_name_len =
     clib_min (sizeof (mp->observation_domain_name) - 1,
-              vec_len (nwi->observation_domain_name));
-  memcpy (mp->observation_domain_name, nwi->observation_domain_name,
+              vec_len (nwi->ipfix.observation_domain_name));
+  memcpy (mp->observation_domain_name, nwi->ipfix.observation_domain_name,
           observation_domain_name_len);
   mp->observation_domain_name[observation_domain_name_len] = 0;
-  mp->observation_point_id = clib_host_to_net_u64 (nwi->observation_point_id);
+  mp->observation_point_id =
+    clib_host_to_net_u64 (nwi->ipfix.observation_point_id);
 
   memcpy (mp->nwi, nwi->name, name_len);
   mp->nwi_len = name_len;
 
-  ip_address_encode (&ip_addr_46 (&nwi->ipfix_collector_ip), IP46_TYPE_ANY,
+  ip_address_encode (&ip_addr_46 (&nwi->ipfix.collector_ip), IP46_TYPE_ANY,
                      &mp->ipfix_collector_ip);
 
   vl_api_send_msg (reg, (u8 *) mp);
diff --git a/upf/upf_cli.c b/upf/upf_cli.c
index 6ad3bbe..9d3734b 100644
--- a/upf/upf_cli.c
+++ b/upf/upf_cli.c
@@ -551,8 +551,8 @@ upf_show_nwi_command_fn (vlib_main_t *vm, unformat_input_t *main_input,
                        "%U, ipfix-collector-ip %U\n",
                        format_pfcp_dns_labels, nwi->name, fib4->hash.table_id,
                        fib6->table_id, format_upf_ipfix_policy,
-                       nwi->ipfix_policy, format_ip_address,
-                       &nwi->ipfix_collector_ip);
+                       nwi->ipfix.default_policy, format_ip_address,
+                       &nwi->ipfix.collector_ip);
     }
 
 done:
diff --git a/upf/upf_ipfix.c b/upf/upf_ipfix.c
index 1527083..4828bcb 100644
--- a/upf/upf_ipfix.c
+++ b/upf/upf_ipfix.c
@@ -54,20 +54,20 @@ uword upf_ipfix_walker_process (vlib_main_t *vm, vlib_node_runtime_t *rt,
                                 vlib_frame_t *f);
 
 static inline ipfix_exporter_t *
-upf_ipfix_get_exporter (upf_ipfix_protocol_context_t *context)
+upf_ipfix_get_exporter (upf_ipfix_context_t *context)
 {
   flow_report_main_t *frm = &flow_report_main;
   ipfix_exporter_t *exp;
-  bool use_default = ip46_address_is_zero (&context->key.collector_ip);
+
+  bool use_default = ip_address_is_zero (&context->key.collector_ip);
 
   if (context->exporter_index != (u32) ~0 &&
       !pool_is_free_index (frm->exporters, context->exporter_index))
     {
       /* Check if the exporter got replaced */
       exp = pool_elt_at_index (frm->exporters, context->exporter_index);
-      if (use_default ||
-          ip46_address_cmp (&context->key.collector_ip,
-                            &ip_addr_46 (&exp->ipfix_collector)) == 0)
+      if (use_default || ip_address_cmp (&context->key.collector_ip,
+                                         &exp->ipfix_collector) == 0)
         return exp;
     }
 
@@ -77,13 +77,7 @@ upf_ipfix_get_exporter (upf_ipfix_protocol_context_t *context)
       return &frm->exporters[0];
     }
 
-  ip_address_t addr;
-  ip_address_from_46 (&context->key.collector_ip,
-                      ip46_address_is_ip4 (&context->key.collector_ip) ?
-                        FIB_PROTOCOL_IP4 :
-                        FIB_PROTOCOL_IP6,
-                      &addr);
-  exp = vnet_ipfix_exporter_lookup (&addr);
+  exp = vnet_ipfix_exporter_lookup (&context->key.collector_ip);
   context->exporter_index = exp ? exp - frm->exporters : (u32) ~0;
 
   return exp;
@@ -111,27 +105,29 @@ upf_ipfix_template_rewrite (ipfix_exporter_t *exp, flow_report_t *fr,
   ipfix_template_header_t *t;
   ipfix_field_specifier_t *f;
   ipfix_field_specifier_t *first_field;
-  u8 *rewrite = 0;
   ip4_ipfix_template_packet_t *tp;
-  u32 field_count = 0;
-  flow_report_stream_t *stream;
-  upf_ipfix_protocol_context_t *context =
-    pool_elt_at_index (fm->proto_contexts, fr->opaque.as_uword);
-  upf_ipfix_template_t *template = upf_ipfix_templates + context->key.policy;
 
+  u8 *rewrite = 0;
+
+  flow_report_stream_t *stream = &exp->streams[fr->stream_index];
+  upf_ipfix_context_t *context =
+    pool_elt_at_index (fm->contexts, fr->opaque.as_uword);
   ASSERT (context);
-  field_count = context->key.is_ip4 ? template->field_count_ipv4 :
-                                      template->field_count_ipv6;
-  ASSERT (field_count);
 
-  stream = &exp->streams[fr->stream_index];
+  fib_protocol_t fproto =
+    context->key.is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
+  upf_ipfix_template_t *template = &upf_ipfix_templates[context->key.policy];
+  upf_ipfix_template_proto_t *ptemplate = &template->per_ip[fproto];
+
+  ASSERT (ptemplate->field_count);
 
   /* allocate rewrite space */
 
-  vec_validate_aligned (rewrite,
-                        sizeof (ip4_ipfix_template_packet_t) +
-                          field_count * sizeof (ipfix_field_specifier_t) - 1,
-                        CLIB_CACHE_LINE_BYTES);
+  vec_validate_aligned (
+    rewrite,
+    sizeof (ip4_ipfix_template_packet_t) +
+      ptemplate->field_count * sizeof (ipfix_field_specifier_t) - 1,
+    CLIB_CACHE_LINE_BYTES);
 
   tp = (ip4_ipfix_template_packet_t *) rewrite;
   ip = (ip4_header_t *) &tp->ip4;
@@ -154,8 +150,7 @@ upf_ipfix_template_rewrite (ipfix_exporter_t *exp, flow_report_t *fr,
   h->domain_id = clib_host_to_net_u32 (stream->domain_id);
 
   /* Add TLVs to the template */
-  f = context->key.is_ip4 ? template->add_ip4_fields (f) :
-                            template->add_ip6_fields (f);
+  f = ptemplate->add_fields (f);
 
   /* Back to the template packet... */
   ip = (ip4_header_t *) &tp->ip4;
@@ -178,7 +173,7 @@ upf_ipfix_template_rewrite (ipfix_exporter_t *exp, flow_report_t *fr,
              vec_len (rewrite));
   upf_debug ("n of fields: %u, hdr size %u, part hdr size %u, "
              "single field spec len %u",
-             field_count, sizeof (ip4_ipfix_template_packet_t),
+             ptemplate->field_count, sizeof (ip4_ipfix_template_packet_t),
              sizeof (ipfix_template_packet_t),
              sizeof (ipfix_field_specifier_t));
   ASSERT ((u8 *) f - (u8 *) ip == vec_len (rewrite));
@@ -191,12 +186,11 @@ upf_ipfix_template_rewrite (ipfix_exporter_t *exp, flow_report_t *fr,
   return rewrite;
 }
 
-static vlib_buffer_t *
-upf_ipfix_get_buffer (vlib_main_t *vm, upf_ipfix_protocol_context_t *context);
+static vlib_buffer_t *upf_ipfix_get_buffer (vlib_main_t *vm,
+                                            upf_ipfix_context_t *context);
 
 static void upf_ipfix_export_send (vlib_main_t *vm, vlib_buffer_t *b0,
-                                   upf_ipfix_protocol_context_t *context,
-                                   u32 now);
+                                   upf_ipfix_context_t *context, u32 now);
 
 /**
  * @brief Flush accumulated data
@@ -216,8 +210,8 @@ upf_ipfix_data_callback (flow_report_main_t *frm, ipfix_exporter_t *exp,
   upf_ipfix_main_t *fm = &upf_ipfix_main;
   vlib_main_t *vm = vlib_get_main ();
   u32 now = (u32) vlib_time_now (vm);
-  upf_ipfix_protocol_context_t *context =
-    pool_elt_at_index (fm->proto_contexts, fr->opaque.as_uword);
+  upf_ipfix_context_t *context =
+    pool_elt_at_index (fm->contexts, fr->opaque.as_uword);
   vlib_buffer_t *b = upf_ipfix_get_buffer (vm, context);
   if (b)
     upf_ipfix_export_send (vm, b, context, now);
@@ -230,7 +224,8 @@ upf_ipfix_report_add_del (upf_ipfix_main_t *fm, u32 domain_id,
                           u32 context_index, u16 *template_id, bool is_ip4,
                           bool is_add)
 {
-  upf_ipfix_protocol_context_t *context = fm->proto_contexts + context_index;
+  upf_ipfix_context_t *context =
+    pool_elt_at_index (fm->contexts, context_index);
   ipfix_exporter_t *exp = upf_ipfix_get_exporter (context);
   if (!exp)
     return VNET_API_ERROR_INVALID_VALUE;
@@ -259,7 +254,7 @@ upf_ipfix_get_headersize (void)
 
 static void
 upf_ipfix_export_send (vlib_main_t *vm, vlib_buffer_t *b0,
-                       upf_ipfix_protocol_context_t *context, u32 now)
+                       upf_ipfix_context_t *context, u32 now)
 {
   flow_report_main_t *frm = &flow_report_main;
   upf_ipfix_main_t *fm = &upf_ipfix_main;
@@ -280,8 +275,9 @@ upf_ipfix_export_send (vlib_main_t *vm, vlib_buffer_t *b0,
       upf_ipfix_get_headersize ())
     return;
 
-  upf_debug ("export send, context %u", context - fm->proto_contexts);
+  upf_debug ("export send, context %u", context - fm->contexts);
 
+  /* TODO: WHAT WE DO HERE? */
   u32 i, index = vec_len (exp->streams);
   for (i = 0; i < index; i++)
     if (exp->streams[i].domain_id == context->key.observation_domain_id)
@@ -369,7 +365,7 @@ upf_ipfix_export_send (vlib_main_t *vm, vlib_buffer_t *b0,
 }
 
 static vlib_buffer_t *
-upf_ipfix_get_buffer (vlib_main_t *vm, upf_ipfix_protocol_context_t *context)
+upf_ipfix_get_buffer (vlib_main_t *vm, upf_ipfix_context_t *context)
 {
   ipfix_exporter_t *exp = upf_ipfix_get_exporter (context);
   vlib_buffer_t *b0;
@@ -421,21 +417,17 @@ upf_ipfix_flow_init (flow_entry_t *f)
   sx = pool_elt_at_index (gtm->sessions, f->session_index);
   active = pfcp_get_rules (sx, PFCP_ACTIVE);
 
+  /* Get uplink PDR,FAR and output NWI */
+
   up_pdr = flow_pdr (f, FTK_EL_SRC ^ f->uplink_direction, active);
   if (!up_pdr)
     return false;
 
-  if (up_pdr->ipfix_cached_context_id == (u16) ~0)
-    return false;
-
-  upf_ipfix_cached_context_t *cached_ctx =
-    pool_elt_at_index (fm->cached_contexts, up_pdr->ipfix_cached_context_id);
-
   up_far = pfcp_get_far_by_id (active, up_pdr->far_id);
   if (!up_far)
     return false;
 
-  if (up_far->apply_action & FAR_NAT && f->nat_sport == 0)
+  if ((up_far->apply_action & FAR_NAT) && f->nat_sport == 0)
     return false;
 
   if (pool_is_free_index (gtm->nwis, up_far->forward.nwi_index))
@@ -443,14 +435,44 @@ upf_ipfix_flow_init (flow_entry_t *f)
 
   up_dst_nwi = pool_elt_at_index (gtm->nwis, up_far->forward.nwi_index);
 
-  bool is_ip4 = f->key.is_ip4 != 0;
+  /* Detect IPFIX policy for this flow */
+
+  // FAR has priority for policy
+  upf_ipfix_policy_t ipfix_policy = up_far->ipfix_policy;
+  if (ipfix_policy == UPF_IPFIX_POLICY_UNSPECIFIED)
+    ipfix_policy = up_dst_nwi->ipfix.default_policy;
+
+  if (ipfix_policy == UPF_IPFIX_POLICY_NONE)
+    {
+      f->ipfix_disabled = 1;
+      return false;
+    }
+
+  bool is_ip4 = f->key.is_ip4;
+
+  /* Get IPFIX context for this flow */
+
+  /* TODO: possible to use cached contexts from nwi to avoid bihash lookup*/
+  upf_ipfix_context_key_t context_key = { 0 };
+  ip_address_copy (&context_key.collector_ip, &up_dst_nwi->ipfix.collector_ip);
+  context_key.observation_domain_id = up_dst_nwi->ipfix.observation_domain_id;
+  context_key.policy = ipfix_policy;
+  context_key.is_ip4 = is_ip4;
+  u32 ipfix_context_index = upf_ipfix_ensure_context (&context_key);
+  if (ipfix_context_index == ~0)
+    return false;
+
   fib_protocol_t fproto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
 
+  /* Determine forwarding policy index */
+
   if (up_far->forward.flags & FAR_F_FORWARDING_POLICY)
     up_forwarding_policy_index = up_far->forward.fp_pool_index;
   else
     up_forwarding_policy_index = ~0;
 
+  /* Determine output interface */
+
   u32 up_sw_if_index;
   u32 up_fib_index = up_dst_nwi->fib_index[fproto];
   if ((up_far->forward.flags & FAR_F_OUTER_HEADER_CREATION))
@@ -478,28 +500,24 @@ upf_ipfix_flow_init (flow_entry_t *f)
         up_fib_index, &f->key.ip[FTK_EL_DST ^ f->uplink_direction], is_ip4);
     }
 
+  /* Cache detected ipfix values in flow */
+
+  /* TODO: to avoid caching in future we may use
+   * flow[uplink]->pdi->far->forwarding_policy instead, but now pdi/far access
+   * is slow, so do not do this. Same for nwi. */
   f->ipfix.forwarding_policy_index = up_forwarding_policy_index;
   f->ipfix.up_dst_nwi_index = up_dst_nwi - gtm->nwis;
+
+  f->ipfix.context_index = ipfix_context_index;
   f->ipfix.up_dst_sw_if_index = up_sw_if_index;
   f->ipfix.up_dst_fib_index = up_fib_index;
 
-  f->ipfix.context_index = cached_ctx->protocol_context_id[fproto];
-  if (f->ipfix.context_index != (u16) ~0)
-    {
-      upf_ipfix_protocol_context_t *context =
-        pool_elt_at_index (fm->proto_contexts, f->ipfix.context_index);
-
-      ASSERT (!ip46_address_cmp (&context->key.collector_ip,
-                                 &up_dst_nwi->ipfix_collector_ip.ip));
-      ASSERT (context->key.observation_domain_id ==
-              up_dst_nwi->observation_domain_id);
-      ASSERT (context->key.is_ip4 == is_ip4);
-
-      upf_ref_ipfix_proto_context_by_index (f->ipfix.context_index);
-      return true;
-    }
+  upf_ipfix_context_t *context =
+    pool_elt_at_index (fm->contexts, ipfix_context_index);
+  ASSERT (context->key.is_ip4 == is_ip4);
+  ASSERT (context->key.policy == ipfix_policy);
 
-  return false;
+  return true;
 }
 
 static void
@@ -509,7 +527,7 @@ upf_ipfix_export_entry (vlib_main_t *vm, flow_entry_t *f, u32 now, bool last)
   upf_ipfix_main_t *fm = &upf_ipfix_main;
   vlib_buffer_t *b0;
   upf_main_t *gtm = &upf_main;
-  upf_ipfix_protocol_context_t *context;
+  upf_ipfix_context_t *context;
   u16 offset;
   upf_ipfix_template_t *template;
   vnet_main_t *vnm = vnet_get_main ();
@@ -518,9 +536,9 @@ upf_ipfix_export_entry (vlib_main_t *vm, flow_entry_t *f, u32 now, bool last)
     if (!upf_ipfix_flow_init (f))
       return;
 
-  context = pool_elt_at_index (fm->proto_contexts, f->ipfix.context_index);
+  context = pool_elt_at_index (fm->contexts, f->ipfix.context_index);
   offset = context->next_record_offset_per_worker[my_cpu_number];
-  template = upf_ipfix_templates + context->key.policy;
+  template = &upf_ipfix_templates[context->key.policy];
 
   upf_debug ("export entry [%s], policy %u",
              context->key.is_ip4 ? "ip4" : "ip6", context->key.policy);
@@ -535,9 +553,9 @@ upf_ipfix_export_entry (vlib_main_t *vm, flow_entry_t *f, u32 now, bool last)
       return;
     }
 
-  ASSERT (f->key.is_ip4 == context->key.is_ip4);
-
   bool is_ip4 = f->key.is_ip4;
+  ASSERT (context->key.is_ip4 == is_ip4);
+
   fib_protocol_t fproto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
 
   upf_session_t *sx = pool_elt_at_index (gtm->sessions, f->session_index);
@@ -569,16 +587,12 @@ upf_ipfix_export_entry (vlib_main_t *vm, flow_entry_t *f, u32 now, bool last)
       }
   }
 
-  if (context->key.is_ip4)
-    offset += template->add_ip4_values (b0, offset, sx, f, f->uplink_direction,
-                                        nwi, &info, last);
-  else
-    offset += template->add_ip6_values (b0, offset, sx, f, f->uplink_direction,
-                                        nwi, &info, last);
+  offset += template->per_ip[fproto].add_values (
+    b0, offset, sx, f, f->uplink_direction, nwi, &info, last);
 
   /* Reset per flow-export counters */
-  f->ipfix.next_export_at = now + nwi->ipfix_report_interval;
-  f->exported = 1;
+  f->ipfix.next_export_at = now + nwi->ipfix.report_interval;
+  f->ipfix_exported = 1;
 
   b0->current_length = offset;
   context->next_record_offset_per_worker[my_cpu_number] = offset;
@@ -599,7 +613,7 @@ upf_ipfix_flow_stats_update_handler (flow_entry_t *f, u32 now)
   upf_ipfix_main_t *fm = &upf_ipfix_main;
   vlib_main_t *vm = fm->vlib_main;
 
-  if (fm->disabled)
+  if (f->ipfix_disabled)
     return;
 
   if (PREDICT_FALSE (now >= f->ipfix.next_export_at))
@@ -608,97 +622,26 @@ upf_ipfix_flow_stats_update_handler (flow_entry_t *f, u32 now)
   return;
 }
 
-static void
-upf_ipfix_free_flow (flow_entry_t *f)
-{
-  if (f->ipfix.context_index != (u16) ~0)
-    upf_unref_ipfix_proto_context_by_index (f->ipfix.context_index);
-}
-
 void
 upf_ipfix_flow_remove_handler (flow_entry_t *f, u32 now)
 {
   upf_ipfix_main_t *fm = &upf_ipfix_main;
   vlib_main_t *vm = fm->vlib_main;
 
-  if (fm->disabled)
+  if (f->ipfix_disabled)
     return;
 
   upf_ipfix_export_entry (vm, f, now, true);
-  upf_ipfix_free_flow (f);
 }
 
 u32
-upf_ref_ipfix_cached_context (const upf_ipfix_context_key_t *key)
-{
-  clib_bihash_kv_24_8_t kv, value;
-  upf_ipfix_main_t *fm = &upf_ipfix_main;
-  upf_ipfix_cached_context_t *cached_ctx;
-
-  upf_ipfix_context_key_t key_copy = *key;
-  key_copy.is_ip4 = ~0;
-
-  clib_memcpy_fast (&kv.key, &key_copy, sizeof (kv.key));
-
-  if (PREDICT_TRUE (
-        !clib_bihash_search_24_8 (&fm->cached_context_by_key, &kv, &value)))
-    {
-      cached_ctx = pool_elt_at_index (fm->cached_contexts, value.value);
-      clib_atomic_add_fetch (&cached_ctx->refcnt, 1);
-      return value.value;
-    }
-
-  pool_get_zero (fm->cached_contexts, cached_ctx);
-  u32 idx = cached_ctx - fm->cached_contexts;
-  cached_ctx->refcnt = 1;
-  key_copy.is_ip4 = 1;
-  cached_ctx->protocol_context_id[FIB_PROTOCOL_IP4] =
-    upf_ref_ipfix_proto_context (&key_copy);
-
-  key_copy.is_ip4 = 0;
-  cached_ctx->protocol_context_id[FIB_PROTOCOL_IP6] =
-    upf_ref_ipfix_proto_context (&key_copy);
-
-  clib_memcpy_fast (&cached_ctx->key, &kv.key, sizeof (cached_ctx->key));
-
-  kv.value = idx;
-  clib_bihash_add_del_24_8 (&fm->cached_context_by_key, &kv, 1);
-  return idx;
-}
-
-void
-upf_unref_ipfix_cached_context_by_index (u32 idx)
-{
-  upf_ipfix_main_t *fm = &upf_ipfix_main;
-  clib_bihash_kv_24_8_t kv;
-  upf_ipfix_cached_context_t *cached_ctx;
-
-  cached_ctx = pool_elt_at_index (fm->cached_contexts, idx);
-  if (clib_atomic_sub_fetch (&cached_ctx->refcnt, 1))
-    return;
-
-  clib_memcpy_fast (&kv.key, &cached_ctx->key, sizeof (kv.key));
-  clib_bihash_add_del_24_8 (&fm->cached_context_by_key, &kv, 0 /* is_add */);
-
-  if (cached_ctx->protocol_context_id[FIB_PROTOCOL_IP4] != ~0)
-    upf_unref_ipfix_proto_context_by_index (
-      cached_ctx->protocol_context_id[FIB_PROTOCOL_IP4]);
-
-  if (cached_ctx->protocol_context_id[FIB_PROTOCOL_IP6] != ~0)
-    upf_unref_ipfix_proto_context_by_index (
-      cached_ctx->protocol_context_id[FIB_PROTOCOL_IP6]);
-
-  pool_put (fm->cached_contexts, cached_ctx);
-}
-
-u32
-upf_ref_ipfix_proto_context (const upf_ipfix_context_key_t *key)
+upf_ipfix_ensure_context (const upf_ipfix_context_key_t *key)
 {
   int rv;
   vlib_thread_main_t *tm = &vlib_thread_main;
   upf_ipfix_main_t *fm = &upf_ipfix_main;
   clib_bihash_kv_24_8_t kv, value;
-  upf_ipfix_protocol_context_t *context;
+  upf_ipfix_context_t *context;
   /* Decide how many worker threads we have */
   u32 num_threads = 1 /* main thread */ + tm->n_threads;
   u32 idx = ~0;
@@ -706,14 +649,13 @@ upf_ref_ipfix_proto_context (const upf_ipfix_context_key_t *key)
   clib_memcpy_fast (&kv.key, key, sizeof (kv.key));
 
   if (PREDICT_TRUE (
-        !clib_bihash_search_24_8 (&fm->proto_context_by_key, &kv, &value)))
+        !clib_bihash_search_24_8 (&fm->context_by_key, &kv, &value)))
     {
-      context = pool_elt_at_index (fm->proto_contexts, value.value);
-      clib_atomic_add_fetch (&context->refcnt, 1);
+      context = pool_elt_at_index (fm->contexts, value.value);
       return value.value;
     }
 
-  pool_get_zero (fm->proto_contexts, context);
+  pool_get_zero (fm->contexts, context);
 
   vec_validate (context->buffers_per_worker, num_threads - 1);
   vec_validate (context->frames_per_worker, num_threads - 1);
@@ -723,70 +665,24 @@ upf_ref_ipfix_proto_context (const upf_ipfix_context_key_t *key)
 
   /* lookup the exporter a bit later */
   context->exporter_index = (u32) ~0;
-  context->refcnt = 1;
 
-  idx = context - fm->proto_contexts;
+  idx = context - fm->contexts;
   rv = upf_ipfix_report_add_del (fm, key->observation_domain_id, idx,
                                  &context->template_id, key->is_ip4, true);
   if (rv)
     {
       clib_warning ("couldn't add IPFIX report, perhaps "
                     "the exporter has been deleted?");
-      pool_put (fm->proto_contexts, context);
+      pool_put (fm->contexts, context);
       return ~0;
     }
 
   kv.value = idx;
-  clib_bihash_add_del_24_8 (&fm->proto_context_by_key, &kv, 1);
+  clib_bihash_add_del_24_8 (&fm->context_by_key, &kv, 1);
 
   return idx;
 }
 
-void
-upf_ref_ipfix_proto_context_by_index (u32 cidx)
-{
-  upf_ipfix_main_t *fm = &upf_ipfix_main;
-  upf_ipfix_protocol_context_t *context;
-
-  context = pool_elt_at_index (fm->proto_contexts, cidx);
-  clib_atomic_add_fetch (&context->refcnt, 1);
-}
-
-void
-upf_unref_ipfix_proto_context_by_index (u32 cidx)
-{
-  int rv;
-  upf_ipfix_main_t *fm = &upf_ipfix_main;
-  clib_bihash_kv_24_8_t kv;
-  upf_ipfix_protocol_context_t *context;
-
-  context = pool_elt_at_index (fm->proto_contexts, cidx);
-
-  clib_atomic_sub_fetch (&context->refcnt, 1);
-  // Do not remove context since it will use different FlowSet Id on
-  // possible future recreation.
-  return;
-
-  // if (clib_atomic_sub_fetch (&context->refcnt, 1))
-  //   return;
-  //
-  // clib_memcpy_fast (&kv.key, &context->key, sizeof (kv.key));
-  // clib_bihash_add_del_24_8 (&fm->proto_context_by_key, &kv, 0 /* is_add */);
-  //
-  // rv = upf_ipfix_report_add_del (fm, context->key.observation_domain_id,
-  // cidx,
-  //                                &context->template_id, context->key.is_ip4,
-  //                                false);
-  // if (rv)
-  //   clib_warning ("couldn't remove IPFIX report, perhaps "
-  //                 "the exporter has been deleted?");
-  //
-  // vec_free (context->buffers_per_worker);
-  // vec_free (context->frames_per_worker);
-  // vec_free (context->next_record_offset_per_worker);
-  // pool_put (fm->proto_contexts, context);
-}
-
 /**
  * @brief Set up the API message handling tables
  * @param vm vlib_main_t * vlib main data structure pointer
@@ -798,31 +694,18 @@ upf_ipfix_init (vlib_main_t *vm)
   upf_ipfix_main_t *fm = &upf_ipfix_main;
   clib_error_t *error = 0;
 
-  clib_spinlock_init (&fm->lock);
-
-  fm->vnet_main = vnet_get_main ();
   fm->vlib_main = vm; /* FIXME: shouldn't need that */
 
   /* Set up time reference pair */
   fm->vlib_time_0 = (u32) vlib_time_now (vm);
 
   /* initialize the IP/TEID hash's */
-  clib_bihash_init_24_8 (&fm->proto_context_by_key, "proto_context_by_key",
+  clib_bihash_init_24_8 (&fm->context_by_key, "context_by_key",
                          UPF_IPFIX_MAPPING_BUCKETS,
                          UPF_IPFIX_MAPPING_MEMORY_SIZE);
   /* clib_bihash_set_kvp_format_fn_24_8 (&fm->context_by_key, */
   /* 				      format_ipfix_context_key); */
 
-  clib_bihash_init_24_8 (&fm->cached_context_by_key, "cached_context_by_key",
-                         UPF_IPFIX_MAPPING_BUCKETS,
-                         UPF_IPFIX_MAPPING_MEMORY_SIZE);
-
-  clib_bihash_init_24_8 (&fm->info_by_key, "info_by_key",
-                         UPF_IPFIX_MAPPING_BUCKETS,
-                         UPF_IPFIX_MAPPING_MEMORY_SIZE);
-  /* clib_bihash_set_kvp_format_fn_24_8 (&fm->info_by_key, */
-  /* 				      format_ipfix_info_key); */
-
   return error;
 }
 
diff --git a/upf/upf_ipfix.h b/upf/upf_ipfix.h
index 9a9f64e..7422e47 100644
--- a/upf/upf_ipfix.h
+++ b/upf/upf_ipfix.h
@@ -23,10 +23,10 @@ typedef struct
   {
     struct
     {
-      ip46_address_t collector_ip;
-      u32 observation_domain_id;
+      ip_address_t collector_ip;
       upf_ipfix_policy_t policy;
-      u8 is_ip4;
+      bool is_ip4;
+      u32 observation_domain_id;
     };
     u64 key[3];
   };
@@ -38,30 +38,19 @@ typedef struct
 {
   /** Context key */
   upf_ipfix_context_key_t key;
+
   /** ipfix buffers under construction, per-worker thread */
   vlib_buffer_t **buffers_per_worker;
   /** frames containing ipfix buffers, per-worker thread */
   vlib_frame_t **frames_per_worker;
   /** next record offset, per worker thread */
   u16 *next_record_offset_per_worker;
-  /** record size */
-  u16 rec_size;
-  /** IPFIX template id */
+
+  /** current record size **/
+  u16 rec_size; // TODO: follow and document
   u16 template_id;
-  /** Exporter index */
   u32 exporter_index;
-  /** Reference count */
-  u32 refcnt;
-  /* Reporting inerval */
-  u32 reporting_interval;
-} upf_ipfix_protocol_context_t;
-
-typedef struct
-{
-  u32 protocol_context_id[FIB_PROTOCOL_IP_MAX];
-  u32 refcnt;
-  upf_ipfix_context_key_t key;
-} upf_ipfix_cached_context_t;
+} upf_ipfix_context_t;
 
 typedef struct
 {
@@ -75,32 +64,16 @@ typedef struct
  */
 typedef struct
 {
-  clib_spinlock_t lock;
-  clib_bihash_24_8_t proto_context_by_key;
-  clib_bihash_24_8_t cached_context_by_key;
-  clib_bihash_24_8_t info_by_key;
-  upf_ipfix_protocol_context_t *proto_contexts;
-  upf_ipfix_cached_context_t *cached_contexts;
-  u16 template_id;
-  upf_ipfix_policy_t policy;
+  upf_ipfix_context_t *contexts;     // pool of contexts
+  clib_bihash_24_8_t context_by_key; // reusing of contexts by key
 
+  u16 template_id;
   u32 vlib_time_0;
 
-  bool initialized;
-  bool disabled;
-
-  u8 *flow_per_interface;
-
   /** convenience vlib_main_t pointer */
   vlib_main_t *vlib_main;
-  /** convenience vnet_main_t pointer */
-  vnet_main_t *vnet_main;
 } upf_ipfix_main_t;
 
-u8 *format_upf_ipfix_entry (u8 *s, va_list *args);
-
-clib_error_t *upf_ipfix_init (vlib_main_t *vm);
-
 typedef ipfix_field_specifier_t *(*upf_ipfix_field_func_t) (
   ipfix_field_specifier_t *);
 typedef u32 (*upf_ipfix_value_func_t) (vlib_buffer_t *to_b, u16 offset,
@@ -110,28 +83,28 @@ typedef u32 (*upf_ipfix_value_func_t) (vlib_buffer_t *to_b, u16 offset,
                                        upf_ipfix_report_info_t *info,
                                        bool last);
 
+typedef struct
+{
+  u16 field_count;
+  upf_ipfix_field_func_t add_fields;
+  upf_ipfix_value_func_t add_values;
+} upf_ipfix_template_proto_t;
+
 typedef struct
 {
   char *name;
-  u16 field_count_ipv4;
-  u16 field_count_ipv6;
-  upf_ipfix_field_func_t add_ip4_fields;
-  upf_ipfix_field_func_t add_ip6_fields;
-  upf_ipfix_value_func_t add_ip4_values;
-  upf_ipfix_value_func_t add_ip6_values;
+  upf_ipfix_template_proto_t per_ip[FIB_PROTOCOL_IP_MAX];
 } upf_ipfix_template_t;
 
 extern upf_ipfix_template_t upf_ipfix_templates[];
 
-u32 upf_ref_ipfix_proto_context (const upf_ipfix_context_key_t *key);
-void upf_ref_ipfix_proto_context_by_index (u32 cidx);
-void upf_unref_ipfix_proto_context_by_index (u32 cidx);
+clib_error_t *upf_ipfix_init (vlib_main_t *vm);
 
-u32 upf_ref_ipfix_cached_context (const upf_ipfix_context_key_t *key);
-void upf_unref_ipfix_cached_context_by_index (u32 cidx);
+u32 upf_ipfix_ensure_context (const upf_ipfix_context_key_t *key);
 
 upf_ipfix_policy_t upf_ipfix_lookup_policy (u8 *name, bool *ok);
 uword unformat_ipfix_policy (unformat_input_t *i, va_list *args);
 u8 *format_upf_ipfix_policy (u8 *s, va_list *args);
+u8 *format_upf_ipfix_entry (u8 *s, va_list *args);
 
 #endif
diff --git a/upf/upf_ipfix_templates.c b/upf/upf_ipfix_templates.c
index 06caa65..957aca2 100644
--- a/upf/upf_ipfix_templates.c
+++ b/upf/upf_ipfix_templates.c
@@ -154,33 +154,47 @@ upf_ipfix_template_dest_ip6_values (vlib_buffer_t *to_b, u16 offset,
 upf_ipfix_template_t upf_ipfix_templates[UPF_IPFIX_N_POLICIES] = {
   [UPF_IPFIX_POLICY_NONE] = {
     .name = "none",
-    .field_count_ipv4 = 0,
-    .field_count_ipv6 = 0,
+    .per_ip={
+      [FIB_PROTOCOL_IP4] = {
+        .field_count = 0,
+      },
+      [FIB_PROTOCOL_IP6] = {
+        .field_count = 0,
+      },
+    },
   },
   [UPF_IPFIX_POLICY_DEFAULT] = {
     .name = "default",
-    .field_count_ipv4 =
-    IPFIX_TEMPLATE_COUNT (IPFIX_TEMPLATE_DEFAULT_IPV4,
+    .per_ip={
+      [FIB_PROTOCOL_IP4] = {
+        .field_count = IPFIX_TEMPLATE_COUNT (IPFIX_TEMPLATE_DEFAULT_IPV4,
 			  IPFIX_TEMPLATE_DEFAULT_COMMON),
-    .field_count_ipv6 =
-    IPFIX_TEMPLATE_COUNT (IPFIX_TEMPLATE_DEFAULT_IPV6,
+        .add_fields = upf_ipfix_template_default_ip4_fields,
+        .add_values = upf_ipfix_template_default_ip4_values,
+      },
+      [FIB_PROTOCOL_IP6] = {
+        .field_count = IPFIX_TEMPLATE_COUNT (IPFIX_TEMPLATE_DEFAULT_IPV6,
 			  IPFIX_TEMPLATE_DEFAULT_COMMON),
-    .add_ip4_fields = upf_ipfix_template_default_ip4_fields,
-    .add_ip6_fields = upf_ipfix_template_default_ip6_fields,
-    .add_ip4_values = upf_ipfix_template_default_ip4_values,
-    .add_ip6_values = upf_ipfix_template_default_ip6_values,
+        .add_fields = upf_ipfix_template_default_ip6_fields,
+        .add_values = upf_ipfix_template_default_ip6_values,
+      },
+    },
   },
   [UPF_IPFIX_POLICY_DEST] = {
     .name = "dest",
-    .field_count_ipv4 =
-    IPFIX_TEMPLATE_COUNT (IPFIX_TEMPLATE_DEST_IPV4,
+    .per_ip={
+      [FIB_PROTOCOL_IP4] = {
+        .field_count = IPFIX_TEMPLATE_COUNT (IPFIX_TEMPLATE_DEST_IPV4,
 			  IPFIX_TEMPLATE_DEST_COMMON),
-    .field_count_ipv6 =
-    IPFIX_TEMPLATE_COUNT (IPFIX_TEMPLATE_DEST_IPV6,
+        .add_fields = upf_ipfix_template_dest_ip4_fields,
+        .add_values = upf_ipfix_template_dest_ip4_values,
+      },
+      [FIB_PROTOCOL_IP6] = {
+        .field_count = IPFIX_TEMPLATE_COUNT (IPFIX_TEMPLATE_DEST_IPV6,
 			  IPFIX_TEMPLATE_DEST_COMMON),
-    .add_ip4_fields = upf_ipfix_template_dest_ip4_fields,
-    .add_ip6_fields = upf_ipfix_template_dest_ip6_fields,
-    .add_ip4_values = upf_ipfix_template_dest_ip4_values,
-    .add_ip6_values = upf_ipfix_template_dest_ip6_values,
+        .add_fields = upf_ipfix_template_dest_ip6_fields,
+        .add_values = upf_ipfix_template_dest_ip6_values,
+      },
+    },
   },
 };
diff --git a/upf/upf_ipfix_templates.h b/upf/upf_ipfix_templates.h
index b00c6b8..bed6635 100644
--- a/upf/upf_ipfix_templates.h
+++ b/upf/upf_ipfix_templates.h
@@ -209,12 +209,12 @@
 #define IPFIX_FIELD_OBSERVATION_DOMAIN_NAME(F)			\
   F(observationDomainName, 65535,				\
     IPFIX_VALUE_STRING,						\
-    uplink_nwi->observation_domain_name, 				\
-    vec_len (nwi->observation_domain_name), 1)
+    uplink_nwi->ipfix.observation_domain_name, 				\
+    vec_len (nwi->ipfix.bservation_domain_name), 1)
 #define IPFIX_FIELD_OBSERVATION_POINT_ID(F)			\
   F(observationPointId, 8,					\
     IPFIX_VALUE_U64,						\
-    uplink_nwi->observation_point_id,					\
+    uplink_nwi->ipfix.observation_point_id,					\
     sizeof(u64), 1)
 #define IPFIX_FIELD_BIFLOW_DIRECTION(F) \
   F(biflowDirection, 1,\
@@ -227,7 +227,7 @@
 #define IPFIX_FIELD_NAT_EVENT(F)				\
   F(natEvent, 1,						\
     IPFIX_VALUE_U8_COND,					\
-    !f->exported ? UPF_NAT_EVENT_NAT44_SESSION_CREATE :		\
+    !f->ipfix_exported ? UPF_NAT_EVENT_NAT44_SESSION_CREATE :		\
     last ? UPF_NAT_EVENT_NAT44_SESSION_DELETE : 0,		\
     sizeof (u8), 1)
 
diff --git a/upf/upf_pfcp.c b/upf/upf_pfcp.c
index 9448a02..2530822 100644
--- a/upf/upf_pfcp.c
+++ b/upf/upf_pfcp.c
@@ -156,11 +156,12 @@ vnet_upf_create_nwi_if (u8 *name, u32 ip4_table_id, u32 ip6_table_id,
   memset (&nwi->fib_index, ~0, sizeof (nwi->fib_index));
 
   nwi->name = vec_dup (name);
-  nwi->ipfix_policy = ipfix_policy;
+  nwi->ipfix.default_policy = ipfix_policy;
+
   if (ipfix_collector_ip)
-    ip_address_copy (&nwi->ipfix_collector_ip, ipfix_collector_ip);
+    ip_address_copy (&nwi->ipfix.collector_ip, ipfix_collector_ip);
   else
-    ip_address_reset (&nwi->ipfix_collector_ip);
+    ip_address_reset (&nwi->ipfix.collector_ip);
 
   if_index = nwi - gtm->nwis;
 
@@ -231,28 +232,34 @@ vnet_upf_create_nwi_if (u8 *name, u32 ip4_table_id, u32 ip6_table_id,
 
   hash_set_mem (gtm->nwi_index_by_name, nwi->name, if_index);
 
-  nwi->ipfix_report_interval = ipfix_report_interval;
-  nwi->observation_domain_id = observation_domain_id;
-  nwi->observation_domain_name = vec_dup (observation_domain_name);
-  nwi->observation_point_id = observation_point_id;
+  nwi->ipfix.report_interval = ipfix_report_interval;
+  nwi->ipfix.observation_domain_id = observation_domain_id;
+  nwi->ipfix.observation_domain_name = vec_dup (observation_domain_name);
+  nwi->ipfix.observation_point_id = observation_point_id;
 
   if (sw_if_idx)
     *sw_if_idx = nwi->sw_if_index;
 
+  for (fib_protocol_t fproto = 0; fproto < FIB_PROTOCOL_IP_MAX; fproto++)
+    for (upf_ipfix_policy_t pol = 0; pol < UPF_IPFIX_N_POLICIES; pol++)
+      nwi->ipfix.contexts[fproto][pol] = ~0;
+
   // Try to precreate configured ipfix context to start sending ipfix templates
-  if (nwi->ipfix_policy != UPF_IPFIX_POLICY_NONE &&
-      nwi->ipfix_policy != UPF_IPFIX_POLICY_UNSPECIFIED && ipfix_collector_ip)
+  if (ipfix_collector_ip && ipfix_policy != UPF_IPFIX_POLICY_NONE)
     {
       upf_ipfix_context_key_t context_key = { 0 };
-      ip_address_to_46 (&nwi->ipfix_collector_ip, &context_key.collector_ip);
-      context_key.observation_domain_id = nwi->observation_domain_id;
+      ip_address_copy (&context_key.collector_ip, &nwi->ipfix.collector_ip);
+      context_key.observation_domain_id = nwi->ipfix.observation_domain_id;
       context_key.policy = ipfix_policy;
 
-      nwi->ipfix_cached_context_id =
-        upf_ref_ipfix_cached_context (&context_key);
+      context_key.is_ip4 = true;
+      nwi->ipfix.contexts[FIB_PROTOCOL_IP4][ipfix_policy] =
+        upf_ipfix_ensure_context (&context_key);
+
+      context_key.is_ip4 = false;
+      nwi->ipfix.contexts[FIB_PROTOCOL_IP6][ipfix_policy] =
+        upf_ipfix_ensure_context (&context_key);
     }
-  else
-    nwi->ipfix_cached_context_id = ~0;
 
   return 0;
 }
@@ -271,9 +278,6 @@ vnet_upf_delete_nwi_if (u8 *name)
 
   nwi = pool_elt_at_index (gtm->nwis, p[0]);
 
-  if (nwi->ipfix_cached_context_id != ~0)
-    upf_unref_ipfix_cached_context_by_index (nwi->ipfix_cached_context_id);
-
   /* disable nwi if */
   vnet_sw_interface_set_flags (vnm, nwi->sw_if_index, 0 /* down */);
   vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, nwi->sw_if_index);
@@ -286,7 +290,7 @@ vnet_upf_delete_nwi_if (u8 *name)
   vec_add1 (gtm->free_nwi_hw_if_indices, nwi->hw_if_index);
 
   hash_unset_mem (gtm->nwi_index_by_name, nwi->name);
-  vec_free (nwi->observation_domain_name);
+  vec_free (nwi->ipfix.observation_domain_name);
   vec_free (nwi->name);
   pool_put (gtm->nwis, nwi);
 
@@ -974,11 +978,6 @@ pfcp_free_pdr (upf_pdr_t *pdr)
   vec_free (pdr->pdi.acl);
   vec_free (pdr->urr_ids);
   vec_free (pdr->qer_ids);
-  if (pdr->ipfix_cached_context_id != (u16) ~0)
-    {
-      upf_unref_ipfix_cached_context_by_index (pdr->ipfix_cached_context_id);
-      pdr->ipfix_cached_context_id = ~0;
-    }
 }
 
 int
@@ -999,7 +998,6 @@ pfcp_make_pending_pdr (upf_session_t *sx)
         {
           upf_pdr_t *pdr = vec_elt_at_index (pending->pdr, i);
 
-          pdr->ipfix_cached_context_id = ~0;
           pdr->pdi.acl = vec_dup (vec_elt (active->pdr, i).pdi.acl);
           pdr->urr_ids = vec_dup (vec_elt (active->pdr, i).urr_ids);
           pdr->qer_ids = vec_dup (vec_elt (active->pdr, i).qer_ids);
@@ -2230,8 +2228,6 @@ pfcp_update_apply (upf_session_t *sx)
       upf_pdr_t *pdr;
       vec_foreach (pdr, active->pdr)
         {
-          ASSERT (pdr->ipfix_cached_context_id == (u16) ~0);
-
           if (pdr->far_id == (u16) ~0)
             continue;
 
@@ -2245,29 +2241,32 @@ pfcp_update_apply (upf_session_t *sx)
                 pool_elt_at_index (gtm->nwis, far->forward.nwi_index);
 
               upf_ipfix_policy_t policy;
-
-              // FAR has priority for policiy
+              // FAR has priority for policy
               if (far->ipfix_policy != UPF_IPFIX_POLICY_UNSPECIFIED)
                 policy = far->ipfix_policy;
               else
-                policy = nwi->ipfix_policy;
+                policy = nwi->ipfix.default_policy;
 
-              if (policy != UPF_IPFIX_POLICY_NONE &&
-                  policy != UPF_IPFIX_POLICY_UNSPECIFIED)
+              // Force creation of ipfix contexts to send templates early
+              if (policy != UPF_IPFIX_POLICY_NONE)
                 {
                   upf_ipfix_context_key_t context_key = { 0 };
-                  ip_address_to_46 (&nwi->ipfix_collector_ip,
-                                    &context_key.collector_ip);
+                  ip_address_copy (&context_key.collector_ip,
+                                   &nwi->ipfix.collector_ip);
                   context_key.observation_domain_id =
-                    nwi->observation_domain_id;
+                    nwi->ipfix.observation_domain_id;
                   context_key.policy = policy;
-
-                  pdr->ipfix_cached_context_id =
-                    upf_ref_ipfix_cached_context (&context_key);
+                  if (pdr->pdi.ue_addr.flags & PFCP_UE_IP_ADDRESS_V4)
+                    {
+                      context_key.is_ip4 = true;
+                      upf_ipfix_ensure_context (&context_key);
+                    }
+                  if (pdr->pdi.ue_addr.flags & PFCP_UE_IP_ADDRESS_V6)
+                    {
+                      context_key.is_ip4 = false;
+                      upf_ipfix_ensure_context (&context_key);
+                    }
                 }
-              else
-                // explicitly disable ipfix
-                pdr->ipfix_cached_context_id = ~0;
             }
         }
     }
diff --git a/upf/upf_pfcp_api.c b/upf/upf_pfcp_api.c
index 95c78a3..ec03dff 100644
--- a/upf/upf_pfcp_api.c
+++ b/upf/upf_pfcp_api.c
@@ -863,7 +863,6 @@ handle_create_pdr (upf_session_t *sx, pfcp_ie_create_pdr_t *create_pdr,
       create->pdi.nwi_index = ~0;
       create->pdi.adr.application_id = ~0;
       create->pdi.adr.db_id = ~0;
-      create->ipfix_cached_context_id = ~0;
 
       create->id = pdr->pdr_id;
       create->precedence = pdr->precedence;