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