capture_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/capture/capture_example.c Source File
capture_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/capture/capture_example.c
46  * @section capture_example_description Description
47  *
48  * This source file is an example of how to do capture to disk via the
49  * segment interface using NTAPI.
50  *
51  * The following NTAPI functions are used:
52  * - @ref NT_Init()
53  * - @ref NT_NetRxOpen()
54  * - @ref NT_NTPL()
55  * - @ref NT_NetRxRead()
56  * - @ref NT_NetRxGet()
57  * - @ref NT_NET_GET_SEGMENT_PTR()
58  * - @ref NT_NET_GET_SEGMENT_LENGTH()
59  * - @ref NT_NET_GET_SEGMENT_TIMESTAMP()
60  * - @ref NT_NetRxRelease()
61  * - @ref NT_NetRxClose()
62  * - @ref NT_Done()
63  * - @ref NT_ExplainError()
64  *
65  * @section capture_example_prerequisites Prerequisites
66  * A Napatech capture accelerator is need to run this example. The ntservice.ini
67  * must have at least one HostBuffersRx defined. Below is an example
68  * of a minimum ini-file. It will create a 32MB RX hostbuffer from
69  * NUMA 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 capture_example_flow Program flow
81  * @{
82  * The following is required to perform capture of segments to disk:
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_NetRxOpen() - Open the stream using a stream ID.
92  * The stream ID must match the one used when creating the
93  * filter.
94  * - @ref NT_NTPL() - Assign traffic to the stream. A stream does not
95  * return data until traffic is assigned to it by a filter. Stream
96  * IDs can be shared between other streams.
97  * - NT_NetRxRead() - Get the file header. A NT file header must be
98  * written to the beginning of the file after the last NT_NTPL()
99  * call has been made. Set @ref NtNetRx_s::cmd "NtNetRx_s.cmd"=@ref
100  * NT_NETRX_READ_CMD_GET_FILE_HEADER and issue the NT_NetRxRead()
101  * call. The fileheader is returned in @ref
102  * NtNetRxFileHeader_s::data
103  * "NtNetRx_s.u.fileheader.data".
104  * - Create the capture file and write NT header. Use the OS specific
105  * functions to create a new capture file.
106  * - Optional step. Wait until we start seeing segments that are hit
107  * by the NTPL assign command. This is done to avoid getting
108  * segments that are not fully classified by the stream.
109  * NT_NetRxGet() is called with a timeout of 1000ms and will return
110  * NT_STATUS_TIMEOUT in case nothing is received within 1000ms and
111  * will return NT_SUCCESS when a segment is returned. Segments with NT_NET_GET_SEGMENTLENGTH()==0
112  * can be returned so it is needed to check for the segment length before using data within
113  * the segment. The NT_NET_GET_SEGMENT_TIMESTAMP() macro can still be used on the empty segments.
114  * Return values different from that is an indication of an error. Segments that
115  * are prior to the expected time are released via NT_NetRxRelease().
116  * - NT_NetRxGet(), write to file and NT_NetRxRelease() - Receive
117  * segments, write to disk and release segments. The @ref
118  * SegmentMacros are used to find the segment and length and
119  * timestamp of the segment:
120  * - @ref NT_NET_GET_SEGMENT_PTR() - Get a pointer to the segment.
121  * - @ref NT_NET_GET_SEGMENT_LENGTH() - Get length of the segment to store.
122  * - @ref NT_NET_GET_SEGMENT_TIMESTAMP() - The time the segment was delivered.
123  * - @ref _nt_net_build_pkt_netbuf() and @ref _nt_net_get_next_packet() are used to traverse
124  * packets inside a segment. This is usefull if inspection is needed before saving the
125  * segment.
126  * - NT_NetRxClose() - Close the stream when terminating.
127  * This will close the stream and release the NTPL assignment made on the hostbuffer.
128  * - Close captured file
129  * - @ref NT_Done() - Close down the NTAPI library.
130  *
131  *<hr>
132  * @section capture_example_code Code
133  * @}
134  */
135 
136 // Include this in order to access the Napatech API
137 #include <nt.h>
138 
139 #if defined(WIN32) || defined (WIN64)
140  #define snprintf(dst, ...) _snprintf_s((dst), _countof(dst), __VA_ARGS__)
141 #endif
142 
143 
144 struct ntpcap_ts_s {
145  uint32_t sec;
146  uint32_t usec;
147 };
148 
149 struct ntpcap_hdr_s {
150  struct ntpcap_ts_s ts;
151  uint32_t caplen;
152  uint32_t wirelen;
153 };
154 
155 
156 int main(void)
157 {
158  FILE* hf; // File handle
159  int numSegments = 0; // The number of segments received
160  int numPackets = 0; // The number of packets received
161  uint64_t numBytes = 0; // The number of bytes received
162  uint64_t numBytesWire = 0; // The number of bytes received on the wire
163  char tmpBuffer[20]; // Buffer to build filter string
164  char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer
165  int status; // Status variable
166  NtNetStreamRx_t hNetRx; // Handle to the RX stream
167  NtConfigStream_t hCfgStream; // Handle to a config stream
168  NtNtplInfo_t ntplInfo; // Return data structure from the NT_NTPL() call.
169  NtNetBuf_t hNetBuf; // Net buffer container. Segment data is returned in this when calling NT_NetRxGet().
170  NtNetRx_t readCmd; // NetRx read command structure.
171  struct NtNetBuf_s pktNetBuf; // Packet netbuf structure.
172  struct ntpcap_ts_s* pTs;
173 
174  // Initialize the NTAPI library and thereby check if NTAPI_VERSION can be used together with this library
175  if ((status = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) {
176  // Get the status code as text
177  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
178  fprintf(stderr, "NT_Init() failed: %s\n", errorBuffer);
179  return -1;
180  }
181 
182  // Open a config stream to assign a filter to a stream ID.
183  if ((status = NT_ConfigOpen(&hCfgStream, "TestStream")) != NT_SUCCESS) {
184  // Get the status code as text
185  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
186  fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
187  return -1;
188  }
189 
190  // Assign traffic to stream ID 1 and mask all traffic matching the assign statement color=7.
191  if ((status = NT_NTPL(hCfgStream, "Assign[streamid=1;color=7] = port == 0", &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
192  // Get the status code as text
193  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
194  fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer);
195  fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode);
196  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]);
197  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]);
198  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]);
199  return -1;
200  }
201 
202  // Used if PCAP header used
203  pTs = (struct ntpcap_ts_s *)(void *)&(ntplInfo.ts);
204 
205  // Close the config stream
206  if ((status = NT_ConfigClose(hCfgStream)) != NT_SUCCESS) {
207  // Get the status code as text
208  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
209  fprintf(stderr, "NT_ConfigClose() failed: %s\n", errorBuffer);
210  return -1;
211  }
212 
213  // Open stat stream
214  NtStatStream_t hStat = NULL;
215  if ((status = NT_StatOpen(&hStat, "hStat")) != 0) {
216  fprintf(stderr, "Failed to create statistics stream: 0x%08X\n", status);
217  return -1;
218  }
219 
220  // Reset stats
221  static NtStatistics_t statSet;
223  statSet.u.query_v3.poll = 1;
224  statSet.u.query_v3.clear = 1;
225  if ((status = NT_StatRead(hStat, &statSet))) {
226  fprintf(stderr, "Failed resetting statistics: 0x%08X\n", status);
227  return -1;
228  }
229 
230  // Get a stream handle with stream ID 1. NT_NET_INTERFACE_SEGMENT specify that we will receive data in a segment based matter.
231  if ((status = NT_NetRxOpen(&hNetRx, "TestStream", NT_NET_INTERFACE_SEGMENT, 1, -1)) != NT_SUCCESS) {
232  // Get the status code as text
233  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
234  fprintf(stderr, "NT_NetRxOpen() failed: %s\n", errorBuffer);
235  return -1;
236  }
237 
238  // Read the file header.
240  if ((status = NT_NetRxRead(hNetRx, &readCmd)) != NT_SUCCESS) {
241  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
242  fprintf(stderr, "NT_NetRxRead() failed: %s\n", errorBuffer);
243  return -1;
244  }
245 
246  // Create the capture file
247  if ((hf = fopen("capfile.ntcap", "w+b")) == NULL) {
248  perror("Failed to open capfile.ntcap");
249  return -1;
250  }
251 
252  // Write the file header
253  fwrite(readCmd.u.fileheader.data, readCmd.u.fileheader.size, 1, hf);
254 
255  // Optional step. Wait for the first packet that hit the NTPL assign command
256  printf("Waiting for the first segment\n");
257  while (1) {
258 
259  if ((status = NT_NetRxGet(hNetRx, &hNetBuf, 1000)) != NT_SUCCESS) {
260  if ((status == NT_STATUS_TIMEOUT) || (status == NT_STATUS_TRYAGAIN)) {
261  // Timeouts are ok, we just need to wait a little longer for a segment
262  continue;
263  }
264  // Get the status code as text
265  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
266  fprintf(stderr, "NT_NetRxGet() failed: %s\n", errorBuffer);
267  fclose(hf);
268  return -1;
269  }
270  // We got a segment. Check if the timestamp is newer than when the NTPL assign command was applied
271  // If PCAP header configured, we need to convert the timestamps received
273  if ((((struct ntpcap_ts_s *)NT_NET_GET_SEGMENT_PTR(hNetBuf))->sec * 1000000 + ((struct ntpcap_ts_s *)NT_NET_GET_SEGMENT_PTR(hNetBuf))->usec) >
274  (pTs->sec * 1000000 + pTs->usec)) {
275  break; // Break out, we have received a segment that is received after the NTPL assign command was applied
276  }
277  } else
279  if ((((struct ntpcap_ts_s *)NT_NET_GET_SEGMENT_PTR(hNetBuf))->sec * 1000000000 + ((struct ntpcap_ts_s *)NT_NET_GET_SEGMENT_PTR(hNetBuf))->usec) >
280  (pTs->sec * 1000000000 + pTs->usec)) {
281  break; // Break out, we have received a segment that is received after the NTPL assign command was applied
282  }
283  } else {
284  if (NT_NET_GET_SEGMENT_TIMESTAMP(hNetBuf) > ntplInfo.ts) {
285  break; // Break out, we have received a segment that is received after the NTPL assign command was applied
286  }
287  }
288 
289  // Release the segment as it is too "old".
290  if ((status = NT_NetRxRelease(hNetRx, hNetBuf)) != NT_SUCCESS) {
291  // Get the status code as text
292  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
293  fprintf(stderr, "NT_NetRxRelease() failed: %s\n", errorBuffer);
294  fclose(hf);
295  return -1;
296  }
297  }
298 
299  // Write 10 segments to disk
300  while (1) {
301  if (NT_NET_GET_SEGMENT_LENGTH(hNetBuf)) {
302  // Optional step. Here all packets are inspected before storing
303  // Start by building a packet netbuf structure
306  {
307  struct ntpcap_hdr_s* pHdr = (struct ntpcap_hdr_s *)NT_NET_GET_SEGMENT_PTR(hNetBuf);
308  uint64_t segLength = NT_NET_GET_SEGMENT_LENGTH(hNetBuf);
309  uint64_t totSegmentBytes = 0;
310  while (totSegmentBytes < segLength) {
311  if (pHdr->wirelen) {
312  numPackets++;
313  numBytesWire += pHdr->wirelen;
314  }
315  totSegmentBytes += pHdr->caplen + (uint32_t)sizeof(struct ntpcap_hdr_s);
316  pHdr = (struct ntpcap_hdr_s *)((uint8_t *)pHdr + pHdr->caplen + sizeof(struct ntpcap_hdr_s));
317  }
318  } else {
319  _nt_net_build_pkt_netbuf(hNetBuf, &pktNetBuf);
320  do {
321  // Just count the amount of packets and wire length
322  numPackets++;
323  numBytesWire += NT_NET_GET_PKT_WIRE_LENGTH((&pktNetBuf));
324  } while (_nt_net_get_next_packet(hNetBuf, NT_NET_GET_SEGMENT_LENGTH(hNetBuf), &pktNetBuf)>0);
325  }
326 
327  if (fwrite(NT_NET_GET_SEGMENT_PTR(hNetBuf), NT_NET_GET_SEGMENT_LENGTH(hNetBuf), 1, hf) <= 0) {
328  perror("Failed writing segment");
329  fclose(hf);
330  return -1;
331  }
332  // Increment the number of segments processed.
333  numSegments++;
334  // Increment the bytes received
335  numBytes += NT_NET_GET_SEGMENT_LENGTH(hNetBuf);
336  printf("%016llx - Received segment of %lu bytes.\n",
337  (unsigned long long)NT_NET_GET_SEGMENT_TIMESTAMP(hNetBuf), NT_NET_GET_SEGMENT_LENGTH(hNetBuf));
338  }
339 
340  // Release the current segment
341  if ((status = NT_NetRxRelease(hNetRx, hNetBuf)) != NT_SUCCESS) {
342  // Get the status code as text
343  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
344  fprintf(stderr, "NT_NetRxRelease() failed: %s\n", errorBuffer);
345  fclose(hf);
346  return -1;
347  }
348 
349  if (numSegments == 10) {
350  break;
351  }
352 
353  // Get the next segment
354  while (1) {
355  if ((status = NT_NetRxGet(hNetRx, &hNetBuf, 1000)) != NT_SUCCESS) {
356  if ((status == NT_STATUS_TIMEOUT) || (status == NT_STATUS_TRYAGAIN)) {
357  // Timeouts are ok, we just need to wait a little longer for a segment
358  continue;
359  }
360  // Get the status code as text
361  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
362  fprintf(stderr, "NT_NetRxGet() failed: %s\n", errorBuffer);
363  fclose(hf);
364  return -1;
365  }
366  break; // We got a segment
367  }
368  }
369 
370  // Close the stream and release the hostbuffer. This will also remove the NTPL assignments performed.
371  NT_NetRxClose(hNetRx);
372 
373  // Close the file
374  fclose(hf);
375 
376  // Request stats
378  statSet.u.query_v3.poll = 1;
379  statSet.u.query_v3.clear = 0;
380  if ((status = NT_StatRead(hStat, &statSet)) != NT_SUCCESS) {
381  fprintf(stderr, "Failed reading statistics: 0x%08X\n", status);
382  return -1;
383  }
384 
385  // Read drop counters for streamid 1
386  uint64_t totDropsPkts = statSet.u.query_v3.data.stream.streamid[1].drop.pkts;
387  uint64_t totDropsBytes = statSet.u.query_v3.data.stream.streamid[1].drop.octets;
388 
389  // Close stat stream
390  NT_StatClose(hStat);
391 
392 
393  // Open a config stream to delete a filter.
394  if ((status = NT_ConfigOpen(&hCfgStream, "TestStream")) != NT_SUCCESS) {
395  // Get the status code as text
396  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
397  fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
398  return -1;
399  }
400 
401  // Delete the filter
402  snprintf(tmpBuffer, sizeof(tmpBuffer), "delete=%d", ntplInfo.ntplId);
403  if ((status = NT_NTPL(hCfgStream, tmpBuffer, &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
404  // Get the status code as text
405  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
406  fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer);
407  fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode);
408  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]);
409  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]);
410  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]);
411  return -1;
412  }
413 
414  // Close the config stream
415  if ((status = NT_ConfigClose(hCfgStream)) != NT_SUCCESS) {
416  // Get the status code as text
417  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
418  fprintf(stderr, "NT_ConfigClose() failed: %s\n", errorBuffer);
419  return -1;
420  }
421 
422  printf("Drop: %16lu packets, %16lu bytes\n", totDropsPkts, totDropsBytes);
423  printf("Done: %16d segments, %16d packets, %16lu bytes, %16lu bytes on wire\n", numSegments, numPackets, numBytes, numBytesWire);
424 
425  // Close down the NTAPI library
426  NT_Done();
427 
428  return 0;
429 }
430 
431 //
432 // EOF
433 //