API Reference Manual  1.45.0
odp_l2fwd_simple.c

Minimal L2 forwarding example application

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) 2016-2018 Linaro Limited
* Copyright (c) 2024 Nokia
*/
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <signal.h>
#include <odp_api.h>
#include <odp/helper/odph_api.h>
#define POOL_NUM_PKT 8192
#define POOL_SEG_LEN 1856
#define MAX_PKT_BURST 32
#define MAX_WORKERS 1
typedef struct {
odp_pktio_t if0, if1;
odp_pktin_queue_t if0in, if1in;
odp_pktout_queue_t if0out, if1out;
odph_ethaddr_t src, dst;
odp_shm_t shm;
odp_atomic_u32_t exit_thr;
int wait_sec;
} global_data_t;
static global_data_t *global;
static void sig_handler(int signo ODP_UNUSED)
{
printf("sig_handler!\n");
if (global == NULL)
return;
odp_atomic_store_u32(&global->exit_thr, 1);
}
static odp_pktio_t create_pktio(const char *name, odp_pool_t pool,
{
odp_pktio_param_t pktio_param;
odp_pktin_queue_param_t in_queue_param;
odp_pktout_queue_param_t out_queue_param;
odp_pktio_t pktio;
odp_pktio_param_init(&pktio_param);
pktio = odp_pktio_open(name, pool, &pktio_param);
if (pktio == ODP_PKTIO_INVALID) {
printf("Failed to open %s\n", name);
exit(1);
}
odp_pktio_config(pktio, &config);
odp_pktin_queue_param_init(&in_queue_param);
odp_pktout_queue_param_init(&out_queue_param);
in_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
if (odp_pktin_queue_config(pktio, &in_queue_param)) {
printf("Failed to config input queue for %s\n", name);
exit(1);
}
out_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
if (odp_pktout_queue_config(pktio, &out_queue_param)) {
printf("Failed to config output queue for %s\n", name);
exit(1);
}
if (odp_pktin_queue(pktio, pktin, 1) != 1) {
printf("pktin queue query failed for %s\n", name);
exit(1);
}
if (odp_pktout_queue(pktio, pktout, 1) != 1) {
printf("pktout queue query failed for %s\n", name);
exit(1);
}
return pktio;
}
static int run_worker(void *arg ODP_UNUSED)
{
odp_packet_t pkt_tbl[MAX_PKT_BURST];
int pkts, sent, tx_drops, i;
uint64_t wait_time = odp_pktin_wait_time(ODP_TIME_SEC_IN_NS);
if (odp_pktio_start(global->if0)) {
printf("unable to start input interface\n");
return -1;
}
printf("started input interface\n");
if (odp_pktio_start(global->if1)) {
printf("unable to start output interface\n");
return -1;
}
printf("started output interface\n");
printf("started all\n");
while (!odp_atomic_load_u32(&global->exit_thr)) {
pkts = odp_pktin_recv_tmo(global->if0in, pkt_tbl, MAX_PKT_BURST,
wait_time);
if (odp_unlikely(pkts <= 0)) {
if (global->wait_sec > 0)
if (!(--global->wait_sec))
break;
continue;
}
for (i = 0; i < pkts; i++) {
odp_packet_t pkt = pkt_tbl[i];
odph_ethhdr_t *eth;
printf("warning: packet has no eth header\n");
return 0;
}
eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
eth->src = global->src;
eth->dst = global->dst;
}
sent = odp_pktout_send(global->if1out, pkt_tbl, pkts);
if (sent < 0)
sent = 0;
tx_drops = pkts - sent;
if (odp_unlikely(tx_drops))
odp_packet_free_multi(&pkt_tbl[sent], tx_drops);
}
return 0;
}
int main(int argc, char **argv)
{
odph_helper_options_t helper_options;
odp_pool_t pool;
odp_cpumask_t cpumask;
odph_thread_t thd[MAX_WORKERS];
odp_instance_t instance;
odp_init_t init_param;
odph_thread_common_param_t thr_common;
odph_thread_param_t thr_param;
odph_ethaddr_t correct_src;
uint32_t mtu1, mtu2;
odp_shm_t shm;
odph_thread_join_result_t res[MAX_WORKERS];
/* Let helper collect its own arguments (e.g. --odph_proc) */
argc = odph_parse_options(argc, argv);
if (odph_options(&helper_options)) {
printf("Error: reading ODP helper options failed.\n");
exit(EXIT_FAILURE);
}
odp_init_param_init(&init_param);
init_param.mem_model = helper_options.mem_model;
if (odp_init_global(&instance, &init_param, NULL)) {
printf("Error: ODP global init failed.\n");
exit(1);
}
printf("Error: ODP local init failed.\n");
exit(1);
}
/* Reserve memory for args from shared mem */
shm = odp_shm_reserve("_appl_global_data", sizeof(global_data_t),
ODP_CACHE_LINE_SIZE, 0);
if (shm == ODP_SHM_INVALID) {
printf("Error: shared mem reserve failed.\n");
exit(EXIT_FAILURE);
}
global = odp_shm_addr(shm);
if (global == NULL) {
printf("Error: shared mem alloc failed.\n");
exit(EXIT_FAILURE);
}
memset(global, 0, sizeof(global_data_t));
odp_atomic_init_u32(&global->exit_thr, 0);
global->shm = shm;
if (argc > 7 ||
odph_eth_addr_parse(&global->dst, argv[3]) != 0 ||
odph_eth_addr_parse(&global->src, argv[4]) != 0) {
printf("Usage: odp_l2fwd_simple eth0 eth1 01:02:03:04:05:06"
" 07:08:09:0a:0b:0c [-t sec]\n");
printf("Where eth0 and eth1 are the used interfaces"
" (must have 2 of them)\n");
printf("And the hexadecimal numbers are destination MAC address"
" and source MAC address\n");
exit(1);
}
if (argc == 7 && !strncmp(argv[5], "-t", 2))
global->wait_sec = atoi(argv[6]);
if (global->wait_sec)
printf("running test for %d sec\n", global->wait_sec);
/* Create packet pool */
params.pkt.seg_len = POOL_SEG_LEN;
params.pkt.len = POOL_SEG_LEN;
params.pkt.num = POOL_NUM_PKT;
pool = odp_pool_create("packet pool", &params);
if (pool == ODP_POOL_INVALID) {
printf("Error: packet pool create failed.\n");
exit(1);
}
global->if0 = create_pktio(argv[1], pool, &global->if0in,
&global->if0out);
global->if1 = create_pktio(argv[2], pool, &global->if1in,
&global->if1out);
/* Do some operations to increase code coverage in tests */
if (odp_pktio_mac_addr(global->if0, &correct_src, sizeof(correct_src))
!= sizeof(correct_src))
printf("Warning: can't get MAC address\n");
else if (memcmp(&correct_src, &global->src, sizeof(correct_src)) != 0)
printf("Warning: src MAC invalid\n");
odp_pktio_promisc_mode_set(global->if0, true);
odp_pktio_promisc_mode_set(global->if1, true);
(void)odp_pktio_promisc_mode(global->if0);
(void)odp_pktio_promisc_mode(global->if1);
mtu1 = odp_pktin_maxlen(global->if0);
mtu2 = odp_pktout_maxlen(global->if1);
if (mtu1 && mtu2 && mtu1 > mtu2)
printf("Warning: input MTU bigger than output MTU\n");
odp_cpumask_default_worker(&cpumask, MAX_WORKERS);
odph_thread_common_param_init(&thr_common);
odph_thread_param_init(&thr_param);
thr_param.start = run_worker;
thr_param.thr_type = ODP_THREAD_WORKER;
thr_common.instance = instance;
thr_common.cpumask = &cpumask;
thr_common.share_param = 1;
signal(SIGINT, sig_handler);
if (odph_thread_create(thd, &thr_common, &thr_param, MAX_WORKERS) !=
MAX_WORKERS) {
printf("Error: failed to create threads\n");
exit(EXIT_FAILURE);
}
if (odph_thread_join_result(thd, res, MAX_WORKERS) != MAX_WORKERS) {
printf("Error: failed to join threads\n");
exit(EXIT_FAILURE);
}
for (int i = 0; i < MAX_WORKERS; i++) {
if (res[i].is_sig || res[i].ret != 0) {
printf("Error: thread failure%s: %d\n", res[i].is_sig ?
" (signaled)" : "", res[i].ret);
exit(EXIT_FAILURE);
}
}
if (odp_pktio_stop(global->if0) || odp_pktio_close(global->if0)) {
printf("Error: failed to close interface %s\n", argv[1]);
exit(EXIT_FAILURE);
}
if (odp_pktio_stop(global->if1) || odp_pktio_close(global->if1)) {
printf("Error: failed to close interface %s\n", argv[2]);
exit(EXIT_FAILURE);
}
if (odp_pool_destroy(pool)) {
printf("Error: pool destroy\n");
exit(EXIT_FAILURE);
}
global = NULL;
if (odp_shm_free(shm)) {
printf("Error: shm free global data\n");
exit(EXIT_FAILURE);
}
if (odp_term_local()) {
printf("Error: term local\n");
exit(EXIT_FAILURE);
}
if (odp_term_global(instance)) {
printf("Error: term global\n");
exit(EXIT_FAILURE);
}
return 0;
}
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
uint32_t odp_atomic_load_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable.
void odp_atomic_store_u32(odp_atomic_u32_t *atom, uint32_t val)
Store value to atomic uint32 variable.
void odp_mb_full(void)
Full memory barrier.
#define odp_unlikely(x)
Branch unlikely taken.
Definition: spec/hints.h:64
#define ODP_UNUSED
Intentionally unused variables of functions.
Definition: spec/hints.h:54
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
Thread local ODP initialization.
int odp_init_global(odp_instance_t *instance, const odp_init_t *params, const odp_platform_init_t *platform_params)
Global ODP initialization.
int odp_term_local(void)
Thread local ODP termination.
int odp_term_global(odp_instance_t instance)
Global ODP termination.
uint64_t odp_instance_t
ODP instance ID.
int odp_pktio_mac_addr(odp_pktio_t pktio, void *mac_addr, int size)
Get the default MAC address of a packet IO interface.
void odp_pktin_queue_param_init(odp_pktin_queue_param_t *param)
Initialize packet input queue parameters.
void odp_pktio_param_init(odp_pktio_param_t *param)
Initialize pktio params.
int odp_pktio_promisc_mode(odp_pktio_t pktio)
Determine if promiscuous mode is enabled for a packet IO interface.
int odp_pktio_close(odp_pktio_t pktio)
Close a packet IO interface.
uint32_t odp_pktin_maxlen(odp_pktio_t pktio)
Maximum frame length at packet input.
uint32_t odp_pktout_maxlen(odp_pktio_t pktio)
Maximum frame length at packet output.
int odp_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num)
Direct packet output queues.
int odp_pktio_promisc_mode_set(odp_pktio_t pktio, odp_bool_t enable)
Set promiscuous mode.
void odp_pktio_config_init(odp_pktio_config_t *config)
Initialize packet IO configuration options.
odp_pktio_t odp_pktio_open(const char *name, odp_pool_t pool, const odp_pktio_param_t *param)
Open a packet IO interface.
int odp_pktio_config(odp_pktio_t pktio, const odp_pktio_config_t *config)
Configure packet IO interface options.
int odp_pktio_start(odp_pktio_t pktio)
Start packet receive and transmit.
#define ODP_PKTIO_INVALID
Invalid packet IO handle.
int odp_pktin_queue(odp_pktio_t pktio, odp_pktin_queue_t queues[], int num)
Direct packet input queues.
void odp_pktout_queue_param_init(odp_pktout_queue_param_t *param)
Initialize packet output queue parameters.
uint64_t odp_pktin_wait_time(uint64_t nsec)
Packet input wait time.
int odp_pktio_stop(odp_pktio_t pktio)
Stop packet receive and transmit.
int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[], int num)
Send packets directly to an interface output queue.
int odp_pktin_recv_tmo(odp_pktin_queue_t queue, odp_packet_t packets[], int num, uint64_t wait)
Receive packets directly from an interface input queue with timeout.
int odp_pktin_queue_config(odp_pktio_t pktio, const odp_pktin_queue_param_t *param)
Configure packet input queues.
int odp_pktout_queue_config(odp_pktio_t pktio, const odp_pktout_queue_param_t *param)
Configure packet output queues.
@ ODP_PKTIO_OP_MT_UNSAFE
Not multithread safe operation.
int odp_packet_has_eth(odp_packet_t pkt)
Check for Ethernet header.
void * odp_packet_l2_ptr(odp_packet_t pkt, uint32_t *len)
Layer 2 start pointer.
void odp_packet_free_multi(const odp_packet_t pkt[], int num)
Free multiple packets.
@ ODP_PROTO_LAYER_L2
Layer L2 protocols (Ethernet, VLAN, etc)
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_PACKET
Packet pool.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
#define ODP_SHM_INVALID
Invalid shared memory block.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
Packet input queue parameters.
odp_pktio_op_mode_t op_mode
Operation mode.
Packet IO configuration options.
odp_pktio_parser_config_t parser
Packet input parser configuration.
Packet IO parameters.
odp_proto_layer_t layer
Protocol parsing level in packet input.
Packet output queue parameters.
odp_pktio_op_mode_t op_mode
Operation mode.
Pool parameters.
uint32_t num
Number of buffers in the pool.
odp_pool_type_t type
Pool type.
struct odp_pool_param_t::@123 pkt
Parameters for packet pools.
uint32_t len
Minimum length of 'num' packets.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...