net/netflow/netflow_example.c

Reference Documentation

product_line_custom
Napatech SmartNIC
category
Reference Information

Description

This source file is an example of how to use the Dynamic descriptor 1 to extract NetFlow information from a packet

Note:

This example does only work with accelerators having "dynamic descriptor 1"

Prerequisites

A Napatech capture accelerator is needed to run this example. The ntservice.ini must have at least one HostBuffersRx defined. Below is an example of a minimum ini-file. It will create a 32MB RX hostbuffer from NUMA node 0. [System] TimestampFormat = NATIVE [Adapter0] AdapterType = NT20E BusId = 00:0a:00.00 HostBuffersRx = [1,32,0]

Program flow

The following is required to perform real-time netflow on packets:
  • #include/nt.h - Applications/Tools only need to include nt.h to obtain prototypes, macros etc. from NTAPI.

  • NT_Init(NTAPI_VERSION) - Initialize the NTAPI library. NTAPI_VERSION is a define that describes the version of the API described in the header files included by nt.h. NT_Init() will ask the NTAPI library to convert return data to the NTAPI_VERSION if possible. This will ensure that applications can run on NTAPI libraries of newer versions.

  • NT_ConfigOpen() - Open a config stream in order to setup filter using the NT_NTPL() command.

  • NT_NetRxOpen() - Open a stream. The stream ID must match the one used when creating the filter using the NT_NTPL() command. A stream does not return data until traffic is assigned to it by creating a filter. Stream IDs might be shared between other streams and it is possible to make several filters to one stream ID. Each filter can have a unique color or color mask in the ASSIGN. The "color(mask)" of the ASSIGN can be used to mark packets making it possible for the stream to determine if the packets it receives via NT_NetRxGet() as based on its assign or if the packet belongs to the other streams that also share the hostbuffer.

  • NT_NTPL() - Assign traffic to a stream by creating a filter using a manually chosen stream ID. The stream ID must match the one used NT_NetRxOpen().

  • Optional step. Wait until we start seeing packets that are hit by the NTPL assign command. This is done to avoid getting packets that are not fully classified by the stream. NT_NetRxGet() is called with a timeout of 1000ms and will return NT_STATUS_TIMEOUT in case nothing is received within 1000ms and will return NT_SUCCESS if something is returned. Return values different from that is an indication of an error. Packets that are prior to the expected time are released via NT_NetRxRelease().

  • NT_NetRxGet() and NT_NetRxRelease() - Receive and release packets. NetFlow information is printed for each received packet with help of the dynamic offset fields within NtDyn1Descr_t.

  • NT_NetRxClose() - Close the stream when terminating. This will close the stream and release the NTPL assignment made on the hostbuffer.

Code

