net/replay4GA/replay4ga_example.c

Reference Documentation

product_line_custom
IntelĀ® PAC
Napatech SmartNIC
category
Reference Information
Napatech Software Suite: net/replay4GA/replay4ga_example.c
net/replay4GA/replay4ga_example.c

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:

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

/*
*
* Copyright 2020 Napatech A/S. All Rights Reserved.
*
* 1. Copying, modification, and distribution of this file, or executable
* versions of this file, is governed by the terms of the Napatech Software
* license agreement under which this file was made available. If you do not
* agree to the terms of the license do not install, copy, access or
* otherwise use this file.
*
* 2. Under the Napatech Software license agreement you are granted a
* limited, non-exclusive, non-assignable, copyright license to copy, modify
* and distribute this file in conjunction with Napatech SmartNIC's and
* similar hardware manufactured or supplied by Napatech A/S.
*
* 3. The full Napatech Software license agreement is included in this
* distribution, please see "NP-0405 Napatech Software license
* agreement.pdf"
*
* 4. Redistributions of source code must retain this copyright notice,
* list of conditions and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTIES, EXPRESS OR
* IMPLIED, AND NAPATECH DISCLAIMS ALL IMPLIED WARRANTIES INCLUDING ANY
* IMPLIED WARRANTY OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, OR OF
* FITNESS FOR A PARTICULAR PURPOSE. TO THE EXTENT NOT PROHIBITED BY
* APPLICABLE LAW, IN NO EVENT SHALL NAPATECH BE LIABLE FOR PERSONAL INJURY,
* OR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER,
* INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, CORRUPTION OR
* LOSS OF DATA, FAILURE TO TRANSMIT OR RECEIVE ANY DATA OR INFORMATION,
* BUSINESS INTERRUPTION OR ANY OTHER COMMERCIAL DAMAGES OR LOSSES, ARISING
* OUT OF OR RELATED TO YOUR USE OR INABILITY TO USE NAPATECH SOFTWARE OR
* SERVICES OR ANY THIRD PARTY SOFTWARE OR APPLICATIONS IN CONJUNCTION WITH
* THE NAPATECH SOFTWARE OR SERVICES, HOWEVER CAUSED, REGARDLESS OF THE THEORY
* OF LIABILITY (CONTRACT, TORT OR OTHERWISE) AND EVEN IF NAPATECH HAS BEEN
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT ALLOW
* THE EXCLUSION OR LIMITATION OF LIABILITY FOR PERSONAL INJURY, OR OF
* INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU.
*
*
*/
/**
* @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));
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));
fprintf(stderr, "NT_InfoOpen() failed: %s\n", errorBuffer);
return -1;
}
// Check whether or not TX is supported on the defined port
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));
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 ;
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.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));
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));
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));
fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
NT_ConfigClose(hConfig);
return 0;
}
// Read the adapter time
configRead.u.timestampRead.adapter = PORT;
if ((status = NT_ConfigRead(hConfig, &configRead)) != NT_SUCCESS) {
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
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.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));
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));
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));
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));
fprintf(stderr, "NT_NetFileGet() failed: %s\n", errorBuffer);
return -1;
}
NtNetFileRead_t fileRead;
if ((status = NT_NetFileRead(hNetFile, &fileRead)) != NT_SUCCESS) {
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
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));
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,
&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));
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));
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.u.timestampRead.adapter = PORT;
if ((status = NT_ConfigRead(hConfig, &configRead)) != NT_SUCCESS) {
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
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.u.transmitOnTimestamp.portNo = (uint8_t) PORT;
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));
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;
}