netflow_example.c Source File

Reference Documentation

Platform
Napatech SmartNIC
Content Type
Reference Information
Capture Software Version
Link™ Capture Software 12.15
Napatech Software Suite: examples/net/netflow/netflow_example.c Source File
netflow_example.c
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2025 Napatech A/S. All Rights Reserved.
4  *
5  * 1. Copying, modification, and distribution of this file, or executable
6  * versions of this file, is governed by the terms of the Napatech Software
7  * license agreement under which this file was made available. If you do not
8  * agree to the terms of the license do not install, copy, access or
9  * otherwise use this file.
10  *
11  * 2. Under the Napatech Software license agreement you are granted a
12  * limited, non-exclusive, non-assignable, copyright license to copy, modify
13  * and distribute this file in conjunction with Napatech SmartNIC's and
14  * similar hardware manufactured or supplied by Napatech A/S.
15  *
16  * 3. The full Napatech Software License Agreement is included in this
17  * distribution, please see "NA-0009 Software License Agreement.pdf"
18  *
19  * 4. Redistributions of source code must retain this copyright notice,
20  * list of conditions and the following disclaimer.
21  *
22  * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTIES, EXPRESS OR
23  * IMPLIED, AND NAPATECH DISCLAIMS ALL IMPLIED WARRANTIES INCLUDING ANY
24  * IMPLIED WARRANTY OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, OR OF
25  * FITNESS FOR A PARTICULAR PURPOSE. TO THE EXTENT NOT PROHIBITED BY
26  * APPLICABLE LAW, IN NO EVENT SHALL NAPATECH BE LIABLE FOR PERSONAL INJURY,
27  * OR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER,
28  * INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, CORRUPTION OR
29  * LOSS OF DATA, FAILURE TO TRANSMIT OR RECEIVE ANY DATA OR INFORMATION,
30  * BUSINESS INTERRUPTION OR ANY OTHER COMMERCIAL DAMAGES OR LOSSES, ARISING
31  * OUT OF OR RELATED TO YOUR USE OR INABILITY TO USE NAPATECH SOFTWARE OR
32  * SERVICES OR ANY THIRD PARTY SOFTWARE OR APPLICATIONS IN CONJUNCTION WITH
33  * THE NAPATECH SOFTWARE OR SERVICES, HOWEVER CAUSED, REGARDLESS OF THE THEORY
34  * OF LIABILITY (CONTRACT, TORT OR OTHERWISE) AND EVEN IF NAPATECH HAS BEEN
35  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT ALLOW
36  * THE EXCLUSION OR LIMITATION OF LIABILITY FOR PERSONAL INJURY, OR OF
37  * INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU.
38  *
39  *
40 
41  */
42 
43 /**
44  * @example net/netflow/netflow_example.c
45  * @section netflow_example_description Description
46  *
47  * This source file is an example of how to use the Dynamic descriptor 1
48  * to extract NetFlow information from a packet
49  *
50  * The following NTAPI functions are used:
51  * - @ref NT_Init()
52  * - @ref NT_ConfigOpen()
53  * - @ref NT_NetRxOpen()
54  * - @ref NT_NTPL()
55  * - @ref NT_NetRxGet()
56  * - @ref NT_NetRxRelease()
57  * - @ref NT_NetRxClose()
58  * - @ref NT_ConfigClose()
59  * - @ref NT_ExplainError()
60  *
61  * @note
62  * This example does only work with accelerators having "dynamic descriptor 1"
63  *
64  * <hr>
65  * @section netflow_example_prerequisites Prerequisites
66  * A Napatech capture accelerator is needed to run this example. The ntservice.ini must
67  * have at least one HostBuffersRx defined. Below is an example of a
68  * minimum ini-file. It will create a 32MB RX hostbuffer from NUMA
69  * node 0.
70  * @code
71  * [System]
72  * TimestampFormat = NATIVE
73  *
74  * [Adapter0]
75  * AdapterType = NT20E
76  * BusId = 00:0a:00.00
77  * HostBuffersRx = [1,32,0]
78  * @endcode
79  *
80  * @section netflow_example_flow Program flow
81  * @{
82  * The following is required to perform real-time netflow on packets:
83  * - \#include/nt.h - Applications/Tools only need to include @ref
84  * nt.h to obtain prototypes, macros etc. from NTAPI.
85  * - @ref NT_Init(@ref NTAPI_VERSION) - Initialize the NTAPI
86  * library. @ref NTAPI_VERSION is a define that describes the version
87  * of the API described in the header files included by @ref
88  * nt.h. NT_Init() will ask the NTAPI library to convert return data
89  * to the @ref NTAPI_VERSION if possible. This will ensure that
90  * applications can run on NTAPI libraries of newer versions.
91  * - @ref NT_ConfigOpen() - Open a config stream in order to setup
92  * filter using the @ref NT_NTPL() command.
93  * - @ref NT_NetRxOpen() - Open a stream. The stream ID must match the
94  * one used when creating the filter using the @ref NT_NTPL()
95  * command. A stream does not return data until traffic is assigned
96  * to it by creating a filter. Stream IDs might be shared between
97  * other streams and it is possible to make several filters to one
98  * stream ID. Each filter can have a unique color or color mask in the ASSIGN.
99  * The "color(mask)" of the ASSIGN can be used to mark packets making it
100  * possible for the stream to determine if the packets it receives
101  * via @ref NT_NetRxGet() as based on its assign or if the packet belongs
102  * to the other streams that also share the hostbuffer.
103  * - @ref NT_NTPL() - Assign traffic to a stream by creating a filter
104  * using a manually chosen stream ID. The stream ID must match the
105  * one used @ref NT_NetRxOpen().
106  * - Optional step. Wait until we start seeing packets that are hit by
107  * the NTPL assign command. This is done to avoid getting packets
108  * that are not fully classified by the stream. NT_NetRxGet() is
109  * called with a timeout of 1000ms and will return NT_STATUS_TIMEOUT
110  * in case nothing is received within 1000ms and will return
111  * NT_SUCCESS if something is returned. Return values different from
112  * that is an indication of an error. Packets that are prior to the
113  * expected time are released via NT_NetRxRelease().
114  * - NT_NetRxGet() and NT_NetRxRelease() - Receive and release packets. NetFlow information is printed for each received packet
115  * with help of the dynamic offset fields within NtDyn1Descr_t.
116  * - NT_NetRxClose() - Close the stream when terminating. This will
117  * close the stream and release the NTPL assignment made on the
118  * hostbuffer.
119  *
120  *<hr>
121  * @section netflow_example_code Code
122  * @}
123  */
124 
125 // Include this in order to access the Napatech API
126 #include <nt.h>
127 
128 #ifdef _WIN32
129 #include <winsock2.h>
130 #else
131 #include <netinet/in.h>
132 #endif
133 
134 #include <inttypes.h>
135 
136 struct IPv6Header_s {
137  // Little endian encoding
138  uint8_t ip_tclass1:4;
139  uint8_t ip_v:4;
140  uint8_t ip_flow1:4;
141  uint8_t ip_tclass2:4;
142  uint16_t ip_flow2;
143  uint16_t ip_len;
144  uint8_t ip_nexthdr;
145  uint8_t ip_hoplim;
146  uint32_t ip_src[4];
147  uint32_t ip_dest[4];
148 }; // 40 bytes;
149 
150 struct IPv4Header_s {
151  uint16_t ip_hl: 4;
152  uint16_t ip_v: 4;
153  uint16_t ip_tos: 8;
154  uint16_t ip_len;
155 
156  uint32_t ip_id:16;
157  uint32_t ip_frag_off:16;
158 #define IP_DONT_FRAGMENT 0x4000
159 #define IP_MORE_FRAGMENTS 0x2000
160 
161  uint32_t ip_ttl:8;
162  uint32_t ip_prot:8;
163  uint32_t ip_crc:16;
164 
165  uint32_t ip_src;
166  uint32_t ip_dest;
167 }; //20 bytes
168 
169 struct UDPHeader_s {
170  uint32_t udp_src:16;
171  uint32_t udp_dest:16;
172 
173  uint32_t udp_len:16;
174  uint32_t udp_crc:16;
175 }; // 8 bytes
176 
177 struct TCPHeader_s {
178  uint32_t tcp_src:16;
179  uint32_t tcp_dest:16;
180 
181  uint32_t tcp_seq;
182  uint32_t tcp_ack;
183 
184  uint32_t reserved:4;
185  uint32_t tcp_doff:4;
186  uint32_t tcp_ec_ctl:8;
187  uint32_t tcp_window:16;
188 
189  uint32_t tcp_crc:16;
190  uint32_t tcp_urgp:16;
191 }; // 20 bytes
192 
193 #if defined(WIN32) || defined (WIN64)
194  #define snprintf(dst, ...) _snprintf_s((dst), _countof(dst), __VA_ARGS__)
195 #endif
196 
197 #define ARRAY_SIZE(a) (sizeof((a))/sizeof(*(a)))
198 
199 static void DumpL4(NtDyn1Descr_t *pDyn1)
200 {
201  printf(" %3d %8s | ", pDyn1->ipProtocol, pDyn1->ipProtocol == 6 ? "TCP" : pDyn1->ipProtocol == 17 ? "UDP" : "Other");
202  if (pDyn1->ipProtocol == 6) {
203  struct TCPHeader_s *pl4 = (struct TCPHeader_s*)((uint8_t*)pDyn1 + pDyn1->descrLength + pDyn1->offset1);
204  printf(" %04X | %04X | ", ntohs(pl4->tcp_src), ntohs(pl4->tcp_dest));
205  printf(" %03X | ", (pl4->reserved & 1) << 8 | pl4->tcp_ec_ctl);
206  } else if (pDyn1->ipProtocol == 17) {
207  struct UDPHeader_s *pl4 = (struct UDPHeader_s*)((uint8_t*)pDyn1 + pDyn1->descrLength + pDyn1->offset1);
208  printf(" %04X | %04X | ", ntohs(pl4->udp_src), ntohs(pl4->udp_dest));
209  printf("%9s | ", "N/A");
210  } else {
211  printf("%8s %9s | ", " ", " ");
212  printf("%9s | ", " ");
213  }
214  printf("%8d bytes\n", pDyn1->capLength - 4 - pDyn1->descrLength - pDyn1->offset0);
215 }
216 
217 static void DumpIPv4(NtDyn1Descr_t *pDyn1)
218 {
219  uint32_t ipaddr;
220  struct IPv4Header_s *pl3 = (struct IPv4Header_s*)((uint8_t*)pDyn1 + pDyn1->descrLength + pDyn1->offset0);
221  printf("%-16s | %-15s - %-15s | %-16s | %-8s | %-9s | %-9s | %-8s\n", "Time", "Src", "Dest", "Protocol", "Src port", "Dest port", "TCP flags", "Bytes");
222  printf("%16"PRIx64" | ", pDyn1->timestamp);
223  ipaddr = ntohl(pl3->ip_src);
224  printf("%03d.%03d.%03d.%03d - ", (ipaddr >> 24) & 0xFF, (ipaddr >> 16) & 0xFF, (ipaddr >> 8) & 0xFF, ipaddr & 0xFF);
225  ipaddr = ntohl(pl3->ip_dest);
226  printf("%03d.%03d.%03d.%03d | ", (ipaddr >> 24) & 0xFF, (ipaddr >> 16) & 0xFF, (ipaddr >> 8) & 0xFF, ipaddr & 0xFF);
227  DumpL4(pDyn1);
228 }
229 
230 static void DumpIPv6(NtDyn1Descr_t *pDyn1)
231 {
232  int i;
233  struct IPv6Header_s *pl3 = (struct IPv6Header_s*)((uint8_t*)pDyn1 + pDyn1->descrLength + pDyn1->offset0);
234  printf("%-16s | %-32s - %-32s | %-16s | %-8s | %-9s | %-9s | %-8s\n", "Time", "Src", "Dest", "Protocol", "Src port", "Dest port", "TCP flags", "Bytes");
235  printf("%16"PRIx64" | ", pDyn1->timestamp);
236  for (i = 0; i < 16; i++) {
237  printf("%02x", *(((uint8_t*)&pl3->ip_src)+i));
238  }
239  printf(" - ");
240  for (i = 0; i < 16; i++) {
241  printf("%02x", *(((uint8_t*)&pl3->ip_dest)+i));
242  }
243  printf(" | ");
244  DumpL4(pDyn1);
245 }
246 
247 int main(int argc, char *argv[])
248 {
249  int ret = 0;
250  int numPackets=0; // The number of packets received
251  int numBytes=0; // The number of bytes received (wire length)
252  char tmpBuffer[20]; // Buffer to build filter string
253  char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer
254  int status; // Status variable
255  NtNetStreamRx_t hNetRx; // Handle to the RX stream
256  NtConfigStream_t hCfgStream; // Handle to a config stream
257  NtNtplInfo_t ntplInfo; // Return data structure from the NT_NTPL() call.
258  NtNetBuf_t hNetBuf; // Net buffer container. Packet data is returned in this when calling NT_NetRxGet().
259  const char *ntplCommands[] = {
260  "Assign[Priority=1;ColorMask=0x40] = (CvError == True) OR (CrcError == True) OR (Truncated == True)",
261  "Assign[Priority=1;ColorMask=0x20] = Layer3Protocol != IP",
262  "Assign[Priority=1;ColorMask=0x01] = Fragment == First",
263  "Assign[Priority=1;ColorMask=0x02] = (Fragment == Middle) OR (Fragment == Last)",
264  "Assign[Priority=1;ColorMask=0x00] = Layer3Protocol == IPv4",
265  "Assign[Priority=1;ColorMask=0x04] = Layer3Protocol == IPv6",
266  "Assign[Priority=1;ColorMask=0x08] = InnerLayer3Protocol == IPv4",
267  "Assign[Priority=1;ColorMask=0x10] = InnerLayer3Protocol == IPv6",
268  "Assign[Priority=0;streamid=1;Descriptor=Dyn1] = All"
269  };
270  uint32_t ntplIds[ARRAY_SIZE(ntplCommands)];
271  unsigned i;
272 
273  // Initialize the NTAPI library and thereby check if NTAPI_VERSION can be used together with this library
274  if ((status = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) {
275  // Get the status code as text
276  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
277  fprintf(stderr, "NT_Init() failed: %s\n", errorBuffer);
278  return -1;
279  }
280 
281  // Open a config stream to assign a filter to a stream ID.
282  if ((status = NT_ConfigOpen(&hCfgStream, "TestStream")) != NT_SUCCESS) {
283  // Get the status code as text
284  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
285  fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
286  return -1;
287  }
288 
289  memset(ntplIds, 0, sizeof(ntplIds));
290 
291  // Assign traffic to stream ID 1 and mask traffic matching the assign statements
292  // with an individual color bit.
293  // Select that all packets matching this are provided with the Dyn1 descriptor
294  for (i = 0; i < ARRAY_SIZE(ntplCommands); i++) {
295  if ((status = NT_NTPL(hCfgStream, ntplCommands[i], &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
296  // Get the status code as text
297  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
298  fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer);
299  fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode);
300  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]);
301  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]);
302  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]);
303  ret = -1;
304  goto delete_out;
305  }
306  ntplIds[i] = ntplInfo.ntplId;
307  }
308 
309  // Get a stream handle with the hostBuffer mapped to it. NT_NET_INTERFACE_PACKET specify that we will receive data packet-by-packet
310  if ((status = NT_NetRxOpen(&hNetRx, "TestStream", NT_NET_INTERFACE_PACKET, 1, -1)) != NT_SUCCESS) {
311  // Get the status code as text
312  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
313  fprintf(stderr, "NT_NetRxOpen() failed: %s\n", errorBuffer);
314  return -1;
315  }
316 
317  // Optional step. Wait for the first packet that hit the NTPL assign command
318  printf("Waiting for the first packet\n");
319  while (1) {
320  if ((status = NT_NetRxGet(hNetRx, &hNetBuf, 1000)) != NT_SUCCESS) {
321  if ((status == NT_STATUS_TIMEOUT) || (status == NT_STATUS_TRYAGAIN)) {
322  // Timeouts are ok, we just need to wait a little longer for a packet
323  continue;
324  }
325  // Get the status code as text
326  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
327  fprintf(stderr, "NT_NetRxGet() failed: %s\n", errorBuffer);
328  return -1;
329  }
330  // We got a packet. Check if the timestamp is newer than when the NTPL assign command was applied
331  if (NT_NET_GET_PKT_TIMESTAMP(hNetBuf) > ntplInfo.ts) {
332  break; // Break out, we have received a packet that is received after the NTPL assign command was applied
333  }
334  // Release the packet, it is too "old".
335  if ((status = NT_NetRxRelease(hNetRx, hNetBuf)) != NT_SUCCESS) {
336  // Get the status code as text
337  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
338  fprintf(stderr, "NT_NetRxRelease() failed: %s\n", errorBuffer);
339  return -1;
340  }
341  }
342 
343  // Dump packet info. Stop when 10 packets has been received
344  while (1) {
345  if (NT_NET_GET_PKT_DESCRIPTOR_TYPE(hNetBuf) != NT_PACKET_DESCRIPTOR_TYPE_DYNAMIC || NT_NET_GET_PKT_DESCRIPTOR_FORMAT(hNetBuf) != 1) {
346  fprintf(stderr, "Expected Dynamic Descriptor 1\n");
347  return -1;
348  }
349 
350  NtDyn1Descr_t *pDyn1 = NT_NET_DESCR_PTR_DYN1(hNetBuf);
351  if (pDyn1 == NULL) {
352  fprintf(stderr, "Unexpected Dynamic Descriptor\n");
353  return -1;
354  }
355  if (argc == 2 && argv[1][0] == 'v') {
356  printf("caplength : %d\n", pDyn1->capLength);
357  printf("color : %d\n", pDyn1->color);
358  printf("descrFormat: %d\n", pDyn1->descrFormat);
359  printf("descrLength: %d\n", pDyn1->descrLength);
360  printf("tsColor : %d\n", pDyn1->tsColor);
361  printf("IPProt : %d\n", pDyn1->ipProtocol);
362  printf("offset0 : %d\n", pDyn1->offset0);
363  printf("offset1 : %d\n", pDyn1->offset1);
364  printf("offset2 : %d\n", pDyn1->offset2);
365  printf("rxport : %d\n", pDyn1->rxPort);
366  printf("timestamp : %16lX\n", pDyn1->timestamp);
367  }
368 
369  if (pDyn1->color & (1 << 6)) {
370  printf("Packet contain an error and decoding cannot be trusted\n");
371  } else {
372  if (pDyn1->color & (1 << 5)) {
373  printf("A non IPv4,IPv6 packet received\n");
374  } else if (pDyn1->color & 3) {
375  printf("Fragmented packet. Must be assembled before the netflow information can be gathered\n");
376  } else {
377  switch (pDyn1->color >> 2) {
378  case 0: // IPv4
379  printf("IPv4 packet received\n");
380  DumpIPv4(pDyn1);
381  break;
382  case 1: // IPv6
383  printf("IPv6 packet received\n");
384  DumpIPv6(pDyn1);
385  break;
386  case 2: // Tunneled IPv4
387  printf("Tunneled IPv4 packet received\n");
388  DumpIPv4(pDyn1);
389  break;
390  case 3: // Tunneled IPv6
391  printf("Tunneled IPv6 packet received\n");
392  DumpIPv6(pDyn1);
393  break;
394  }
395  }
396  }
397  // Increment the number of packets processed.
398  numPackets++;
399  // Increment the bytes received
400  numBytes+=pDyn1->capLength;
401 
402  // Release the current packet
403  if ((status = NT_NetRxRelease(hNetRx, hNetBuf)) != NT_SUCCESS) {
404  // Get the status code as text
405  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
406  fprintf(stderr, "NT_NetRxGet() failed: %s\n", errorBuffer);
407  return -1;
408  }
409 
410  if (numPackets == 10) {
411  break;
412  }
413 
414  // Get the next packet
415  while (1) {
416  if ((status = NT_NetRxGet(hNetRx, &hNetBuf, 1000)) != NT_SUCCESS) {
417  if ((status == NT_STATUS_TIMEOUT) || (status == NT_STATUS_TRYAGAIN)) {
418  // Timeouts are ok, we just need to wait a little longer for a packet
419  continue;
420  }
421  // Get the status code as text
422  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
423  fprintf(stderr, "NT_NetRxGet() failed: %s\n", errorBuffer);
424  return -1;
425  }
426  break; // We got a packet
427  }
428  }
429 
430 delete_out:
431 
432  // Delete the filters
433  for (i = 0; i < ARRAY_SIZE(ntplCommands); i++) {
434  if (!ntplIds[i]) {
435  continue;
436  }
437  snprintf(tmpBuffer, 20, "delete=%u", ntplIds[i]);
438  if ((status = NT_NTPL(hCfgStream, tmpBuffer, &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
439  // Get the status code as text
440  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
441  fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer);
442  fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode);
443  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]);
444  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]);
445  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]);
446  return -1;
447  }
448  }
449 
450  // Close the config stream
451  if ((status = NT_ConfigClose(hCfgStream)) != NT_SUCCESS) {
452  // Get the status code as text
453  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
454  fprintf(stderr, "NT_ConfigClose() failed: %s\n", errorBuffer);
455  return -1;
456  }
457 
458  // Close the stream and release the hostbuffer. This will also remove the NTPL assignments performed.
459  NT_NetRxClose(hNetRx);
460 
461  printf("Done: %d packets, %d bytes\n", numPackets, numBytes);
462  return ret;
463 }