/* * %NT_SOFTWARE_LICENSE% */ /** * @example net/netflow/netflow_example.c * @section netflow_example_description Description * * This source file is an example of how to use the Dynamic descriptor 1 * to extract NetFlow information from a packet * * The following NTAPI functions are used: * - @ref NT_Init() * - @ref NT_ConfigOpen() * - @ref NT_NetRxOpen() * - @ref NT_NTPL() * - @ref NT_NetRxGet() * - @ref NT_NetRxRelease() * - @ref NT_NetRxClose() * - @ref NT_ConfigClose() * - @ref NT_ExplainError() * * @note * This example does only work with accelerators having "dynamic descriptor 1" * * <hr> * @section netflow_example_prerequisites Prerequisites * A Napatech capture accelerator is needed to run this example. The ntservice.ini must * have at least one HostBuffersRx defined. Below is an example of a * minimum ini-file. It will create a 32MB RX hostbuffer from NUMA * node 0. * @code * [System] * TimestampFormat = NATIVE * * [Adapter0] * AdapterType = NT20E * BusId = 00:0a:00.00 * HostBuffersRx = [1,32,0] * @endcode * * @section netflow_example_flow Program flow * @{ * The following is required to perform real-time netflow on packets: * - \#include/nt.h - Applications/Tools only need to include @ref * nt.h to obtain prototypes, macros etc. from NTAPI. * - @ref NT_Init(@ref NTAPI_VERSION) - Initialize the NTAPI * library. @ref NTAPI_VERSION is a define that describes the version * of the API described in the header files included by @ref * nt.h. NT_Init() will ask the NTAPI library to convert return data * to the @ref NTAPI_VERSION if possible. This will ensure that * applications can run on NTAPI libraries of newer versions. * - @ref NT_ConfigOpen() - Open a config stream in order to setup * filter using the @ref NT_NTPL() command. * - @ref NT_NetRxOpen() - Open a stream. The stream ID must match the * one used when creating the filter using the @ref NT_NTPL() * command. A stream does not return data until traffic is assigned * to it by creating a filter. Stream IDs might be shared between * other streams and it is possible to make several filters to one * stream ID. Each filter can have a unique color or color mask in the ASSIGN. * The "color(mask)" of the ASSIGN can be used to mark packets making it * possible for the stream to determine if the packets it receives * via @ref NT_NetRxGet() as based on its assign or if the packet belongs * to the other streams that also share the hostbuffer. * - @ref NT_NTPL() - Assign traffic to a stream by creating a filter * using a manually chosen stream ID. The stream ID must match the * one used @ref NT_NetRxOpen(). * - Optional step. Wait until we start seeing packets that are hit by * the NTPL assign command. This is done to avoid getting packets * that are not fully classified by the stream. NT_NetRxGet() is * called with a timeout of 1000ms and will return NT_STATUS_TIMEOUT * in case nothing is received within 1000ms and will return * NT_SUCCESS if something is returned. Return values different from * that is an indication of an error. Packets that are prior to the * expected time are released via NT_NetRxRelease(). * - NT_NetRxGet() and NT_NetRxRelease() - Receive and release packets. NetFlow information is printed for each received packet * with help of the dynamic offset fields within NtDyn1Descr_t. * - NT_NetRxClose() - Close the stream when terminating. This will * close the stream and release the NTPL assignment made on the * hostbuffer. * *<hr> * @section netflow_example_code Code * @} */ #include <nt.h> #include <inttypes.h> #include <netinet/in.h> struct IPv6Header_s { // Little endian encoding uint8_t ip_tclass1:4; uint8_t ip_v:4; uint8_t ip_flow1:4; uint8_t ip_tclass2:4; uint16_t ip_flow2; uint16_t ip_len; uint8_t ip_nexthdr; uint8_t ip_hoplim; uint32_t ip_src[4]; uint32_t ip_dest[4]; }; // 40 bytes; struct IPv4Header_s { uint16_t ip_hl: 4; uint16_t ip_v: 4; uint16_t ip_tos: 8; uint16_t ip_len; uint32_t ip_id:16; uint32_t ip_frag_off:16; #define IP_DONT_FRAGMENT 0x4000 #define IP_MORE_FRAGMENTS 0x2000 uint32_t ip_ttl:8; uint32_t ip_prot:8; uint32_t ip_crc:16; uint32_t ip_src; uint32_t ip_dest; }; //20 bytes struct UDPHeader_s { uint32_t udp_src:16; uint32_t udp_dest:16; uint32_t udp_len:16; uint32_t udp_crc:16; }; // 8 bytes struct TCPHeader_s { uint32_t tcp_src:16; uint32_t tcp_dest:16; uint32_t tcp_seq; uint32_t tcp_ack; uint32_t reserved:4; uint32_t tcp_doff:4; uint32_t tcp_ec_ctl:8; uint32_t tcp_window:16; uint32_t tcp_crc:16; uint32_t tcp_urgp:16; }; // 20 bytes #if defined(WIN32) || defined (WIN64) //#define snprintf _snprintf #define snprintf(a, b, c, d) _snprintf_s((a), _countof(a), (b), (c), (d)) #endif #define ARRAY_SIZE(a) (sizeof((a))/sizeof(*(a))) static void DumpL4(NtDyn1Descr_t *pDyn1) { printf(" %3d %8s | ", pDyn1->ipProtocol, pDyn1->ipProtocol == 6 ? "TCP" : pDyn1->ipProtocol == 17 ? "UDP" : "Other"); if (pDyn1->ipProtocol == 6) { struct TCPHeader_s *pl4 = (struct TCPHeader_s*)((uint8_t*)pDyn1 + pDyn1->descrLength + pDyn1->offset1); printf(" %04X | %04X | ", ntohs(pl4->tcp_src), ntohs(pl4->tcp_dest)); printf(" %03X | ", (pl4->reserved & 1) << 8 | pl4->tcp_ec_ctl); } else if (pDyn1->ipProtocol == 17) { struct UDPHeader_s *pl4 = (struct UDPHeader_s*)((uint8_t*)pDyn1 + pDyn1->descrLength + pDyn1->offset1); printf(" %04X | %04X | ", ntohs(pl4->udp_src), ntohs(pl4->udp_dest)); printf("%9s | ", "N/A"); } else { printf("%8s %9s | ", " ", " "); printf("%9s | ", " "); } printf("%8d bytes\n", pDyn1->capLength - 4 - pDyn1->descrLength - pDyn1->offset0); } static void DumpIPv4(NtDyn1Descr_t *pDyn1) { uint32_t ipaddr; struct IPv4Header_s *pl3 = (struct IPv4Header_s*)((uint8_t*)pDyn1 + pDyn1->descrLength + pDyn1->offset0); printf("%-16s | %-15s - %-15s | %-16s | %-8s | %-9s | %-9s | %-8s\n", "Time", "Src", "Dest", "Protocol", "Src port", "Dest port", "TCP flags", "Bytes"); printf("%16"PRIx64" | ", pDyn1->timestamp); ipaddr = ntohl(pl3->ip_src); printf("%03d.%03d.%03d.%03d - ", (ipaddr >> 24) & 0xFF, (ipaddr >> 16) & 0xFF, (ipaddr >> 8) & 0xFF, ipaddr & 0xFF); ipaddr = ntohl(pl3->ip_dest); printf("%03d.%03d.%03d.%03d | ", (ipaddr >> 24) & 0xFF, (ipaddr >> 16) & 0xFF, (ipaddr >> 8) & 0xFF, ipaddr & 0xFF); DumpL4(pDyn1); } static void DumpIPv6(NtDyn1Descr_t *pDyn1) { int i; struct IPv6Header_s *pl3 = (struct IPv6Header_s*)((uint8_t*)pDyn1 + pDyn1->descrLength + pDyn1->offset0); printf("%-16s | %-32s - %-32s | %-16s | %-8s | %-9s | %-9s | %-8s\n", "Time", "Src", "Dest", "Protocol", "Src port", "Dest port", "TCP flags", "Bytes"); printf("%16"PRIx64" | ", pDyn1->timestamp); for(i=0; i < 16; i++) { printf("%02x", *(((uint8_t*)&pl3->ip_src)+i)); } printf(" - "); for(i=0; i < 16; i++) { printf("%02x", *(((uint8_t*)&pl3->ip_dest)+i)); } printf(" | "); DumpL4(pDyn1); } int main(int argc, char *argv[]) { int ret = 0; int numPackets=0; // The number of packets received int numBytes=0; // The number of bytes received (wire length) char tmpBuffer[20]; // Buffer to build filter string char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer int status; // Status variable NtNetStreamRx_t hNetRx; // Handle to the RX stream NtConfigStream_t hCfgStream; // Handle to a config stream NtNtplInfo_t ntplInfo; // Return data structure from the NT_NTPL() call. NtNetBuf_t hNetBuf; // Net buffer container. Packet data is returned in this when calling NT_NetRxGet(). const char *ntplCommands[] = { "Assign[Priority=1;ColorMask=0x40] = (CvError == True) OR (CrcError == True) OR (Truncated == True)", "Assign[Priority=1;ColorMask=0x20] = Layer3Protocol != IP", "Assign[Priority=1;ColorMask=0x01] = Fragment == First", "Assign[Priority=1;ColorMask=0x02] = (Fragment == Middle) OR (Fragment == Last)", "Assign[Priority=1;ColorMask=0x00] = Layer3Protocol == IPv4", "Assign[Priority=1;ColorMask=0x04] = Layer3Protocol == IPv6", "Assign[Priority=1;ColorMask=0x08] = InnerLayer3Protocol == IPv4", "Assign[Priority=1;ColorMask=0x10] = InnerLayer3Protocol == IPv6", "Assign[Priority=0;streamid=1;Descriptor=Dyn1] = All" }; uint32_t ntplIds[ARRAY_SIZE(ntplCommands)]; unsigned i; // Initialize the NTAPI library and thereby check if NTAPI_VERSION can be used together with this library if((status = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); fprintf(stderr, "NT_Init() failed: %s\n", errorBuffer); return -1; } // Open a config stream to assign a filter to a stream ID. if((status = NT_ConfigOpen(&hCfgStream, "TestStream")) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer); return -1; } memset(ntplIds, 0, sizeof(ntplIds)); // Assign traffic to stream ID 1 and mask traffic matching the assign statements // with an individual color bit. // Select that all packets matching this are provided with the Dyn1 descriptor for(i=0; i < ARRAY_SIZE(ntplCommands); i++) { if((status = NT_NTPL(hCfgStream, ntplCommands[i], &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer); fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode); fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]); fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]); fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]); ret = -1; goto delete_out; } ntplIds[i] = ntplInfo.ntplId; } // Get a stream handle with the hostBuffer mapped to it. NT_NET_INTERFACE_PACKET specify that we will receive data packet-by-packet if((status = NT_NetRxOpen(&hNetRx, "TestStream", NT_NET_INTERFACE_PACKET, 1, -1)) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); fprintf(stderr, "NT_NetRxOpen() failed: %s\n", errorBuffer); return -1; } // Optional step. Wait for the first packet that hit the NTPL assign command printf("Waiting for the first packet\n"); while(1) { if((status = NT_NetRxGet(hNetRx, &hNetBuf, 1000)) != NT_SUCCESS) { if((status == NT_STATUS_TIMEOUT) || (status == NT_STATUS_TRYAGAIN)) { // Timeouts are ok, we just need to wait a little longer for a packet continue; } // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); fprintf(stderr, "NT_NetRxGet() failed: %s\n", errorBuffer); return -1; } // We got a packet. Check if the timestamp is newer than when the NTPL assign command was applied if(NT_NET_GET_PKT_TIMESTAMP(hNetBuf) > ntplInfo.ts) { break; // Break out, we have received a packet that is received after the NTPL assign command was applied } // Release the packet, it is too "old". if((status = NT_NetRxRelease(hNetRx, hNetBuf)) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); fprintf(stderr, "NT_NetRxRelease() failed: %s\n", errorBuffer); return -1; } } // Dump packet info. Stop when 10 packets has been received while(1) { if (NT_NET_GET_PKT_DESCRIPTOR_TYPE(hNetBuf) != NT_PACKET_DESCRIPTOR_TYPE_DYNAMIC || NT_NET_GET_PKT_DESCRIPTOR_FORMAT(hNetBuf) != 1) { fprintf(stderr, "Expected Dynamic Descriptor 1\n"); return -1; } NtDyn1Descr_t *pDyn1 = NT_NET_DESCR_PTR_DYN1(hNetBuf); if (argc == 2 && argv[1][0]=='v') { printf("caplength : %d\n", pDyn1->capLength); printf("color : %d\n", pDyn1->color); printf("descrFormat: %d\n", pDyn1->descrFormat); printf("descrLength: %d\n", pDyn1->descrLength); printf("tsColor : %d\n", pDyn1->tsColor); printf("IPProt : %d\n", pDyn1->ipProtocol); printf("offset0 : %d\n", pDyn1->offset0); printf("offset1 : %d\n", pDyn1->offset1); printf("offset2 : %d\n", pDyn1->offset2); printf("rxport : %d\n", pDyn1->rxPort); printf("timestamp : %16lX\n", pDyn1->timestamp); } if (pDyn1->color & (1 << 6)) { printf("Packet contain an error and decoding cannot be trusted\n"); } else { if (pDyn1->color & (1 << 5)) { printf("A non IPv4,IPv6 packet received\n"); } else if (pDyn1->color & 3) { printf("Fragmented packet. Must be assembled before the netflow information can be gathered\n"); } else { switch (pDyn1->color >> 2) { case 0: // IPv4 printf("IPv4 packet received\n"); DumpIPv4(pDyn1); break; case 1: // IPv6 printf("IPv6 packet received\n"); DumpIPv6(pDyn1); break; case 2: // Tunneled IPv4 printf("Tunneled IPv4 packet received\n"); DumpIPv4(pDyn1); break; case 3: // Tunneled IPv6 printf("Tunneled IPv6 packet received\n"); DumpIPv6(pDyn1); break; } } } // Increment the number of packets processed. numPackets++; // Increment the bytes received numBytes+=pDyn1->capLength; // Release the current packet if((status = NT_NetRxRelease(hNetRx, hNetBuf)) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); fprintf(stderr, "NT_NetRxGet() failed: %s\n", errorBuffer); return -1; } if(numPackets == 10) { break; } // Get the next packet while(1) { if((status = NT_NetRxGet(hNetRx, &hNetBuf, 1000)) != NT_SUCCESS) { if((status == NT_STATUS_TIMEOUT) || (status == NT_STATUS_TRYAGAIN)) { // Timeouts are ok, we just need to wait a little longer for a packet continue; } // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); fprintf(stderr, "NT_NetRxGet() failed: %s\n", errorBuffer); return -1; } break; // We got a packet } } delete_out: // Delete the filters for(i=0; i < ARRAY_SIZE(ntplCommands); i++) { if(!ntplIds[i]) { continue; } snprintf(tmpBuffer, 20, "delete=%u", ntplIds[i]); if((status = NT_NTPL(hCfgStream, tmpBuffer, &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer); fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode); fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]); fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]); fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]); return -1; } } // Close the config stream if((status = NT_ConfigClose(hCfgStream)) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); fprintf(stderr, "NT_ConfigClose() failed: %s\n", errorBuffer); return -1; } // Close the stream and release the hostbuffer. This will also remove the NTPL assignments performed. NT_NetRxClose(hNetRx); printf("Done: %d packets, %d bytes\n", numPackets, numBytes); return ret; }