net/inline/inline_example.c

Reference Documentation

Platform
Intel® PAC
Napatech SmartNIC
Content Type
Reference Information
Capture Software Version
Link™ Capture Software 12.10
Napatech Software Suite: net/inline/inline_example.c
net/inline/inline_example.c

Description

INFO: This is an 3GA and 4GA inline example. A more advanced 4GA inline example can be found in examples/net/vlandemo/.

This source file is an example of how to run in an inline scenario. The example will discard every 2nd packet.

The following NTAPI functions are used:

Prerequisites

An Napatech inline accelerator with at least one RX host buffer per port defined. Below is an example of a minimum ini-file. It will create two 32MB RX hostbuffers from NUMA node 0. The source and destination is determined by the filter. Use a traffic generator to apply traffic to one of the ports.

[System]
TimestampFormat = NATIVE
[Adapter0]
AdapterType = NT20E
BusId = 00:0a:00.00
HostBuffersRx = [2,32,0]

Program flow

The following is required to run in an inline scenario:

  • #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_InfoOpen() - Detect the adapter generation and if TX is available.
  • 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 doesn't 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 in the ASSIGN. The "color" 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(). On 4GA adapters and above, the Setup NTPL statement must be used to forward the packets from the stream to the output tx port (as opposed to the older form on 3Garch adapters where this could be done in the Assign statement). To indicate whether a packet should be dropped, we use the DYN3 descriptors and indicate that the wirelength is used.
  • 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(), discard every 2nd packet and NT_NetRxRelease() - Receive packets, discard every 2nd and release packets. The Packet macros are used to find the packet length ant the timestamp of the packet:
    • NT_NET_GET_PKT_WIRE_LENGTH() - Get length of the packet to store.
    • NT_NET_GET_PKT_TIMESTAMP() - The time the packet was delivered.
    • NT_NET_SET_PKT_TXIGNORE() - Don't send the current packet (3GArch).
    • NT_NET_GET_PKT_DESCR_PTR() - Get the packet descriptor header (4GArch). In the 4GArch, we set the wire length to 0 in order to discard the packet as opposed to using the NT_NET_SET_PKT_TXIGNORE().
  • NT_NetRxClose() - Close the stream when terminating. This will close the stream and release the NTPL assignment made on the hostbuffer.

Code

