-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial IPFIX implementation #219
Conversation
c6601d0
to
c9bfd43
Compare
upf/upf_ipfix.c
Outdated
/* the value can't be 255 or more due to limitations of pfcp.c, */ | ||
/* thus we use simpler encoding */ | ||
to_b->data[offset] = sx->imsi_len; | ||
clib_memcpy_fast (to_b->data + offset + 1, sx->imsi, sx->imsi_len); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gave this a whirl out of curiosity for the functionality and while pretty much everything works out of the box the imsi ipfix field is not decoded by wireshark or any ipfix collector I've tried. As far as I could see the mobileImsi is sent in the ipfix payload as a string so, considering the imsi has a length of 15 codepoints the provided buffer should be 16 byte buffer (imsi + null terminator). sx->imsi
is currently stored as 8 bytes though.
I guess here you need to format the mobile identify and allocate a 16 buffer for this to work:
diff --git a/upf/upf_ipfix.c b/upf/upf_ipfix.c
index e493271..4756036 100644
--- a/upf/upf_ipfix.c
+++ b/upf/upf_ipfix.c
@@ -470,9 +470,15 @@ upf_ipfix_common_add (vlib_buffer_t * to_b, flow_entry_t * f, flow_direction_t d
/* mobileIMSI */
/* the value can't be 255 or more due to limitations of pfcp.c, */
/* thus we use simpler encoding */
- to_b->data[offset] = sx->imsi_len;
- clib_memcpy_fast (to_b->data + offset + 1, sx->imsi, sx->imsi_len);
- offset += sx->imsi_len + 1;
+
+ // note: imsi string is 15 code points and is currently stored as an 8 bit array (u8* -> sizeof(sx->imsi)=8).
+ // the ipfix rfc state that the imsi is encoded as a string (unicode uses 1 byte per codepoint for the first 128 codepoints). Hence when transfering the imsi in the ipfix payload
+ // we need to pre-allocate a buffer of size 16 before calling format_mobile_identity (15 codepoints + null terminator). This is equivalent to 2 * sx->imsi_len + 1.
+
+ to_b->data[offset] = sx->imsi_len * 2;
+ u8 *imsi = format (NULL, "%U", format_mobile_identity, sx->imsi, sx->imsi_len);
+ clib_memcpy_fast (to_b->data + offset + 1, imsi, 2 * sx->imsi_len);
+ offset += 2 * sx->imsi_len + 1;
Snippet quality might not be the best one ever but works as a PoC for me :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw I also got a segmentation fault on the vpp startup, gdb pointed out at the inability of allocating memory for the flow table. I had to reduce its size to runtime test this:
diff --git a/upf/flowtable.h b/upf/flowtable.h
index e535d87..467242d 100644
--- a/upf/flowtable.h
+++ b/upf/flowtable.h
@@ -207,7 +207,7 @@ typedef struct
* hashtable is configured to alloc (NUM_BUCKETS * CLIB_CACHE_LINE_BYTES) Bytes
* with (flow_count / (BIHASH_KVP_PER_PAGE / 2)) Buckets
*/
-#define FM_POOL_COUNT_LOG2 22
+#define FM_POOL_COUNT_LOG2 15
#define FM_POOL_COUNT (1 << FM_POOL_COUNT_LOG2)
#define FM_NUM_BUCKETS (1 << (FM_POOL_COUNT_LOG2 - (BIHASH_KVP_PER_PAGE / 2)))
#define FM_MEMORY_SIZE (FM_NUM_BUCKETS * CLIB_CACHE_LINE_BYTES * 6)
Doesn't seem to happen on master (nor with the base branch - 22.02) so likely has to do with the ipfix addition - I haven't really tracked the root cause but can provide the stack trace later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the late reply.
Concerning mobileIMSI
field: I had an impression that it must be stored using TBCD encoding, but will recheck
Concerning the segfault (actually SIGABRT I think due to heap exhaustion): you may try increasing the heap size, e.g. by setting heapsize 3G
in startup.conf
a73affd
to
a4a4a6d
Compare
upf/pfcp.c
Outdated
for (i = 0; i < n_bytes; i++) | ||
{ | ||
if (bytes[i] & 0xf0 == 0xf0 && i == n_bytes - 1) | ||
s = format(s, "%d", bytes[i] & 0xf); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A TCBD string can also contain the special characters *
(10), #
(11), 'a' (12), 'b' (13), and 'c' (14).
|
||
for (i = 0; i < n_bytes; i++) | ||
{ | ||
if (bytes[i] & 0xf0 == 0xf0 && i == n_bytes - 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In GTPv1 the IMSI is a fixed length field where unused digits are filled with 2#1111. I might therefore be safer to support a tail of arbitrary many fillers.
Terminating the loop when the lower nibble is 2#1111 is what gtplib is doing. That should work here as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
/* timer management */ | ||
flow_update_active (flow0, current_time); | ||
flow_update_active (flow1, current_time); | ||
|
||
/* | ||
* Should update lifetime after updating flow activity to | ||
* avoid scheduling flows "in the past" | ||
*/ | ||
flow_update_lifetime (flow0, p0, is_ip4); | ||
flow_update_lifetime (flow1, p1, is_ip4); | ||
|
||
/* flow statistics */ | ||
flow0->stats[is_reverse0].pkts++; | ||
flow0->stats[is_reverse0].bytes += b0->current_length; | ||
flow1->stats[is_reverse1].pkts++; | ||
flow1->stats[is_reverse1].bytes += b1->current_length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicating code like this is often use in VPP to reap performance benefits from instruction pipelining.
We should probably micro-bench this and similar places to find out if make sense to keep them like this or move them to helpers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I'm not sure inline funcs will be much worse than repeated code / macros (that is: perhaps the same code will be generated in the end), although perhaps I don't know enough of clang internals to be sure about that. If further testing will reveal performance degradation, I'll take a closer look at this place; although so far the main bottleneck in UPG appears to be insufficient cache locality
/* *INDENT-OFF* */ | ||
VLIB_REGISTER_NODE (flowtable_process_node) = { | ||
.function = flowtable_process, | ||
.type = VLIB_NODE_TYPE_PROCESS, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you made this a VLIB_NODE_TYPE_PRE_INPUT
instead, it would run on every worker. The function could then work on the per_cpu flowtable only and that would mean the barrier_sync in there is not needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just replacing VLIB_NODE_TYPE_PROCESS
with VLIB_NODE_TYPE_PRE_INPUT
causes the code to crash, so it needs a bit more adjustments to do it that way. Perhaps could do it in a follow-up.
This node is disabled by default and is only used in testing situations with low traffic (causing some remaining flows not to expire) and interrupt mode in use.
6e52b12
to
3309fa2
Compare
4068a0a
to
c4ed5bf
Compare
be03c81
to
e1477dd
Compare
In IPFIX tests, there happen to be too many VPP instances active at the same time, leading to memory exhaustion.
Also, make sure that `postNAPTSourceTransportPort` is always correct when NAT is being used. It's necessary to postpone NAT processing upon the first packet to avoid having 0 in this field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overal - LGTM
@@ -444,7 +454,7 @@ VLIB_CLI_COMMAND (upf_nwi_add_del_command, static) = | |||
{ | |||
.path = "upf nwi", | |||
.short_help = | |||
"upf nwi name <name> [table <table-id>] [del]", | |||
"upf nwi name <name> [table <table-id>] [vrf <vrf-id] [ipfix-policy <name>] [del]", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missed >
in vrf
help, but we can keep it and fix some day
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR is too large for an in depth review. I have a few comments, but considering the size of this beast, those could be addressed in a follow up PR.
u32 pkts_unreported; | ||
u64 bytes; | ||
u64 bytes_unreported; | ||
u64 l4_bytes; | ||
u64 l4_bytes_unreported; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, that applies to l4_bytes_unreported and pkts_unreported. There is no user for l4_bytes and bytes_unreported. I wonder if that is an oversight?
@@ -2570,6 +2608,9 @@ handle_session_establishment_request (pfcp_msg_t * msg, | |||
pending->inactivity_timer.handle = ~0; | |||
} | |||
|
|||
if (ISSET_BIT (req->grp.fields, SESSION_ESTABLISHMENT_REQUEST_USER_ID)) | |||
memcpy (&sess->user_id, &req->user_id, sizeof (pfcp_user_id_t)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That doesn't work for the nai
field. We don't use it at the moment, but to avoid problems in the future it would be better initialize it to zero in the copy.
If you wanted to copy it as well, then it needs to freed together with the session
IPFIX_FIELD_PROTOCOL_IDENTIFIER(F) | ||
|
||
#define IPFIX_TEMPLATE_DEFAULT_COMMON(F) \ | ||
IPFIX_FIELD_MOBILE_IMSI(F) \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about IMSI and IMEI ?
TODO in a follow-up:
IMSI in the IPFIX packets is presently encoded using the format described in TS 29.274 Section 8.3.
go-ipfix is being used for E2E testing, fixes:
vmware/go-ipfix#276
vmware/go-ipfix#277