net/replay4GA/replay4ga_example.c

Reference Documentation

product_line_custom
Napatech SmartNIC
category
Reference Information

Prerequisites

  • Capture file, capfile.ntcap, that has been captured with the net/capture/capture_example.c example.

  • A fourth generation adapter supporting transmit on timestamp.

  • The ntservice.ini must have at least one HostBuffersTx defined. Below is an example of a minimum ini-file. It will create a 32MB TX hostbuffer from NUMA node 0. [System] TimestampFormat = NATIVE [Adapter0] AdapterType = NT20E2 BusId = 00:0a:00.00 HostBuffersTx = [1,32,0]

Program flow

The following is required to perform replay of a captured file:
  • #include/nt.h - Applications/Tools only need to include nt.h to obtain prototypes, macros etc. from NTAPI.

  • #include/unistd.h - sleep()

  • 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 the configuration stream.

  • NT_ConfigRead() - Read from the configuration stream to fetch the adapter timestamp.

  • NT_ConfigWrite() - Write to the configuration stream to set transmit on timestamp configuration.

  • NT_ConfigClose() - Close the configuration stream.

  • NT_InfoOpen() - Open the information stream.

  • NT_InfoRead() - Read the adapter information to check whether or not we are using absolut TX timing. The txTiming tells whether relative or absolut TX timing is enabled.

  • NT_InfoClose() - Close the information stream.

  • NT_NetFileOpen() - Open the captured file and assign it to a stream.

  • NT_NetTxOpen() - Open a hostbuffer than can transmit packets to port 0.

  • NT_NetFileGet() - Get a segment from the file. This call will return NT_SUCCESS upon return of a segment and NT_STATUS_END_OF_FILE when there is no more segments avaialble. The latter will cause the example to exit.

  • NT_NetTxGet() - Get an empty tx buffer. This will get a segment of the requested length from the hostbuffer that will be sent onto port 0 when the buffer is released.
    • NT_NET_GET_SEGMENT_PTR() and NT_NET_GET_SEGMENT_LENGTH() are used to tell where the segment data is located and how much there is. _nt_net_build_pkt_netbuf() and _nt_net_get_next_packet() are used to traverse packets inside a segment. This is usefull if ie. the timing of the packet transmit rate needs to be changed.

  • NT_NetTxRelease() - Release the segment buffer. Once a tx buffer is released it will be transmitted according to the timestamps of the packets in the segment.

  • NT_NetFileRelease() - Release the segment from the file stream.

  • NT_NetFileClose() - Close the file stream when no more segments can be found.

  • NT_NetTxClose() - Close the TX stream.

Code