/*
*
* Copyright 2023 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/inline/inline_example.c
* @section inline_example_description Description
*
* INFO: This is an 3GA and 4GA inline example.
* A more advanced 4GA inline example can be found in examples/net/vlandemo/.
*
* This source file is an example of how to run in an inline
* scenario. The example will discard every 2nd packet.
*
* The following NTAPI functions are used:
* - @ref NT_Init()
* - @ref NT_InfoOpen()
* - @ref NT_InfoRead()
* - @ref NT_InfoClose()
* - @ref NT_ConfigOpen()
* - @ref NT_NetRxOpen()
* - @ref NT_NTPL()
* - @ref NT_NetRxGet()
* - @ref NT_NET_SET_PKT_TXIGNORE()
* - @ref NT_NET_GET_PKT_WIRE_LENGTH()
* - @ref NT_NET_GET_PKT_TIMESTAMP()
* - @ref NT_NetRxRelease()
* - @ref NT_NetRxClose()
* - @ref NT_ConfigClose()
* - @ref NT_ExplainError()
*
* @section inline_example_prerequisites Prerequisites
* An Napatech inline accelerator with at least one RX host buffer per port
* defined. Below is an example of a minimum ini-file. It will create
* two 32MB RX hostbuffers from NUMA node 0. The source and
* destination is determined by the filter. Use a traffic generator to
* apply traffic to one of the ports.
* @code
* [System]
* TimestampFormat = NATIVE
*
* [Adapter0]
* AdapterType = NT20E
* BusId = 00:0a:00.00
* HostBuffersRx = [2,32,0]
* @endcode
*
* @section inline_example_flow Program flow
* @{
* The following is required to run in an inline scenario:
* - \#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_InfoOpen() - Detect the adapter generation and if TX is
* available.
* - @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 doesn't 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 in the ASSIGN. The
* "color" 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(). On 4GA adapters and above,
* the Setup NTPL statement must be used to forward the packets from
* the stream to the output tx port (as opposed to the older form on
* 3Garch adapters where this could be done in the Assign statement).
* To indicate whether a packet should be dropped, we use the DYN3
* descriptors and indicate that the wirelength is used.
* - 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. @ref 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 @ref NT_NetRxRelease().
* - @ref NT_NetRxGet(), discard every 2nd packet and @ref NT_NetRxRelease() -
* Receive packets, discard every 2nd and release packets. The @ref
* PacketMacros are used to find the packet length ant the timestamp
* of the packet:
* - @ref NT_NET_GET_PKT_WIRE_LENGTH() - Get length of the packet to store.
* - @ref NT_NET_GET_PKT_TIMESTAMP() - The time the packet was delivered.
* - @ref NT_NET_SET_PKT_TXIGNORE() - Don't send the current packet (3GArch).
* - @ref NT_NET_GET_PKT_DESCR_PTR() - Get the packet descriptor header (4GArch).
* In the 4GArch, we set the wire length to 0 in order to discard the packet
* as opposed to using the NT_NET_SET_PKT_TXIGNORE().
* - @ref NT_NetRxClose() - Close the stream when terminating. This
* will close the stream and release the NTPL assignment made on the
* hostbuffer.
*
*<hr>
* @section inline_example_code Code
* @}
*/
// Include this in order to access the Napatech API
#include <nt.h>
#if defined(__linux__)
#include <signal.h>
#include <unistd.h>
#endif
#if defined(WIN32) || defined (WIN64)
#define snprintf(dst, ...) _snprintf_s((dst), _countof(dst), __VA_ARGS__)
#endif
#define PORT 0
static volatile int running = 1;
#if defined(__linux__)
static void catch_signal(int sig)
{
(void)sig;
running = 0;
}
#endif
int main(void)
{
int numPackets=0; // The number of packets received
int numBytes=0; // The number of bytes received
int numPacketsDiscard=0; // The number of packets discarded
int numBytesDiscard=0; // The number of bytes discarded
char tmpBuffer[20]; // Buffer to build filter string
char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer
int status; // Status variable
int isFpga4Garch=0; // Flag specifying whether this is 4GA adapter.
int isTxSupported=0; // Flag specifying whether Tx is supported on this adapter.
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().
NtInfoStream_t hInfo; // Handle to the Info stream for determining adapter type
NtInfo_t info; // Return data structure from NT_InfoRead() calls.
const char *ntplFilter; // Temporary variable storing the filter.
uint8_t adapterNo; // The adapter number for the port defined as PORT.
#if defined(__linux__)
signal(SIGINT, catch_signal); //<-- Catch keyboard QUIT (CTRL-\)
#endif
// 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;
}
if ((status = NT_InfoOpen(&hInfo, "inline_example")) != NT_SUCCESS) {
// Get the status code as text
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_InfoOpen() failed: %s\n", errorBuffer);
return -1;
}
// Detect adapter version and feature set
info.u.port_v9.portNo = PORT;
if ((status = NT_InfoRead(hInfo, &info)) != NT_SUCCESS) {
NT_InfoClose(hInfo);
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer);
return -1;
}
adapterNo = info.u.port_v9.data.adapterNo;
snprintf(info.u.property.path, sizeof(info.u.property.path), "Adapter%d.FpgaGeneration", adapterNo);
if ((status = NT_InfoRead(hInfo, &info)) != NT_SUCCESS) {
NT_InfoClose(hInfo);
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer);
return -1;
}
isFpga4Garch = info.u.property.data.u.i >= 4;
if (isFpga4Garch) {
memset(&info, 0, sizeof(NtInfo_t));
if ((status = NT_InfoRead(hInfo, &info)) != NT_SUCCESS) {
NT_InfoClose(hInfo);
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer); return -1;
}
// 4GA adapters with ANL9 feature level support native TX with inline capabilities
// NOTE: 4GA adapters with ANL9 feature level doesn't support
// NT_NET_SET_PKT_DESCR_TYPE_EXT7 descriptor type
isTxSupported=1;
}
} else {
isTxSupported=1;
}
if ((status = NT_InfoClose(hInfo)) != NT_SUCCESS) {
NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
fprintf(stderr, "NT_InfoClose() failed: %s\n", errorBuffer);
return -1;
}
// Bail out early because inline isn't possible without Tx.
if (!isTxSupported) {
fprintf(stderr, "error: Tx is not supported on this adapter.\n");
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;
}
// Assign traffic to stream ID 1 and mask all traffic matching the assign statement color=7.
if (isFpga4Garch) {
// Setup traffic to stream ID(=1) and Tx port = 0, and use wire length to discard packets
if ((status = NT_NTPL(hCfgStream, "Setup[TxDescriptor=Dyn;TxPorts=0;UseWL=True] = StreamId == 1",
&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;
}
ntplFilter = "Assign[streamid=1;color=7;Descriptor=Dyn3] = All";
} else { // 3Garch - for older FPGAs and adapters
ntplFilter = "Assign[streamid=1;color=7;txport=1] = All";
}
if ((status = NT_NTPL(hCfgStream, ntplFilter, &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;
}
// Get a stream handle with the stream ID. NT_NET_INTERFACE_PACKET specify that we will receive data in a packet based matter.
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;
}
}
// Forward all packages and skip every second packet until signal received
while (running) {
static int disc=0;
// Check if the packet should be discarded
if (disc) {
// Discard the current packet
if (isFpga4Garch) {
// On 4garch, we use wirelength = 0 to discard packet (see vlandemo for other methods)
NtDyn3Descr_t *pDyn3 = (NtDyn3Descr_t *)NT_NET_GET_PKT_DESCR_PTR(hNetBuf);
pDyn3->wireLength = 0;
} else { // 3Garch
NT_NET_SET_PKT_TXIGNORE(hNetBuf, 1);
}
// Increment the number of packets and bytes discarded.
numBytesDiscard+=NT_NET_GET_PKT_WIRE_LENGTH(hNetBuf);
numPacketsDiscard++;
printf("Discarded: %016llx - %d bytes\n", (unsigned long long)NT_NET_GET_PKT_TIMESTAMP(hNetBuf), NT_NET_GET_PKT_WIRE_LENGTH(hNetBuf));
} else {
// Increment the number of packets and bytes forwarded.
numBytes+=NT_NET_GET_PKT_WIRE_LENGTH(hNetBuf);
numPackets++;
printf("Forwarded: %016llx - %d bytes\n", (unsigned long long)NT_NET_GET_PKT_TIMESTAMP(hNetBuf), NT_NET_GET_PKT_WIRE_LENGTH(hNetBuf));
}
// Toggle the discard bit
disc ^= 1;
// 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_NetRxRelease() failed: %s\n", errorBuffer);
return -1;
}
// Get the next packet
while (running) {
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
}
}
// Open a config stream to delete a filter.
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;
}
// Delete the filter
snprintf(tmpBuffer, sizeof(tmpBuffer), "delete=%d", ntplInfo.ntplId);
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: Discarded %d packets out of %d, %d bytes out of %d\n",
numPacketsDiscard, numPackets+numPacketsDiscard, numBytes, numBytes+numBytesDiscard);
return 0;
}