netflow_example.c

Reference Documentation

product_line_custom
Napatech SmartNIC
category
Reference Information

Go to the documentation of this file.

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