/* * %NT_SOFTWARE_LICENSE% */ /** * @example net/replay4GA/replay4ga_example.c * @section replay4ga_example_description Description * * This source file is an example of how to replay a capture file onto * a port using NTAPI when using a 4GA transmit on timestamp adapter. * * The following NTAPI functions are used: * - @ref NT_Init() * - @ref NT_ConfigOpen() * - @ref NT_ConfigRead() * - @ref NT_ConfigWrite() * - @ref NT_ConfigClose() * - @ref NT_InfoOpen() * - @ref NT_InfoRead() * - @ref NT_InfoClose() * - @ref NT_NetFileOpen() * - @ref NT_NetTxOpen() * - @ref NT_NTPL() * - @ref NT_NetFileGet() * - @ref NT_NetTxGet() * - @ref NT_NET_GET_SEGMENT_PTR() * - @ref NT_NET_GET_SEGMENT_LENGTH() * - @ref NT_NetTxRelease() * - @ref NT_NetFileRelease() * - @ref NT_NetFileClose() * - @ref NT_NetTxClose() * - @ref NT_ExplainError() * * @section replay4ga_example_prerequisites Prerequisites * - Capture file, capfile.ntcap, that has been captured with the @ref * net/capture/capture_example.c "net/capture/capture_example.c" example. * - A fourth generation adapter supporting transmit on timestamp. * - The ntservice.ini must have at least one HostBuffersTx * defined. Below is an example of a minimum ini-file. It will * create a 32MB TX hostbuffer from NUMA node 0. * @code * [System] * TimestampFormat = NATIVE * * [Adapter0] * AdapterType = NT20E2 * BusId = 00:0a:00.00 * HostBuffersTx = [1,32,0] * @endcode * * @section replay4ga_example_flow Program flow * @{ * The following is required to perform replay of a captured file: * - \#include/nt.h - Applications/Tools only need to include @ref * nt.h to obtain prototypes, macros etc. from NTAPI. * - \#include/unistd.h - sleep() * - @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 the configuration stream. * - @ref NT_ConfigRead() - Read from the configuration stream to fetch the * adapter timestamp. * - @ref NT_ConfigWrite() - Write to the configuration stream to set transmit * on timestamp configuration. * - @ref NT_ConfigClose() - Close the configuration stream. * - @ref NT_InfoOpen() - Open the information stream. * - @ref NT_InfoRead() - Read the adapter information to check whether or not * we are using absolut TX timing. The @ref NtTxTimingMethod_e "txTiming" * tells whether relative or absolut TX timing is enabled. * - @ref NT_InfoClose() - Close the information stream. * - @ref NT_NetFileOpen() - Open the captured file and assign it to a stream. * - @ref NT_NetTxOpen() - Open a hostbuffer than can transmit packets to port 0. * - @ref NT_NetFileGet() - Get a segment from the file. This call will * return @ref NT_SUCCESS upon return of a segment and * @ref NT_STATUS_END_OF_FILE when there is no more segments * avaialble. The latter will cause the example to exit. * - @ref NT_NetTxGet() - Get an empty tx buffer. This will get a segment of the * requested length from the hostbuffer that will be sent * onto port 0 when the buffer is released. * - @ref NT_NET_GET_SEGMENT_PTR() and @ref NT_NET_GET_SEGMENT_LENGTH() are used * to tell where the segment data is located and how much there is. * _nt_net_build_pkt_netbuf() and _nt_net_get_next_packet() are used to traverse * packets inside a segment. This is usefull if ie. the timing of the packet * transmit rate needs to be changed. * - @ref NT_NetTxRelease() - Release the segment buffer. Once a tx * buffer is released it will be transmitted according to the * timestamps of the packets in the segment. * - @ref NT_NetFileRelease() - Release the segment from the file stream. * - @ref NT_NetFileClose() - Close the file stream when no more segments can be found. * - @ref NT_NetTxClose() - Close the TX stream. * *<hr> * @section replay4ga_example_code Code * @} */ #include <nt.h> #include <unistd.h> #if defined(__linux__) || defined(__FreeBSD__) #include <string.h> // memcpy #endif // default setup #define PORT 0 int main(void) { int numPackets=0; // The number of packets replayed int numBytes=0; // The number of bytes replayed char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer int status; // Status variable NtNetStreamFile_t hNetFile; // Handle to the File stream NtNetBuf_t hNetBufFile; // Net buffer container. Used to return segments from the file stream NtNetStreamTx_t hNetTx; // Handle to the TX stream NtNetBuf_t hNetBufTx; // Net buffer container. Used when getting a transmit buffer NtInfoStream_t hInfo; // Handle to a info stream NtInfo_t infoRead; // Buffer to hold data from infostream NtConfigStream_t hConfig; // Handle to a config stream NtConfig_t configRead; // Config stream data container NtConfig_t configWrite; // Config stream data container struct NtNetBuf_s pktNetBuf; // Packet netbuf structure. uint8_t adapterNo ; // Adapter no uint64_t firstPacketTS=0; // Timestamp of first packet uint64_t lastPacketTS=0; // Timestamp of last packet uint64_t adapterTS=0; // Timestamp on the adapter uint64_t timeDelta=0; // Calculated time delta // 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)-1); fprintf(stderr, "NT_Init() failed: %s\n", errorBuffer); return -1; } // Open the infostream. if((status = NT_InfoOpen(&hInfo, "replay")) != NT_SUCCESS) { NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_InfoOpen() failed: %s\n", errorBuffer); return -1; } // Check whether or not TX is supported on the defined port infoRead.cmd = NT_INFO_CMD_READ_PORT_V9; infoRead.u.port_v9.portNo=PORT; // TX port is hardcoded to defined PORT if((status = NT_InfoRead(hInfo, &infoRead)) != NT_SUCCESS) { NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer); NT_InfoClose(hInfo); return -1; } // save adapter number of the TX port adapterNo = infoRead.u.port_v9.data.adapterNo ; if(infoRead.u.port_v9.data.capabilities.featureMask&NT_PORT_FEATURE_RX_ONLY) { fprintf(stderr, "Transmit is not possible on the selected port %d\n",PORT); NT_InfoClose(hInfo); return -1; } // Check whether or not absolut TX timing is supported adapterNo = infoRead.u.port_v9.data.adapterNo ; infoRead.cmd = NT_INFO_CMD_READ_ADAPTER_V6; infoRead.u.adapter_v6.adapterNo=adapterNo; // Adapter is derived from PORT of adapter if((status = NT_InfoRead(hInfo, &infoRead)) != NT_SUCCESS) { NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer); NT_InfoClose(hInfo); return -1; } NT_InfoClose(hInfo); // Open the capture file to replay (captured with the capture example) if((status = NT_NetFileOpen(&hNetFile, "FileStream", NT_NET_INTERFACE_SEGMENT, "capfile.ntcap")) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_NetFileOpen() failed: %s\n", errorBuffer); return -1; } // Read one segment from the file to get the first packet timestamp if ((status = NT_NetFileGet(hNetFile, &hNetBufFile)) != NT_SUCCESS) { if (status == NT_STATUS_END_OF_FILE) { fprintf(stderr, "The file %s has no data\n", "capfile.ntpcap"); return -1; } } _nt_net_build_pkt_netbuf(hNetBufFile, &pktNetBuf); firstPacketTS = NT_NET_GET_PKT_TIMESTAMP(&pktNetBuf) * 10; // Convert 10ns ticks to 1ns // Close the file again. We will open it again later to actually transmit packets. NT_NetFileClose(hNetFile); // Open the config stream if ((status = NT_ConfigOpen(&hConfig, "replay")) != NT_SUCCESS) { NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer); NT_ConfigClose(hConfig); return 0; } // Read the adapter time configRead.parm = NT_CONFIG_PARM_ADAPTER_TIMESTAMP; configRead.u.timestampRead.adapter = PORT; if ((status = NT_ConfigRead(hConfig, &configRead)) != NT_SUCCESS) { NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_ConfigRead() failed: %s\n", errorBuffer); NT_ConfigClose(hConfig); return 0; } adapterTS = configRead.u.timestampRead.data.nativeUnixTs * 10; // Convert 10ns ticks to 1ns // Calculate time delta - we add 1 second to give ourselves some headroom timeDelta = (adapterTS - firstPacketTS) + 1000000000; // Configure transmit on timestamp configWrite.parm = NT_CONFIG_PARM_PORT_TRANSMIT_ON_TIMESTAMP; configWrite.u.transmitOnTimestamp.portNo = (uint8_t) PORT; configWrite.u.transmitOnTimestamp.data.timeDelta = timeDelta; configWrite.u.transmitOnTimestamp.data.enable = true; configWrite.u.transmitOnTimestamp.data.forceTxOnTs = true; if ((status = NT_ConfigWrite(hConfig, &configWrite)) != NT_SUCCESS) { NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_ConfigWrite() failed: %s\n", errorBuffer); NT_ConfigClose(hConfig); return 0; } // Open the capture file to replay (captured with the capture example) if((status = NT_NetFileOpen(&hNetFile, "FileStream", NT_NET_INTERFACE_SEGMENT, "capfile.ntcap")) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_NetFileOpen() failed: %s\n", errorBuffer); return -1; } // Open a TX hostbuffer from TX host buffer pool on the NUMA node of the adapter with the defined PORT if((status = NT_NetTxOpen(&hNetTx, "TxStreamPort", (0x1<<PORT), NT_NETTX_NUMA_ADAPTER_HB, 0)) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_NetTxOpen() failed: %s\n", errorBuffer); return -1; } // Get segments from the file and transmit them to defined PORT while(1) { printf("Getting segment.\n"); // Get the packet if((status = NT_NetFileGet(hNetFile, &hNetBufFile)) != NT_SUCCESS) { if(status == NT_STATUS_END_OF_FILE) { // The file has no more data break; } // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_NetFileGet() failed: %s\n", errorBuffer); return -1; } NtNetFileRead_t fileRead; fileRead.cmd = NT_NETFILE_READ_INFO_CMD_V1; if ((status = NT_NetFileRead(hNetFile, &fileRead)) != NT_SUCCESS) { NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_NetfileRead() failed: %s\n", errorBuffer); return -1; } lastPacketTS = fileRead.u.info_v1.lastTimestamp * 10; // Get a TX buffer for this segment if((status = NT_NetTxGet(hNetTx, &hNetBufTx, PORT, NT_NET_GET_SEGMENT_LENGTH(hNetBufFile), NT_NETTX_SEGMENT_OPTION_RAW, -1 /* wait forever */)) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_NetFileGet() failed: %s\n", errorBuffer); return -1; } // Copy the segment into the TX buffer memcpy(NT_NET_GET_SEGMENT_PTR(hNetBufTx), NT_NET_GET_SEGMENT_PTR(hNetBufFile), NT_NET_GET_SEGMENT_LENGTH(hNetBufFile)); // Build a packet netbuf structure _nt_net_build_pkt_netbuf(hNetBufTx, &pktNetBuf); // Optional step. Here all packets are inspected before sent if(NT_NET_GET_SEGMENT_LENGTH(hNetBufTx)) { do { // Just count the amount of packets and wire length numPackets++; numBytes+=NT_NET_GET_PKT_WIRE_LENGTH((&pktNetBuf)); } while(_nt_net_get_next_packet(hNetBufTx, NT_NET_GET_SEGMENT_LENGTH(hNetBufTx), &pktNetBuf)>0); } // Release the TX buffer and the packets within the segment will be transmitted if((status = NT_NetTxRelease(hNetTx, hNetBufTx)) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_NetTxRelease() failed: %s\n", errorBuffer); return -1; } // Release the file packet if((status = NT_NetFileRelease(hNetFile, hNetBufFile)) != NT_SUCCESS) { // Get the status code as text NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_NetFileRelease() failed: %s\n", errorBuffer); return -1; } } // Wait until all packets should have been sent while(1) { // Read the adapter time configRead.parm = NT_CONFIG_PARM_ADAPTER_TIMESTAMP; configRead.u.timestampRead.adapter = PORT; if ((status = NT_ConfigRead(hConfig, &configRead)) != NT_SUCCESS) { NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_ConfigRead() failed: %s\n", errorBuffer); NT_ConfigClose(hConfig); return 0; } adapterTS = configRead.u.timestampRead.data.nativeUnixTs * 10; // Convert 10ns ticks to 1ns if (adapterTS > (lastPacketTS + timeDelta)) { // The last packet should be sending now break; } sleep(1); } // Close the file stream NT_NetFileClose(hNetFile); // Close the TX stream NT_NetTxClose(hNetTx); // Disable transmit on timestamp configWrite.parm = NT_CONFIG_PARM_PORT_TRANSMIT_ON_TIMESTAMP; configWrite.u.transmitOnTimestamp.portNo = (uint8_t) PORT; configWrite.u.transmitOnTimestamp.data.timeDelta = 0; configWrite.u.transmitOnTimestamp.data.enable = false; configWrite.u.transmitOnTimestamp.data.forceTxOnTs = false; if ((status = NT_ConfigWrite(hConfig, &configWrite)) != NT_SUCCESS) { NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)-1); fprintf(stderr, "NT_ConfigWrite() failed: %s\n", errorBuffer); NT_ConfigClose(hConfig); return 0; } // Close the config stream NT_ConfigClose(hConfig); printf("Done: %d packets %d bytes has been replayed\n", numPackets, numBytes); return 0; }