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;