netflow_example.c Source File

Reference Documentation

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