NVIDIA DOCA SDK Data Center on a Chip Framework Documentation
packet_parser.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2025 NVIDIA CORPORATION AND AFFILIATES. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are permitted
5  * provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright notice, this list of
7  * conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright notice, this list of
9  * conditions and the following disclaimer in the documentation and/or other materials
10  * provided with the distribution.
11  * * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
12  * to endorse or promote products derived from this software without specific prior written
13  * permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
21  * STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  */
25 
26 #include <rte_ip_frag.h>
27 #include <rte_ether.h>
28 #include <rte_tcp.h>
29 #include <rte_udp.h>
30 #include <rte_gtp.h>
31 #include <rte_ip.h>
32 
33 #include <doca_flow_net.h>
34 #include <doca_bitfield.h>
35 #include <doca_log.h>
36 
37 #include "packet_parser.h"
38 
39 DOCA_LOG_REGISTER(PACKET_PARSER);
40 
41 doca_error_t link_parse(uint8_t *data, uint8_t *data_end, struct link_parser_ctx *ctx)
42 {
43  struct rte_ether_hdr *eth;
44  uint16_t next_proto;
45 
46  eth = (struct rte_ether_hdr *)data;
47  if (data + sizeof(*eth) > data_end) {
48  DOCA_LOG_DBG("Error parsing Ethernet header");
50  }
51 
52  next_proto = rte_be_to_cpu_16(eth->ether_type);
53  if (next_proto != DOCA_FLOW_ETHER_TYPE_IPV4 && next_proto != DOCA_FLOW_ETHER_TYPE_IPV6) {
54  DOCA_LOG_DBG("Unsupported L3 type 0x%x", eth->ether_type);
56  }
57 
58  ctx->len = sizeof(*eth);
59  ctx->next_proto = next_proto;
60  ctx->eth = eth;
61  return DOCA_SUCCESS;
62 }
63 
64 /*
65  * Parse IPv6 header and its extension headers, accumulating their total length in the process
66  *
67  * @data [in]: pointer to the start of the data
68  * @data_end [in]: pointer to the end of the data
69  * @ctx [out]: pointer to the parser context
70  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
71  */
72 static inline doca_error_t ipv6_hdr_parse(const uint8_t *data, const uint8_t *data_end, struct network_parser_ctx *ctx)
73 {
74  struct rte_ipv6_hdr *ipv6_hdr = (struct rte_ipv6_hdr *)data;
75  struct rte_ipv6_fragment_ext *ipv6_frag_ext = NULL;
76  size_t total_len = sizeof(*ipv6_hdr);
77  uint8_t next_proto;
78  size_t ext_len;
79  int proto;
80 
81  if (data + total_len > data_end) {
82  DOCA_LOG_DBG("Error parsing IPv6 header");
84  }
85  proto = ipv6_hdr->proto;
86 
87  while (proto != -EINVAL) {
88  if (proto == IPPROTO_FRAGMENT)
89  ipv6_frag_ext = (struct rte_ipv6_fragment_ext *)(data + total_len);
90 
91  next_proto = proto;
92  ext_len = 0;
93  proto = rte_ipv6_get_next_ext(data + total_len, proto, &ext_len);
94  total_len += ext_len;
95  if (data + total_len > data_end) {
96  DOCA_LOG_DBG("Error parsing IPv6 header with extensions");
98  }
99  }
100 
101  ctx->next_proto = next_proto;
102  ctx->ipv6.hdr = ipv6_hdr;
103  if (ipv6_frag_ext) {
104  ctx->frag = true;
105  ctx->ipv6.frag_ext = ipv6_frag_ext;
106  }
107  ctx->len = total_len;
108 
109  return DOCA_SUCCESS;
110 }
111 
112 doca_error_t network_parse(uint8_t *data, uint8_t *data_end, uint16_t expected_proto, struct network_parser_ctx *ctx)
113 {
114  struct rte_ipv4_hdr *ipv4_hdr;
115  uint16_t version;
116  doca_error_t ret;
117  uint8_t hdr_len;
118 
119  if (data == data_end) {
120  DOCA_LOG_DBG("Error parsing IP header");
122  }
123  version = *data >> 4;
124 
125  switch (version) {
126  case 4:
127  if (expected_proto && expected_proto != DOCA_FLOW_ETHER_TYPE_IPV4) {
128  DOCA_LOG_DBG("Expected IPv4 header, got version 0x%x", version);
130  }
131  ctx->ip_version = DOCA_FLOW_PROTO_IPV4;
132 
133  ipv4_hdr = (struct rte_ipv4_hdr *)data;
134  if (data + sizeof(*ipv4_hdr) > data_end) {
135  DOCA_LOG_DBG("Error parsing IPv4 header");
137  }
138  hdr_len = rte_ipv4_hdr_len(ipv4_hdr);
139  if (data + hdr_len > data_end) {
140  DOCA_LOG_DBG("Error parsing IPv4 header options");
142  }
143 
144  ctx->len = hdr_len;
145  ctx->next_proto = ipv4_hdr->next_proto_id;
146  ctx->frag = rte_ipv4_frag_pkt_is_fragmented(ipv4_hdr);
147  ctx->ipv4_hdr = ipv4_hdr;
148  break;
149  case 6:
150  if (expected_proto && expected_proto != DOCA_FLOW_ETHER_TYPE_IPV6) {
151  DOCA_LOG_DBG("Expected IPv6 header, got version 0x%x", version);
153  }
154  ctx->ip_version = DOCA_FLOW_PROTO_IPV6;
155 
156  ret = ipv6_hdr_parse(data, data_end, ctx);
157  if (ret != DOCA_SUCCESS) {
158  DOCA_LOG_DBG("Error parsing IPv6 header");
160  }
161  break;
162  default:
163  DOCA_LOG_DBG("Unsupported IP version 0x%x", version);
165  }
166 
167  return DOCA_SUCCESS;
168 }
169 
170 doca_error_t transport_parse(uint8_t *data, uint8_t *data_end, uint8_t proto, struct transport_parser_ctx *ctx)
171 {
172  struct rte_tcp_hdr *tcp_hdr;
173  struct rte_udp_hdr *udp_hdr;
174  uint8_t hdr_len;
175 
176  switch (proto) {
177  case DOCA_FLOW_PROTO_TCP:
178  tcp_hdr = (struct rte_tcp_hdr *)data;
179 
180  if (data + sizeof(*tcp_hdr) > data_end) {
181  DOCA_LOG_DBG("Error parsing TCP header");
183  }
184  hdr_len = rte_tcp_hdr_len(tcp_hdr);
185  if (data + hdr_len > data_end) {
186  DOCA_LOG_DBG("Error parsing TCP options");
188  }
189 
190  ctx->len = hdr_len;
191  ctx->tcp_hdr = tcp_hdr;
192  break;
193  case DOCA_FLOW_PROTO_UDP:
194  udp_hdr = (struct rte_udp_hdr *)data;
195 
196  if (data + sizeof(*udp_hdr) > data_end) {
197  DOCA_LOG_DBG("Error parsing UDP header");
199  }
200 
201  ctx->len = sizeof(*udp_hdr);
202  ctx->udp_hdr = udp_hdr;
203  break;
204  default:
205  DOCA_LOG_WARN("Unsupported L4 %d", proto);
207  }
208 
209  ctx->proto = proto;
210  return DOCA_SUCCESS;
211 }
212 
213 doca_error_t gtpu_parse(uint8_t *data, uint8_t *data_end, struct gtp_parser_ctx *ctx)
214 {
215  struct rte_gtp_psc_type0_hdr *gtpext_hdr = NULL;
216  struct rte_gtp_hdr_ext_word *gtpopt_hdr = NULL;
217  struct rte_gtp_hdr *gtp_hdr;
218  uint8_t *data_beg = data;
219  size_t gtpext_len;
220 
221  gtp_hdr = (struct rte_gtp_hdr *)data;
222  if (data + sizeof(*gtp_hdr) > data_end) {
223  DOCA_LOG_DBG("Error parsing GTPU header");
225  }
226  data += sizeof(*gtp_hdr);
227 
228  if (gtp_hdr->ver != 1) {
229  DOCA_LOG_WARN("Unsupported GTPU version %hhu", gtp_hdr->ver);
231  }
232 
233  if (gtp_hdr->e || gtp_hdr->s || gtp_hdr->pn) {
234  gtpopt_hdr = (struct rte_gtp_hdr_ext_word *)data;
235  if (data + sizeof(*gtpopt_hdr) > data_end) {
236  DOCA_LOG_DBG("Error parsing GTPU option header");
238  }
239  data += sizeof(*gtpopt_hdr);
240 
241  if (gtp_hdr->e) {
242  /* Only support single fixed 1-word sized 0x85-type extension */
243  if (gtpopt_hdr->next_ext != 0x85) {
244  DOCA_LOG_WARN("Unsupported GTPU extension %hhu", gtpopt_hdr->next_ext);
246  }
247 
248  gtpext_hdr = (struct rte_gtp_psc_type0_hdr *)data;
249  /* +1 due to dynamic array at the end of the struct */
250  gtpext_len = sizeof(*gtpext_hdr) + 1;
251  if (data + gtpext_len > data_end) {
252  DOCA_LOG_DBG("Error parsing GTPU extension header");
254  }
255  data += gtpext_len;
256 
257  if (gtpext_hdr->ext_hdr_len != 1) {
258  DOCA_LOG_WARN("GTPU extension sizes more than 1 word are not supported %u",
259  gtpext_hdr->ext_hdr_len != 1);
261  }
262 
263  if (gtpext_hdr->data[0]) {
264  DOCA_LOG_WARN("Chained GTPU extensions are not supported, type %hhu",
265  gtpext_hdr->data[0]);
267  }
268  }
269  }
270 
271  ctx->len = data - data_beg;
272  ctx->gtp_hdr = gtp_hdr;
273  ctx->opt_hdr = gtpopt_hdr;
274  ctx->ext_hdr = gtpext_hdr;
275  return DOCA_SUCCESS;
276 }
277 
278 doca_error_t conn_parse(uint8_t *data, uint8_t *data_end, struct conn_parser_ctx *ctx)
279 {
280  doca_error_t ret;
281 
282  ret = network_parse(data + ctx->len, data_end, ctx->link_ctx.next_proto, &ctx->network_ctx);
283  if (ret != DOCA_SUCCESS)
284  return ret;
285  ctx->len += ctx->network_ctx.len;
286  if (ctx->network_ctx.frag)
287  /* Parse again after the defragmentation */
288  return DOCA_ERROR_AGAIN;
289 
290  ret = transport_parse(data + ctx->len, data_end, ctx->network_ctx.next_proto, &ctx->transport_ctx);
291  if (ret != DOCA_SUCCESS)
292  return ret;
293  ctx->len += ctx->transport_ctx.len;
294 
295  return DOCA_SUCCESS;
296 }
297 
298 doca_error_t plain_parse(uint8_t *data, uint8_t *data_end, struct conn_parser_ctx *ctx)
299 {
300  doca_error_t ret;
301 
302  ret = link_parse(data + ctx->len, data_end, &ctx->link_ctx);
303  if (ret != DOCA_SUCCESS)
304  return ret;
305  ctx->len += ctx->link_ctx.len;
306 
307  return conn_parse(data, data_end, ctx);
308 }
309 
310 doca_error_t tunnel_parse(uint8_t *data, uint8_t *data_end, struct tun_parser_ctx *ctx)
311 {
312  doca_error_t ret;
313 
314  ret = link_parse(data + ctx->len, data_end, &ctx->link_ctx);
315  if (ret != DOCA_SUCCESS)
316  return ret;
317  ctx->len += ctx->link_ctx.len;
318 
319  ret = network_parse(data + ctx->len, data_end, ctx->link_ctx.next_proto, &ctx->network_ctx);
320  if (ret != DOCA_SUCCESS)
321  return ret;
322  ctx->len += ctx->network_ctx.len;
323  if (ctx->network_ctx.frag)
324  /* Parse again after the defragmentation */
325  return DOCA_ERROR_AGAIN;
326 
327  ret = transport_parse(data + ctx->len, data_end, ctx->network_ctx.next_proto, &ctx->transport_ctx);
328  if (ret != DOCA_SUCCESS)
329  return ret;
330  ctx->len += ctx->transport_ctx.len;
331 
332  ret = gtpu_parse(data + ctx->len, data_end, &ctx->gtp_ctx);
333  if (ret != DOCA_SUCCESS)
334  return ret;
335  ctx->len += ctx->gtp_ctx.len;
336 
337  ret = conn_parse(data + ctx->len, data_end, &ctx->inner);
338  if (ret == DOCA_SUCCESS || ret == DOCA_ERROR_AGAIN)
339  ctx->len += ctx->inner.len;
340  return ret;
341 }
342 
344  uint8_t *data_end,
345  struct tun_parser_ctx *ctx,
347 {
348  struct transport_parser_ctx transport_ctx = {0};
349  struct network_parser_ctx network_ctx = {0};
350  struct link_parser_ctx link_ctx = {0};
351  doca_error_t ret;
352  size_t len = 0;
353 
354  ret = link_parse(data + len, data_end, &link_ctx);
355  if (ret != DOCA_SUCCESS)
356  return ret;
357  len += link_ctx.len;
358 
359  ret = network_parse(data + len, data_end, link_ctx.next_proto, &network_ctx);
360  if (ret != DOCA_SUCCESS)
361  return ret;
362  len += network_ctx.len;
363  /* Parse again after the defragmentation */
364  if (network_ctx.frag) {
365  /*
366  * We can't determine whether a fragmented packet is tunnel-encapsulated so we treat it as incoming from
367  * non-encapsulated until reparse.
368  */
370  ctx->inner.link_ctx = link_ctx;
371  ctx->inner.network_ctx = network_ctx;
372  ctx->inner.len += len;
373  ctx->len += len;
374  return DOCA_ERROR_AGAIN;
375  }
376 
377  ret = transport_parse(data + len, data_end, network_ctx.next_proto, &transport_ctx);
378  if (ret != DOCA_SUCCESS)
379  return ret;
380  len += transport_ctx.len;
381  if (transport_ctx.proto != DOCA_FLOW_PROTO_UDP ||
382  transport_ctx.udp_hdr->dst_port != DOCA_HTOBE16(DOCA_FLOW_GTPU_DEFAULT_PORT)) {
384  ctx->inner.link_ctx = link_ctx;
385  ctx->inner.network_ctx = network_ctx;
386  ctx->inner.transport_ctx = transport_ctx;
387  ctx->inner.len += len;
388  ctx->len += len;
389  return DOCA_SUCCESS;
390  }
391 
393  ctx->link_ctx = link_ctx;
394  ctx->network_ctx = network_ctx;
395  ctx->transport_ctx = transport_ctx;
396  ctx->len += len;
397 
398  ret = gtpu_parse(data + ctx->len, data_end, &ctx->gtp_ctx);
399  if (ret != DOCA_SUCCESS)
400  return ret;
401  ctx->len += ctx->gtp_ctx.len;
402 
403  ret = conn_parse(data + ctx->len, data_end, &ctx->inner);
404  if (ret == DOCA_SUCCESS || ret == DOCA_ERROR_AGAIN)
405  ctx->len += ctx->inner.len;
406  return ret;
407 }
#define NULL
Definition: __stddef_null.h:26
uint64_t len
#define DOCA_HTOBE16(_x)
enum doca_error doca_error_t
DOCA API return codes.
@ DOCA_ERROR_INVALID_VALUE
Definition: doca_error.h:44
@ DOCA_ERROR_NOT_SUPPORTED
Definition: doca_error.h:42
@ DOCA_ERROR_AGAIN
Definition: doca_error.h:43
@ DOCA_SUCCESS
Definition: doca_error.h:38
#define DOCA_FLOW_PROTO_IPV4
Definition: doca_flow_net.h:39
#define DOCA_FLOW_PROTO_UDP
Definition: doca_flow_net.h:42
#define DOCA_FLOW_ETHER_TYPE_IPV6
Definition: doca_flow_net.h:58
#define DOCA_FLOW_PROTO_TCP
Definition: doca_flow_net.h:41
#define DOCA_FLOW_PROTO_IPV6
Definition: doca_flow_net.h:40
#define DOCA_FLOW_GTPU_DEFAULT_PORT
Definition: doca_flow_net.h:48
#define DOCA_FLOW_ETHER_TYPE_IPV4
Definition: doca_flow_net.h:57
#define DOCA_LOG_WARN(format,...)
Generates a WARNING application log message.
Definition: doca_log.h:476
#define DOCA_LOG_DBG(format,...)
Generates a DEBUG application log message.
Definition: doca_log.h:496
static doca_error_t ipv6_hdr_parse(const uint8_t *data, const uint8_t *data_end, struct network_parser_ctx *ctx)
Definition: packet_parser.c:72
DOCA_LOG_REGISTER(PACKET_PARSER)
doca_error_t unknown_parse(uint8_t *data, uint8_t *data_end, struct tun_parser_ctx *ctx, enum parser_pkt_type *parser_pkt_type)
doca_error_t gtpu_parse(uint8_t *data, uint8_t *data_end, struct gtp_parser_ctx *ctx)
doca_error_t tunnel_parse(uint8_t *data, uint8_t *data_end, struct tun_parser_ctx *ctx)
doca_error_t network_parse(uint8_t *data, uint8_t *data_end, uint16_t expected_proto, struct network_parser_ctx *ctx)
doca_error_t link_parse(uint8_t *data, uint8_t *data_end, struct link_parser_ctx *ctx)
Definition: packet_parser.c:41
doca_error_t conn_parse(uint8_t *data, uint8_t *data_end, struct conn_parser_ctx *ctx)
doca_error_t plain_parse(uint8_t *data, uint8_t *data_end, struct conn_parser_ctx *ctx)
doca_error_t transport_parse(uint8_t *data, uint8_t *data_end, uint8_t proto, struct transport_parser_ctx *ctx)
parser_pkt_type
Definition: packet_parser.h:35
@ PARSER_PKT_TYPE_PLAIN
Definition: packet_parser.h:37
@ PARSER_PKT_TYPE_TUNNELED
Definition: packet_parser.h:36
rte_ether_hdr eth
Definition: psp_gw_flows.cpp:1
uint8_t next_proto_id
Definition: packets.h:73
struct rte_udp_hdr * udp_hdr
Definition: packet_parser.h:66
struct upf_accel_ctx * ctx