flowmatch_example_receiver.cpp Source File

Reference Documentation

Platform
Napatech SmartNIC
Content Type
Reference Information
Capture Software Version
Link™ Capture Software 12.15
Napatech Software Suite: examples/flowmatch/flowmatch_example_receiver.cpp Source File
flowmatch_example_receiver.cpp
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 // Include this in order to access the Napatech API
44 #include <nt.h>
45 
46 #include <atomic>
47 #include <chrono>
48 #include <cstring>
49 #include <iostream>
50 #include <memory>
51 #include <thread>
52 #include <vector>
53 
55 
57 
58 // This application requires 64-bit pointers
59 static_assert(sizeof(NtFlow_t*) == sizeof(uint64_t), "Pointers must be 64bit");
60 
61 namespace {
62 
63 /**
64  * Print flow info and status records related to flow stream.
65  */
66 void printFlowStreamInfo(NtFlowStream_t& flowStream, std::vector<std::unique_ptr<NtFlow_t>>& learnedFlowList,
67  std::atomic<uint64_t>* ipv4counter, std::atomic<uint64_t>* ipv6counter)
68 {
69  const char* ip;
70  NtFlowInfo_t flowInfo;
71  NtFlowStatus_t flowStatus;
72 
73  // For each element in internal flow stream queue print the flow status record.
74  while (NT_FlowStatusRead(flowStream, &flowStatus) == NT_SUCCESS) {
75  ip = learnedFlowList[flowStatus.id]->keyId == KEY_ID_IPV4 ? " (IPv4): " : " (IPv6): ";
76  std::cout << "Flow status for ID " << flowStatus.id << ip;
77 
78  // The flags returned from NT_FlowStatusRead depend on ntservice.ini settings.
79  switch (flowStatus.flags) {
80  case NT_FLOW_STAT_LDS: std::cout << "NT_FLOW_STAT_LDS" << std::endl; break;
81  case NT_FLOW_STAT_LFS: std::cout << "NT_FLOW_STAT_LFS" << std::endl; break;
82  case NT_FLOW_STAT_LIS: std::cout << "NT_FLOW_STAT_LIS" << std::endl; break;
83  case NT_FLOW_STAT_UDS: std::cout << "NT_FLOW_STAT_UDS" << std::endl; break;
84  case NT_FLOW_STAT_UIS: std::cout << "NT_FLOW_STAT_UIS" << std::endl; break;
85  default: std::cout << "Unknown flag" << std::endl; break;
86  }
87  }
88 
89  std::cout << std::endl;
90 
91  // For each element in internal flow stream queue print the flow info record.
92  // The flow info record is only available when NtFlow_t.gfi is set to 1.
93  // Maintaining the flow info record has a performance overhead, so if the
94  // info record is not need, it is recommended to set NtFlow_t.gfi to 0.
95  while (NT_FlowRead(flowStream, &flowInfo, 0) == NT_SUCCESS) {
96  if (learnedFlowList[flowInfo.id]->keyId == KEY_ID_IPV4) {
97  ip = " (IPv4):";
98  *ipv4counter += (flowInfo.packetsA + flowInfo.packetsB);
99  }
100  else {
101  ip = " (IPv6):";
102  *ipv6counter += (flowInfo.packetsA + flowInfo.packetsB);
103  }
104 
105  std::cout << "NT_FlowRead of flow ID " << flowInfo.id << ip << std::endl
106  << "CSA: packets: " << flowInfo.packetsA
107  << ", Octets: " << flowInfo.octetsA << std::endl
108  << "CSB: packets: " << flowInfo.packetsB
109  << ", Octets: " << flowInfo.octetsB << std::endl
110  << "Time stamp: " << flowInfo.ts << std::endl
111  << "TCP flags A: " << flowInfo.flagsA
112  << ", TCP flags B: " << flowInfo.flagsB << std::endl;
113 
114  switch (flowInfo.cause) {
115  case 0: std::cout << "Unlearn cause: Software" << std::endl; break;
116  case 1: std::cout << "Unlearn cause: Timeout" << std::endl; break;
117  case 2: std::cout << "Unlearn cause: TCP flow termination" << std::endl; break;
118  default: std::cout << "Unlearn cause: Not supported" << std::endl; break;
119  }
120  std::cout << std::endl;
121  }
122 }
123 
124 } // Unnamed namespace
125 
126 void taskReceiverMiss(const char* streamName, uint32_t streamId,
127  std::atomic<uint64_t>* ipv4counter, std::atomic<uint64_t>* ipv6counter)
128 {
129  int status;
130  uint64_t idCounter = 0U;
131  std::vector<std::unique_ptr<NtFlow_t>> learnedFlowList;
132 
133  NtFlowAttr_t flowAttr;
134  NtFlowStream_t flowStream;
135 
136  NtNetStreamRx_t hNetRx;
137  NtNetBuf_t hNetBuffer;
138 
139  // Initialize flow stream attributes and set adapter number attribute.
140  NT_FlowOpenAttrInit(&flowAttr);
142 
143  // Opens a flow programming stream and returns a stream handle (flowStream).
144  status = NT_FlowOpen_Attr(&flowStream, "flowmatch_example_receiver_attr", &flowAttr);
145  handleErrorStatus(status, "NT_FlowOpen_Attr() failed");
146 
147  uint32_t FlowStreamVersion = NT_FlowGetVersion(flowStream);
148  std::cout << "Detected Flow Stream API verion " << FlowStreamVersion << std::endl;
149 
150  // Retrieve handle to rx stream.
151  status = NT_NetRxOpen(&hNetRx, streamName, NT_NET_INTERFACE_PACKET, streamId, -1);
152  handleErrorStatus(status, "NT_NetRxOpen() failed");
153 
154  // As long as packets are being transmitted, this loop will handle all
155  // packets from the MISSED filters. When a flow has been learned, it will no
156  // longer be received here.
157  while (applicationRunning()) {
158  // Get packet from rx stream.
159  status = NT_NetRxGetNextPacket(hNetRx, &hNetBuffer, 100);
160  if (status == NT_STATUS_TIMEOUT || status == NT_STATUS_TRYAGAIN) continue;
161  handleErrorStatus(status, "NT_NetRxGetNextPacket() failed");
162 
163  // Here a packet has successfully been received, and the parameters for the
164  // next flow to be learned will be set up.
165  auto flow = std::unique_ptr<NtFlow_t>(new NtFlow_t);
166  std::memset(flow.get(), 0x0, sizeof(NtFlow_t));
167 
168  // In this example, the ID is a simple incremental value that can be used
169  // for lookup in the std::vector learnedFlowList. However, any value can be used,
170  // including the raw value of pointers.
171  flow->id = idCounter++; // User defined ID
172  flow->color = 0; // Flow color
173  flow->overwrite = 0; // Overwrite filter action (1: enable, 0: disable)
174  flow->streamId = 0; // Marks the stream id if overwrite filter action is enabled
175  flow->ipProtocolField = 6; // IP protocol number of next header (6: TCP)
176  flow->keySetId = KEY_SET_ID; // Key Set ID as used in the NTPL filter
177  flow->op = 1; // Flow programming operation (1: learn, 0: un-learn)
178  flow->gfi = 1; // Generate flow info record (1: generate, 0: do not generate)
179  flow->tau = 0; // TCP auto unlearn (1: auto unlearn enable, 0: auto unlearn disable)
180 
181  // For this example the descriptor DYN3 is used, which is set up by NTPL.
182  NtDyn4Descr_t* dyn4 = _NT_NET_GET_PKT_DESCR_PTR_DYN4(hNetBuffer);
183  uint8_t* packet = reinterpret_cast<uint8_t*>(dyn4) + dyn4->descrLength;
184 
185  // Because colormask was used in the filters, it is very easy to check for
186  // the IP type.
187  // The filters also set up an alternative offset0, such that it points
188  // directly to the IP source address.
189  switch (dyn4->color0 & (COLOR_IPV4 | COLOR_IPV6)) {
190  case COLOR_IPV4: {
191  *ipv4counter += 1U;
192  std::memcpy(flow->keyData, packet + dyn4->offset0, 4); // IPv4 src
193  std::memcpy(flow->keyData + 4, packet + dyn4->offset0 + 4, 4); // IPv4 dst
194  std::memcpy(flow->keyData + 8, packet + dyn4->offset1, 2); // TCP port src
195  std::memcpy(flow->keyData + 10, packet + dyn4->offset1 + 2, 2); // TCP port dst
196  flow->keyId = KEY_ID_IPV4; // Key ID as used in the NTPL Key Test
197  break;
198  }
199  case COLOR_IPV6: {
200  *ipv6counter += 1U;
201  std::memcpy(flow->keyData, packet + dyn4->offset0, 16); // IPv6 src
202  std::memcpy(flow->keyData + 16, packet + dyn4->offset0 + 16, 16); // IPv6 dst
203  std::memcpy(flow->keyData + 32, packet + dyn4->offset1, 2); // TCP port src
204  std::memcpy(flow->keyData + 34, packet + dyn4->offset1 + 2, 2); // TCP port dst
205  flow->keyId = KEY_ID_IPV6; // Key ID as used in the NTPL Key Test
206  break;
207  }
208  }
209 
210  // Program the flow into the adapter.
211  status = NT_FlowWrite(flowStream, flow.get(), -1);
212  handleErrorStatus(status, "NT_FlowWrite() failed");
213 
214  learnedFlowList.push_back(std::move(flow));
215  }
216 
217  // Unlearn all stored flows
218  for (auto&& flow : learnedFlowList) {
219  flow->op = 0;
220  status = NT_FlowWrite(flowStream, flow.get(), -1);
221  handleErrorStatus(status, "NT_FlowWrite() failed");
222  }
223 
224  // While it doesn't take a full 1000 ms for the internal flow status queue to
225  // be updated, it is still a slow operation, thus waiting to a bit after
226  // NT_FlowWrite is a good idea.
227  std::this_thread::sleep_for(std::chrono::milliseconds(1000));
228  printFlowStreamInfo(flowStream, learnedFlowList, ipv4counter, ipv6counter);
229 
230  // Closes rx stream.
231  status = NT_NetRxClose(hNetRx);
232  handleErrorStatus(status, "NT_NetRxClose() failed");
233 
234  // Closes flow programming stream
235  status = NT_FlowClose(flowStream);
236  handleErrorStatus(status, "NT_FlowClose() failed");
237 }
238 
239 void taskReceiverCounter(const char* streamName, uint32_t streamId,
240  std::atomic<uint64_t>* counter)
241 {
242  int status;
243 
244  NtNetStreamRx_t hNetRx;
245  NtNetBuf_t hNetBuffer;
246 
247  // Retrieve handle to rx stream.
248  status = NT_NetRxOpen(&hNetRx, streamName, NT_NET_INTERFACE_PACKET, streamId, -1);
249  handleErrorStatus(status, "NT_NetRxOpen() failed");
250 
251  while (applicationRunning()) {
252  // Get packet from rx stream.
253  status = NT_NetRxGetNextPacket(hNetRx, &hNetBuffer, 100);
254  if (status == NT_STATUS_TIMEOUT || status == NT_STATUS_TRYAGAIN) continue;
255  handleErrorStatus(status, "NT_NetRxGetNextPacket() failed");
256 
257  // When a packet has successfully been received increment the counter.
258  *counter += 1U;
259  }
260 
261  // Closes rx stream.
262  status = NT_NetRxClose(hNetRx);
263  handleErrorStatus(status, "NT_NetRxClose() failed");
264 }