API Reference Manual  1.45.0
odp_classifier.c

Classifier API example application

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) 2015-2018 Linaro Limited
* Copyright (c) 2019-2022 Nokia
* Copyright (c) 2020 Marvell
*/
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <inttypes.h>
#include <signal.h>
#include <odp_api.h>
#include <odp/helper/odph_api.h>
#include <strings.h>
#include <errno.h>
#include <stdio.h>
#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
#define MAX_PKT_BURST 64
#define DEF_PKT_BURST 32
#define SHM_PKT_POOL_SIZE 10000
#define SHM_PKT_POOL_BUF_SIZE 1856
#define MAX_PMR_COUNT 32
#define DISPLAY_STRING_LEN 32
#define MAX_VAL_SIZE 16
#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
strrchr((file_name), '/') + 1 : (file_name))
typedef struct {
odp_queue_t queue;
odp_pool_t pool;
odp_cos_t cos;
odp_pmr_t pmr;
odp_atomic_u64_t queue_pkt_count;
odp_atomic_u64_t pool_pkt_count;
char cos_name[ODP_COS_NAME_LEN];
char src_cos_name[ODP_COS_NAME_LEN];
struct {
uint8_t value_be[MAX_VAL_SIZE];
uint8_t mask_be[MAX_VAL_SIZE];
uint32_t val_sz;
uint32_t offset;
} rule;
char value[DISPLAY_STRING_LEN];
char mask[DISPLAY_STRING_LEN];
int has_src_cos;
} global_statistics;
typedef struct {
char cos_name[ODP_COS_NAME_LEN];
uint64_t count;
} ci_pass_counters;
typedef struct {
odp_pktout_queue_t pktout[MAX_WORKERS];
int num_pktout;
global_statistics stats[MAX_PMR_COUNT];
ci_pass_counters ci_pass_rules[MAX_PMR_COUNT];
int policy_count;
int num_ci_pass_rules;
int appl_mode;
odp_atomic_u64_t total_packets;
unsigned int cpu_count;
uint32_t time;
char *if_name;
int shutdown;
int shutdown_sig;
int verbose;
int promisc_mode;
int classifier_enable;
int parse_layer;
int cos_pools;
int pool_size;
int burst_size;
} appl_args_t;
enum packet_mode {
APPL_MODE_DROP,
APPL_MODE_REPLY
};
static appl_args_t *appl_args_gbl;
static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
static void swap_pkt_addrs(odp_packet_t pkt_tbl[], unsigned len);
static int parse_args(int argc, char *argv[], appl_args_t *appl_args);
static void print_info(char *progname, appl_args_t *appl_args);
static void usage(void);
static inline int check_ci_pass_count(appl_args_t *args)
{
int i, j;
uint64_t count;
if (args->num_ci_pass_rules == 0)
return 0;
for (i = 0; i < args->num_ci_pass_rules; i++) {
for (j = 0; j < args->policy_count; j++) {
if (!strcmp(args->stats[j].cos_name,
args->ci_pass_rules[i].cos_name)) {
count = odp_atomic_load_u64(&args->stats[i].queue_pkt_count);
if (args->ci_pass_rules[i].count > count) {
ODPH_ERR("Error: Cos = %s, expected packets = %" PRIu64 ","
"received packet = %" PRIu64 "\n",
args->stats[j].cos_name,
args->ci_pass_rules[i].count, count);
return -1;
}
break;
}
}
if (j == args->policy_count) {
ODPH_ERR("Error: invalid Cos:%s specified for CI pass count\n",
args->ci_pass_rules[i].cos_name);
return -1;
}
}
return 0;
}
static inline void print_cls_statistics(appl_args_t *args)
{
int i;
uint32_t timeout;
int infinite = 0;
printf("\n");
for (i = 0; i < 40; i++)
printf("-");
printf("\n");
/* print statistics */
printf("CLASSIFIER EXAMPLE STATISTICS\n");
for (i = 0; i < 40; i++)
printf("-");
printf("\n");
printf("CONFIGURATION\n");
printf("\n");
printf("COS\tVALUE\t\tMASK\n");
for (i = 0; i < 40; i++)
printf("-");
printf("\n");
for (i = 0; i < args->policy_count - 1; i++) {
printf("%s\t", args->stats[i].cos_name);
printf("%s\t", args->stats[i].value);
printf("%s\n", args->stats[i].mask);
}
printf("\n");
printf("RECEIVED PACKETS\n");
for (i = 0; i < 40; i++)
printf("-");
printf("\n");
for (i = 0; i < args->policy_count; i++)
printf("%-12s |", args->stats[i].cos_name);
printf("%-6s %-6s", "Total", "Mpps");
printf("\n");
for (i = 0; i < args->policy_count; i++)
printf("%-6s %-6s|", "queue", "pool");
printf("\n");
timeout = args->time;
/* Incase if default value is given for timeout
run the loop infinitely */
if (timeout == 0)
infinite = 1;
uint64_t total_packets, last_total_packets = 0;
odp_time_t start = odp_time_local(), end;
float mpps;
for (; timeout > 0 || infinite; timeout--) {
sleep(1);
for (i = 0; i < args->policy_count; i++) {
printf("%-6" PRIu64 " ",
odp_atomic_load_u64(&args->stats[i]
.queue_pkt_count));
printf("%-6" PRIu64 "|",
odp_atomic_load_u64(&args->stats[i]
.pool_pkt_count));
}
end = odp_time_local();
total_packets = odp_atomic_load_u64(&args->total_packets);
mpps = (total_packets - last_total_packets) /
(odp_time_diff_ns(end, start) / 1000.0);
printf("%-6" PRIu64 " %-6.3f\n", total_packets, mpps);
last_total_packets = total_packets;
start = end;
if (args->shutdown_sig)
break;
}
printf("\n");
}
static int parse_custom(const char *str, uint8_t *buf_be, int max_size)
{
int i, len;
/* hex string without 0x prefix */
len = strlen(str);
if (len > 2 * max_size)
return -1;
for (i = 0; i < len; i += 2)
if (sscanf(&str[i], "%2" SCNx8, &buf_be[i / 2]) != 1)
return -1;
return len / 2;
}
static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool)
{
odp_pktio_t pktio;
odp_pktio_param_t pktio_param;
odp_pktout_queue_param_t pktout_queue_param;
int num_tx;
odp_pktio_param_init(&pktio_param);
/* Open a packet IO instance */
pktio = odp_pktio_open(dev, pool, &pktio_param);
if (pktio == ODP_PKTIO_INVALID) {
ODPH_ERR("pktio create failed for %s\n", dev);
exit(EXIT_FAILURE);
}
if (odp_pktio_capability(pktio, &capa)) {
ODPH_ERR("pktio capability failed for %s\n", dev);
exit(EXIT_FAILURE);
}
pktin_param.classifier_enable = appl_args_gbl->classifier_enable;
if (odp_pktin_queue_config(pktio, &pktin_param)) {
ODPH_ERR("pktin queue config failed for %s\n", dev);
exit(EXIT_FAILURE);
}
num_tx = appl_args_gbl->cpu_count;
if (num_tx > (int)capa.max_output_queues) {
printf("Sharing %i output queues between %i workers\n",
capa.max_output_queues, num_tx);
num_tx = capa.max_output_queues;
}
appl_args_gbl->num_pktout = num_tx;
odp_pktout_queue_param_init(&pktout_queue_param);
pktout_queue_param.num_queues = num_tx;
if (odp_pktout_queue_config(pktio, &pktout_queue_param)) {
ODPH_ERR("pktout queue config failed for %s\n", dev);
exit(EXIT_FAILURE);
}
if (odp_pktout_queue(pktio, appl_args_gbl->pktout, num_tx) != num_tx) {
ODPH_ERR("Pktout queue query failed: %s\n", dev);
exit(EXIT_FAILURE);
}
if (appl_args_gbl->promisc_mode && odp_pktio_promisc_mode(pktio) != 1) {
if (!capa.set_op.op.promisc_mode) {
ODPH_ERR("enabling promisc mode not supported %s\n", dev);
exit(EXIT_FAILURE);
}
if (odp_pktio_promisc_mode_set(pktio, true)) {
ODPH_ERR("failed to enable promisc mode for %s\n", dev);
exit(EXIT_FAILURE);
}
}
printf("created pktio:%" PRIu64 ", dev:%s", odp_pktio_to_u64(pktio), dev);
odph_ethaddr_t mac;
if (odp_pktio_mac_addr(pktio, &mac, sizeof(mac)) == sizeof(mac)) {
printf(", mac");
for (int c = 0; c < (int)sizeof(mac); c++)
printf(":%02x", mac.addr[c]);
}
printf("\n");
cfg.parser.layer = appl_args_gbl->parse_layer;
if (odp_pktio_config(pktio, &cfg)) {
ODPH_ERR("failed to configure pktio %s\n", dev);
exit(EXIT_FAILURE);
}
return pktio;
}
static int pktio_receive_thread(void *arg)
{
int thr;
odp_packet_t pkt[MAX_PKT_BURST];
odp_pool_t pool;
odp_event_t ev[MAX_PKT_BURST];
odp_queue_t queue;
int i, j, num, dropped, sent;
global_statistics *stats;
unsigned long err_cnt = 0;
thr = odp_thread_id();
appl_args_t *appl = (appl_args_t *)arg;
uint64_t wait_time = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
odp_pktout_queue_t pktout = appl_args_gbl->pktout[thr % appl_args_gbl->num_pktout];
/* Loop packets */
for (;;) {
if (appl->shutdown)
break;
/* Use schedule to get buf from any input queue */
num = odp_schedule_multi(&queue, wait_time, ev, appl_args_gbl->burst_size);
/* Loop back to receive packets incase of invalid event */
if (odp_unlikely(!num))
continue;
if (odp_unlikely(appl->verbose)) {
for (j = 0; j < num; j++) {
uint32_t len = odp_packet_len(pkt[j]);
if (odp_queue_info(queue, &info) == 0)
printf("Queue: %s\n", info.name);
if (len > 96)
len = 96;
odp_packet_print_data(pkt[j], 0, len);
}
}
/* Total packets received */
odp_atomic_add_u64(&appl->total_packets, num);
/* Drop packets with errors */
dropped = drop_err_pkts(pkt, num);
if (odp_unlikely(dropped)) {
num -= dropped;
err_cnt += dropped;
ODPH_ERR("Drop frame - err_cnt:%lu\n", err_cnt);
}
for (j = 0; j < num; j++) {
pool = odp_packet_pool(pkt[j]);
for (i = 0; i < MAX_PMR_COUNT; i++) {
stats = &appl->stats[i];
if (queue == stats->queue)
odp_atomic_inc_u64(&stats->queue_pkt_count);
if (pool == stats->pool)
odp_atomic_inc_u64(&stats->pool_pkt_count);
}
}
if (appl->appl_mode == APPL_MODE_DROP) {
continue;
}
/* Swap Eth MACs and possibly IP-addrs before sending back */
swap_pkt_addrs(pkt, num);
sent = odp_pktout_send(pktout, pkt, num);
sent = sent < 0 ? 0 : sent;
if (sent != num) {
ODPH_ERR(" [%i] Packet send failed\n", thr);
odp_packet_free_multi(pkt + sent, num - sent);
}
}
return 0;
}
static odp_pool_t pool_create(const char *name)
{
odp_pool_param_t pool_params;
if (!appl_args_gbl->cos_pools && pool != ODP_POOL_INVALID)
return pool;
odp_pool_param_init(&pool_params);
pool_params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
pool_params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
pool_params.pkt.num = appl_args_gbl->pool_size;
pool_params.type = ODP_POOL_PACKET;
pool = odp_pool_create(name, &pool_params);
if (pool == ODP_POOL_INVALID) {
ODPH_ERR("Error: failed to create pool %s\n", name);
exit(EXIT_FAILURE);
}
return pool;
}
static odp_cos_t configure_default_cos(odp_pktio_t pktio, appl_args_t *args)
{
const char *queue_name = "DefaultQueue";
const char *pool_name = "DefaultPool";
const char *cos_name = "DefaultCos";
odp_queue_t queue_default;
odp_pool_t pool_default;
odp_cos_t cos_default;
global_statistics *stats = args->stats;
queue_default = odp_queue_create(queue_name, &qparam);
if (queue_default == ODP_QUEUE_INVALID) {
ODPH_ERR("Error: default queue create failed\n");
exit(EXIT_FAILURE);
}
pool_default = pool_create(pool_name);
cls_param.pool = pool_default;
cls_param.queue = queue_default;
cos_default = odp_cls_cos_create(cos_name, &cls_param);
if (cos_default == ODP_COS_INVALID) {
ODPH_ERR("Error: default cos create failed\n");
exit(EXIT_FAILURE);
}
if (0 > odp_pktio_default_cos_set(pktio, cos_default)) {
ODPH_ERR("odp_pktio_default_cos_set failed\n");
exit(EXIT_FAILURE);
}
stats[args->policy_count].cos = cos_default;
/* add default queue to global stats */
stats[args->policy_count].queue = queue_default;
if (appl_args_gbl->cos_pools)
stats[args->policy_count].pool = pool_default;
snprintf(stats[args->policy_count].cos_name,
sizeof(stats[args->policy_count].cos_name),
"%s", cos_name);
odp_atomic_init_u64(&stats[args->policy_count].queue_pkt_count, 0);
odp_atomic_init_u64(&stats[args->policy_count].pool_pkt_count, 0);
args->policy_count++;
return cos_default;
}
static int find_cos(appl_args_t *args, const char *name, odp_cos_t *cos)
{
global_statistics *stats;
int i;
for (i = 0; i < args->policy_count - 1; i++) {
stats = &args->stats[i];
if (strcmp(stats->cos_name, name) == 0) {
*cos = stats->cos;
return 0;
}
}
return -1;
}
static void configure_cos(odp_cos_t default_cos, appl_args_t *args)
{
char cos_name[ODP_COS_NAME_LEN];
char pool_name[ODP_POOL_NAME_LEN];
const char *queue_name;
int i;
global_statistics *stats;
for (i = 0; i < args->policy_count - 1; i++) {
stats = &args->stats[i];
queue_name = args->stats[i].cos_name;
stats->queue = odp_queue_create(queue_name, &qparam);
if (ODP_QUEUE_INVALID == stats->queue) {
ODPH_ERR("odp_queue_create failed\n");
exit(EXIT_FAILURE);
}
snprintf(pool_name, sizeof(pool_name), "%sPool%d",
args->stats[i].cos_name, i);
snprintf(cos_name, sizeof(cos_name), "CoS%s",
stats->cos_name);
cls_param.pool = pool_create(pool_name);
if (appl_args_gbl->cos_pools)
stats->pool = cls_param.pool;
cls_param.queue = stats->queue;
stats->cos = odp_cls_cos_create(cos_name, &cls_param);
odp_atomic_init_u64(&stats->queue_pkt_count, 0);
odp_atomic_init_u64(&stats->pool_pkt_count, 0);
}
for (i = 0; i < args->policy_count - 1; i++) {
odp_pmr_param_t pmr_param;
odp_cos_t src_cos = default_cos;
stats = &args->stats[i];
if (stats->has_src_cos) {
if (find_cos(args, stats->src_cos_name, &src_cos)) {
ODPH_ERR("find_cos failed\n");
exit(EXIT_FAILURE);
}
}
pmr_param.term = stats->rule.term;
pmr_param.match.value = stats->rule.value_be;
pmr_param.match.mask = stats->rule.mask_be;
pmr_param.val_sz = stats->rule.val_sz;
pmr_param.offset = stats->rule.offset;
stats->pmr = odp_cls_pmr_create(&pmr_param, 1, src_cos,
stats->cos);
if (stats->pmr == ODP_PMR_INVALID) {
ODPH_ERR("odp_pktio_pmr_cos failed\n");
exit(EXIT_FAILURE);
}
}
}
static void sig_handler(int signo)
{
(void)signo;
if (appl_args_gbl == NULL)
return;
appl_args_gbl->shutdown_sig = 1;
}
int main(int argc, char *argv[])
{
odph_helper_options_t helper_options;
odph_thread_t thread_tbl[MAX_WORKERS];
odp_pool_t pool;
int num_workers;
int i;
odp_cpumask_t cpumask;
char cpumaskstr[ODP_CPUMASK_STR_SIZE];
odp_pktio_t pktio;
appl_args_t *args;
odp_cos_t default_cos;
odp_shm_t shm;
int ret;
odp_instance_t instance;
odp_init_t init_param;
odph_thread_common_param_t thr_common;
odph_thread_param_t thr_param;
signal(SIGINT, sig_handler);
/* Let helper collect its own arguments (e.g. --odph_proc) */
argc = odph_parse_options(argc, argv);
if (odph_options(&helper_options)) {
ODPH_ERR("Error: reading ODP helper options failed\n");
exit(EXIT_FAILURE);
}
odp_init_param_init(&init_param);
init_param.mem_model = helper_options.mem_model;
/* Init ODP before calling anything else */
if (odp_init_global(&instance, &init_param, NULL)) {
ODPH_ERR("Error: ODP global init failed\n");
exit(EXIT_FAILURE);
}
/* Init this thread */
ODPH_ERR("Error: ODP local init failed\n");
exit(EXIT_FAILURE);
}
/* Reserve memory for args from shared mem */
shm = odp_shm_reserve("cls_shm_args", sizeof(appl_args_t),
ODP_CACHE_LINE_SIZE, 0);
if (shm == ODP_SHM_INVALID) {
ODPH_ERR("Error: shared mem reserve failed\n");
exit(EXIT_FAILURE);
}
args = odp_shm_addr(shm);
if (args == NULL) {
ODPH_ERR("Error: shared mem alloc failed\n");
exit(EXIT_FAILURE);
}
appl_args_gbl = args;
memset(args, 0, sizeof(*args));
/* Parse and store the application arguments */
if (parse_args(argc, argv, args))
goto args_error;
/* Print both system and application information */
print_info(NO_PATH(argv[0]), args);
num_workers = MAX_WORKERS;
if (args->cpu_count && args->cpu_count < MAX_WORKERS)
num_workers = args->cpu_count;
/* Get default worker cpumask */
num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
(void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
printf("num worker threads: %i\n", num_workers);
printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
printf("cpu mask: %s\n", cpumaskstr);
/* Create packet pool */
pool = pool_create("packet_pool");
/* Configure scheduler */
/* odp_pool_print(pool); */
odp_atomic_init_u64(&args->total_packets, 0);
/* create pktio per interface */
pktio = create_pktio(args->if_name, pool);
/* configure default Cos */
default_cos = configure_default_cos(pktio, args);
configure_cos(default_cos, args);
printf("\n");
if (odp_pktio_start(pktio)) {
ODPH_ERR("Error: unable to start pktio\n");
exit(EXIT_FAILURE);
}
/* Create and init worker threads */
memset(thread_tbl, 0, sizeof(thread_tbl));
odph_thread_common_param_init(&thr_common);
odph_thread_param_init(&thr_param);
thr_param.start = pktio_receive_thread;
thr_param.arg = args;
thr_param.thr_type = ODP_THREAD_WORKER;
thr_common.instance = instance;
thr_common.cpumask = &cpumask;
thr_common.share_param = 1;
odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
if (args->verbose == 0) {
print_cls_statistics(args);
} else {
int timeout = args->time;
for (i = 0; timeout == 0 || i < timeout; i++) {
if (args->shutdown_sig)
break;
sleep(1);
}
}
args->shutdown = 1;
odph_thread_join(thread_tbl, num_workers);
if (check_ci_pass_count(args)) {
ODPH_ERR("Error: Packet count verification failed\n");
exit(EXIT_FAILURE);
}
for (i = 0; i < args->policy_count; i++) {
if ((i != args->policy_count - 1) &&
odp_cls_pmr_destroy(args->stats[i].pmr))
ODPH_ERR("err: odp_cls_pmr_destroy for %d\n", i);
if (odp_cos_destroy(args->stats[i].cos))
ODPH_ERR("err: odp_cos_destroy for %d\n", i);
if (odp_queue_destroy(args->stats[i].queue))
ODPH_ERR("err: odp_queue_destroy for %d\n", i);
if (args->cos_pools && odp_pool_destroy(args->stats[i].pool))
ODPH_ERR("err: odp_pool_destroy for %d\n", i);
}
if (odp_pktio_close(pktio))
ODPH_ERR("err: close pktio error\n");
if (odp_pool_destroy(pool))
ODPH_ERR("err: odp_pool_destroy error\n");
free(args->if_name);
args_error:
ret = odp_term_local();
if (ret)
ODPH_ERR("odp_term_local error %d\n", ret);
ret = odp_term_global(instance);
if (ret)
ODPH_ERR("odp_term_global error %d\n", ret);
printf("Exit\n\n");
return ret;
}
static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
{
unsigned i, j;
int dropped = 0;
for (i = 0, j = 0; i < len; ++i) {
pkt = pkt_tbl[i];
odp_packet_free(pkt); /* Drop */
dropped++;
} else if (odp_unlikely(i != j++)) {
pkt_tbl[j-1] = pkt;
}
}
return dropped;
}
static void swap_pkt_addrs(odp_packet_t pkt_tbl[], unsigned len)
{
odph_ethhdr_t *eth;
odph_ethaddr_t tmp_addr;
odph_ipv4hdr_t *ip;
odp_u32be_t ip_tmp_addr; /* tmp ip addr */
unsigned i;
for (i = 0; i < len; ++i) {
pkt = pkt_tbl[i];
if (odp_packet_has_eth(pkt)) {
eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
tmp_addr = eth->dst;
eth->dst = eth->src;
eth->src = tmp_addr;
if (odp_packet_has_ipv4(pkt)) {
/* IPv4 */
ip = (odph_ipv4hdr_t *)
odp_packet_l3_ptr(pkt, NULL);
ip_tmp_addr = ip->src_addr;
ip->src_addr = ip->dst_addr;
ip->dst_addr = ip_tmp_addr;
}
}
}
}
static int convert_str_to_pmr_enum(char *token, odp_cls_pmr_term_t *term)
{
if (NULL == token)
return -1;
if (strcasecmp(token, "ODP_PMR_ETHTYPE_0") == 0) {
return 0;
} else if (strcasecmp(token, "ODP_PMR_ETHTYPE_X") == 0) {
return 0;
} else if (strcasecmp(token, "ODP_PMR_VLAN_ID_0") == 0) {
return 0;
} else if (strcasecmp(token, "ODP_PMR_VLAN_ID_X") == 0) {
return 0;
} else if (strcasecmp(token, "ODP_PMR_UDP_DPORT") == 0) {
return 0;
} else if (strcasecmp(token, "ODP_PMR_TCP_DPORT") == 0) {
return 0;
} else if (strcasecmp(token, "ODP_PMR_UDP_SPORT") == 0) {
return 0;
} else if (strcasecmp(token, "ODP_PMR_TCP_SPORT") == 0) {
return 0;
} else if (strcasecmp(token, "ODP_PMR_DIP_ADDR") == 0) {
return 0;
} else if (strcasecmp(token, "ODP_PMR_SIP_ADDR") == 0) {
return 0;
} else if (strcasecmp(token, "ODP_PMR_DMAC") == 0) {
*term = ODP_PMR_DMAC;
return 0;
} else if (strcasecmp(token, "ODP_PMR_CUSTOM_FRAME") == 0) {
return 0;
} else if (strcasecmp(token, "ODP_PMR_CUSTOM_L3") == 0) {
return 0;
}
return -1;
}
static int parse_pmr_policy(appl_args_t *appl_args, char *optarg)
{
int policy_count;
char *token, *cos0, *cos1, *cur_char;
size_t len;
global_statistics *stats;
odph_ethaddr_t mac;
char *pmr_str;
uint32_t offset, ip_addr, u32;
unsigned long int value, mask;
uint16_t u16;
int val_sz, mask_sz;
policy_count = appl_args->policy_count;
stats = appl_args->stats;
/* last array index is needed for default queue */
if (policy_count >= MAX_PMR_COUNT - 1) {
ODPH_ERR("Too many policies. Max count is %i.\n",
MAX_PMR_COUNT - 1);
return -1;
}
len = strlen(optarg);
len++;
pmr_str = malloc(len);
if (pmr_str == NULL) {
ODPH_ERR("Memory allocation failed\n");
return -1;
}
strcpy(pmr_str, optarg);
/* PMR TERM */
/* <term>:<xxx>:<yyy>:<src_cos>:<dst_cos> */
token = strtok(pmr_str, ":");
if (convert_str_to_pmr_enum(token, &term)) {
ODPH_ERR("Invalid ODP_PMR_TERM string\n");
goto error;
}
stats[policy_count].rule.term = term;
stats[policy_count].rule.offset = 0;
/* PMR value */
switch (term) {
/* Fall through */
/* Fall through */
/* :<type>:<mask> */
/* Fall through */
/* Fall through */
/* :<vlan_id>:<mask> */
/* Fall through */
/* Fall through */
/* Fall through */
/* :<port>:<mask> */
token = strtok(NULL, ":");
odph_strcpy(stats[policy_count].value, token,
DISPLAY_STRING_LEN);
value = strtoul(token, NULL, 0);
u16 = value;
u16 = odp_cpu_to_be_16(u16);
memcpy(stats[policy_count].rule.value_be, &u16, sizeof(u16));
token = strtok(NULL, ":");
odph_strcpy(stats[policy_count].mask, token,
DISPLAY_STRING_LEN);
mask = strtoul(token, NULL, 0);
u16 = mask;
u16 = odp_cpu_to_be_16(u16);
memcpy(stats[policy_count].rule.mask_be, &u16, sizeof(u16));
stats[policy_count].rule.val_sz = 2;
break;
/* Fall through */
/* :<IP addr>:<mask> */
token = strtok(NULL, ":");
odph_strcpy(stats[policy_count].value, token,
DISPLAY_STRING_LEN);
if (odph_ipv4_addr_parse(&ip_addr, token)) {
ODPH_ERR("Bad IP address\n");
goto error;
}
u32 = odp_cpu_to_be_32(ip_addr);
memcpy(stats[policy_count].rule.value_be, &u32, sizeof(u32));
token = strtok(NULL, ":");
odph_strcpy(stats[policy_count].mask, token,
DISPLAY_STRING_LEN);
mask = strtoul(token, NULL, 0);
u32 = mask;
u32 = odp_cpu_to_be_32(u32);
memcpy(stats[policy_count].rule.mask_be, &u32, sizeof(u32));
stats[policy_count].rule.val_sz = 4;
break;
/* :<MAC addr>:<mask> */
token = strtok(NULL, ":");
odph_strcpy(stats[policy_count].value, token,
DISPLAY_STRING_LEN);
/* Replace hyphens in the MAC string with colons to be compatible with
* odph_eth_addr_parse(). */
cur_char = token;
while ((cur_char = strchr(cur_char, '-')) != NULL)
*cur_char++ = ':';
if (odph_eth_addr_parse(&mac, token)) {
ODPH_ERR("Invalid MAC address. Use format 11-22-33-44-55-66.\n");
goto error;
}
memcpy(stats[policy_count].rule.value_be, mac.addr, ODPH_ETHADDR_LEN);
stats[policy_count].rule.val_sz = 6;
token = strtok(NULL, ":");
odph_strcpy(stats[policy_count].mask, token, DISPLAY_STRING_LEN);
mask_sz = parse_custom(token, stats[policy_count].rule.mask_be, ODPH_ETHADDR_LEN);
if (mask_sz != ODPH_ETHADDR_LEN) {
ODPH_ERR("Invalid mask. Provide mask without 0x prefix.\n");
goto error;
}
break;
/* Fall through */
/* :<offset>:<value>:<mask> */
token = strtok(NULL, ":");
errno = 0;
offset = strtoul(token, NULL, 0);
stats[policy_count].rule.offset = offset;
if (errno)
goto error;
token = strtok(NULL, ":");
odph_strcpy(stats[policy_count].value, token,
DISPLAY_STRING_LEN);
val_sz = parse_custom(token,
stats[policy_count].rule.value_be,
MAX_VAL_SIZE);
stats[policy_count].rule.val_sz = val_sz;
if (val_sz <= 0)
goto error;
token = strtok(NULL, ":");
odph_strcpy(stats[policy_count].mask, token,
DISPLAY_STRING_LEN);
mask_sz = parse_custom(token,
stats[policy_count].rule.mask_be,
MAX_VAL_SIZE);
if (mask_sz != val_sz)
goto error;
break;
default:
goto error;
}
/* Optional source CoS name and name of this CoS
* :<src_cos>:<cos> */
cos0 = strtok(NULL, ":");
cos1 = strtok(NULL, ":");
if (cos0 == NULL)
goto error;
if (cos1) {
stats[policy_count].has_src_cos = 1;
odph_strcpy(stats[policy_count].src_cos_name, cos0,
odph_strcpy(stats[policy_count].cos_name, cos1,
} else {
odph_strcpy(stats[policy_count].cos_name, cos0,
}
appl_args->policy_count++;
free(pmr_str);
return 0;
error:
free(pmr_str);
return -1;
}
static int parse_policy_ci_pass_count(appl_args_t *appl_args, char *optarg)
{
int num_ci_pass_rules;
char *token, *value;
size_t len;
ci_pass_counters *ci_pass_rules;
char *count_str;
num_ci_pass_rules = appl_args->num_ci_pass_rules;
ci_pass_rules = appl_args->ci_pass_rules;
/* last array index is needed for default queue */
if (num_ci_pass_rules >= MAX_PMR_COUNT) {
ODPH_ERR("Too many ci pass counters. Max count is %i.\n",
MAX_PMR_COUNT);
return -1;
}
len = strlen(optarg);
len++;
count_str = malloc(len);
if (count_str == NULL) {
ODPH_ERR("Memory allocation failed\n");
return -1;
}
strcpy(count_str, optarg);
token = strtok(count_str, ":");
value = strtok(NULL, ":");
if (!token || !value) {
free(count_str);
return -1;
}
strcpy(ci_pass_rules[num_ci_pass_rules].cos_name, token);
ci_pass_rules[num_ci_pass_rules].count = atoll(value);
appl_args->num_ci_pass_rules++;
free(count_str);
return 0;
}
static int parse_args(int argc, char *argv[], appl_args_t *appl_args)
{
int opt;
int long_index;
size_t len;
int i;
int interface = 0;
int ret = 0;
static const struct option longopts[] = {
{"count", required_argument, NULL, 'c'},
{"interface", required_argument, NULL, 'i'},
{"policy", required_argument, NULL, 'p'},
{"mode", required_argument, NULL, 'm'},
{"time", required_argument, NULL, 't'},
{"ci_pass", required_argument, NULL, 'C'},
{"promisc_mode", no_argument, NULL, 'P'},
{"verbose", no_argument, NULL, 'v'},
{"help", no_argument, NULL, 'h'},
{"enable", required_argument, NULL, 'e'},
{"layer", required_argument, NULL, 'l'},
{"dedicated", required_argument, NULL, 'd'},
{"size", required_argument, NULL, 's'},
{"burst", required_argument, NULL, 'b'},
{NULL, 0, NULL, 0}
};
static const char *shortopts = "+c:t:i:p:m:t:C:Pvhe:l:d:s:b:";
appl_args->cpu_count = 1; /* Use one worker by default */
appl_args->verbose = 0;
appl_args->promisc_mode = 0;
appl_args->classifier_enable = 1;
appl_args->parse_layer = ODP_PROTO_LAYER_ALL;
appl_args->cos_pools = 1;
appl_args->pool_size = SHM_PKT_POOL_SIZE;
appl_args->burst_size = DEF_PKT_BURST;
while (ret == 0) {
opt = getopt_long(argc, argv, shortopts,
longopts, &long_index);
if (opt == -1)
break; /* No more options */
switch (opt) {
case 'c':
appl_args->cpu_count = atoi(optarg);
break;
case 'p':
if (parse_pmr_policy(appl_args, optarg)) {
ret = -1;
break;
}
break;
case 't':
appl_args->time = atoi(optarg);
break;
case 'i':
len = strlen(optarg);
if (len == 0) {
ret = -1;
break;
}
len += 1; /* add room for '\0' */
appl_args->if_name = malloc(len);
if (appl_args->if_name == NULL) {
ret = -1;
break;
}
strcpy(appl_args->if_name, optarg);
interface = 1;
break;
case 'm':
i = atoi(optarg);
if (i == 0)
appl_args->appl_mode = APPL_MODE_DROP;
else
appl_args->appl_mode = APPL_MODE_REPLY;
break;
case 'C':
if (parse_policy_ci_pass_count(appl_args, optarg)) {
ret = -1;
break;
}
break;
case 'P':
appl_args->promisc_mode = 1;
break;
case 'v':
appl_args->verbose = 1;
break;
case 'h':
ret = -1;
break;
case 'e':
appl_args->classifier_enable = atoi(optarg);
break;
case 'l':
appl_args->parse_layer = atoi(optarg);
break;
case 'd':
appl_args->cos_pools = atoi(optarg);
break;
case 's':
appl_args->pool_size = atoi(optarg);
break;
case 'b':
appl_args->burst_size = atoi(optarg);
break;
default:
break;
}
}
if (!interface)
ret = -1;
if (appl_args->if_name == NULL)
ret = -1;
if (ret) {
usage();
free(appl_args->if_name);
}
/* reset optind from the getopt lib */
optind = 1;
return ret;
}
static void print_info(char *progname, appl_args_t *appl_args)
{
printf("Running ODP appl: \"%s\"\n"
"-----------------\n"
"Using IF: %s\n",
progname, appl_args->if_name);
printf("Promisc mode: %s\n", appl_args->promisc_mode ? "enabled" : "disabled");
printf("\n\n");
fflush(NULL);
}
static void usage(void)
{
printf("\n"
"ODP Classifier example.\n"
"Usage: odp_classifier OPTIONS\n"
" E.g. odp_classifier -i eth1 -m 0 -p \"ODP_PMR_SIP_ADDR:10.10.10.0:0xFFFFFF00:queue1\" \\\n"
" -p \"ODP_PMR_SIP_ADDR:10.10.10.10:0xFFFFFFFF:queue1:queue2\"\n"
"\n"
"The above example would classify:\n"
" 1) Packets from source IP address 10.10.10.0/24 to queue1, except ...\n"
" 2) Packets from source IP address 10.10.10.10 to queue2\n"
" 3) All other packets to DefaultCos\n"
"\n"
"Mandatory OPTIONS:\n"
" -i, --interface <interface name>\n"
"\n"
"Optional OPTIONS\n"
" -p, --policy <PMR term>:<offset>:<value>:<mask>:<src queue>:<dst queue>\n"
"\n"
" <PMR term> PMR term name defined in odp_cls_pmr_term_t\n"
" <offset> If term is ODP_PMR_CUSTOM_FRAME or _CUSTOM_L3, offset in bytes is used\n"
" <value> PMR value to be matched\n"
" <mask> PMR mask bits to be applied on the PMR value.\n"
" CUSTOM PMR terms accept plain hex string, other PMR terms require\n"
" hex string with '0x' prefix.\n"
" <src queue> Optional name of the source queue (CoS). The default CoS is used when\n"
" this is not defined.\n"
" <dst queue> Name of the destination queue (CoS).\n"
"\n"
" -c, --count <num> CPU count, 0=all available, default=1\n"
"\n"
" -m, --mode <mode> 0: Packet Drop mode. Received packets will be dropped\n"
" !0: Echo mode. Received packets will be sent back\n"
" default: Packet Drop mode\n"
"\n"
" -t, --time <sec> !0: Time for which the classifier will be run in seconds\n"
" 0: Runs in infinite loop\n"
" default: Runs in infinite loop\n"
"\n"
" -e, --enable <enable> 0: Classifier is disabled\n"
" 1: Classifier is enabled\n"
" default: Classifier is enabled\n"
"\n"
" -l, --layer <layer> Parse packets up to and including this layer. See odp_proto_layer_t\n"
" default: ODP_PROTO_LAYER_ALL\n"
"\n"
" -d, --dedicated <enable> 0: One pool for pktio and all CoSes\n"
" 1: Dedicated pools for pktio and each CoS\n"
" default: Dedicated pools\n"
"\n"
" -s, --size <num> Number of packets in each packet pool\n"
" default: %d\n"
"\n"
" -b, --burst <num> Packet burst size\n"
" default: %d\n"
"\n"
" -C, --ci_pass <dst queue:count>\n"
" Minimum acceptable packet count for a CoS destination queue.\n"
" If the received packet count is smaller than this value,\n"
" the application will exit with an error.\n"
" E.g: -C \"queue1:100\" -C \"queue2:200\" -C \"DefaultQueue:100\"\n"
" -P, --promisc_mode Enable promiscuous mode.\n"
" -v, --verbose Verbose output.\n"
" -h, --help Display help and exit.\n"
"\n", SHM_PKT_POOL_SIZE, DEF_PKT_BURST);
}
void odp_atomic_init_u64(odp_atomic_u64_t *atom, uint64_t val)
Initialize atomic uint64 variable.
void odp_atomic_inc_u64(odp_atomic_u64_t *atom)
Increment atomic uint64 variable.
void odp_atomic_add_u64(odp_atomic_u64_t *atom, uint64_t val)
Add to atomic uint64 variable.
uint64_t odp_atomic_load_u64(odp_atomic_u64_t *atom)
Load value of atomic uint64 variable.
#define ODP_COS_NAME_LEN
Maximum class of service name length, including the null character.
int odp_cos_destroy(odp_cos_t cos)
Discard a class-of-service along with all its associated resources.
odp_pmr_t odp_cls_pmr_create(const odp_pmr_param_t *terms, int num_terms, odp_cos_t src_cos, odp_cos_t dst_cos)
Create Packet Matching Rule (PMR)
odp_cls_pmr_term_t
Packet Matching Rule terms.
#define ODP_PMR_INVALID
Invalid packet matching rule handle.
void odp_cls_print_all(void)
Print classifier info.
int odp_cls_pmr_destroy(odp_pmr_t pmr)
Function to destroy a packet match rule.
#define ODP_COS_INVALID
Invalid class of service handle.
odp_cos_t odp_cls_cos_create(const char *name, const odp_cls_cos_param_t *param)
Create a class-of-service.
void odp_cls_cos_param_init(odp_cls_cos_param_t *param)
Initialize class of service parameters.
void odp_cls_pmr_param_init(odp_pmr_param_t *param)
Initialize packet matching rule parameters.
@ ODP_PMR_TCP_DPORT
Destination TCP port (val_sz = 2)
@ ODP_PMR_ETHTYPE_0
Initial (outer) Ethertype only (val_sz = 2)
@ ODP_PMR_SIP_ADDR
Source IPv4 address (val_sz = 4)
@ ODP_PMR_DIP_ADDR
Destination IPv4 address (val_sz = 4)
@ ODP_PMR_TCP_SPORT
Source TCP port (val_sz = 2)
@ ODP_PMR_VLAN_ID_X
Last (most inner) VLAN ID (val_sz = 2)
@ ODP_PMR_CUSTOM_FRAME
Custom frame match rule.
@ ODP_PMR_UDP_SPORT
Source UDP port (val_sz = 2)
@ ODP_PMR_VLAN_ID_0
First (outer) VLAN ID (val_sz = 2)
@ ODP_PMR_UDP_DPORT
Destination UDP port (val_sz = 2)
@ ODP_PMR_DMAC
Destination MAC address (val_sz = 6)
@ ODP_PMR_ETHTYPE_X
Ethertype of most inner VLAN tag (val_sz = 2)
@ ODP_PMR_CUSTOM_L3
Custom layer 3 match rule.
#define odp_unlikely(x)
Branch unlikely taken.
Definition: spec/hints.h:64
uint32_t odp_u32be_t
unsigned 32bit big endian
odp_u16be_t odp_cpu_to_be_16(uint16_t cpu16)
Convert cpu native uint16_t to 16bit big endian.
odp_u32be_t odp_cpu_to_be_32(uint32_t cpu32)
Convert cpu native uint32_t to 32bit big endian.
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
int odp_cpumask_first(const odp_cpumask_t *mask)
Find first set CPU in mask.
int32_t odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int32_t size)
Format a string from CPU mask.
#define ODP_CPUMASK_STR_SIZE
The maximum number of characters needed to record any CPU mask as a string (output of odp_cpumask_to_...
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.
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.
void odp_pktout_queue_param_init(odp_pktout_queue_param_t *param)
Initialize packet output queue parameters.
int odp_pktio_stop(odp_pktio_t pktio)
Stop packet receive and transmit.
int odp_pktio_capability(odp_pktio_t pktio, odp_pktio_capability_t *capa)
Query packet IO interface capabilities.
int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[], int num)
Send packets directly to an interface output queue.
uint64_t odp_pktio_to_u64(odp_pktio_t pktio)
Get printable value for an odp_pktio_t.
int odp_pktio_default_cos_set(odp_pktio_t pktio, odp_cos_t default_cos)
Setup per-port default class-of-service.
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_PKTIN_MODE_SCHED
Packet input through scheduler and scheduled event queues.
void odp_packet_print_data(odp_packet_t pkt, uint32_t offset, uint32_t len)
Print packet data.
void odp_packet_from_event_multi(odp_packet_t pkt[], const odp_event_t ev[], int num)
Convert multiple packet events to packet handles.
int odp_packet_has_ipv4(odp_packet_t pkt)
Check for IPv4.
int odp_packet_has_eth(odp_packet_t pkt)
Check for Ethernet header.
uint32_t odp_packet_len(odp_packet_t pkt)
Packet data length.
int odp_packet_has_error(odp_packet_t pkt)
Check for all parse errors in packet.
void odp_packet_free(odp_packet_t pkt)
Free packet.
void * odp_packet_l2_ptr(odp_packet_t pkt, uint32_t *len)
Layer 2 start pointer.
void * odp_packet_l3_ptr(odp_packet_t pkt, uint32_t *len)
Layer 3 start pointer.
void odp_packet_free_multi(const odp_packet_t pkt[], int num)
Free multiple packets.
odp_pool_t odp_packet_pool(odp_packet_t pkt)
Packet pool.
@ ODP_PROTO_LAYER_ALL
All layers.
#define ODP_POOL_NAME_LEN
Maximum pool name length, including the null character.
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()
void odp_pool_print_all(void)
Print debug info about all pools.
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_PACKET
Packet pool.
void odp_queue_param_init(odp_queue_param_t *param)
Initialize queue params.
#define ODP_QUEUE_INVALID
Invalid queue.
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
Queue create.
int odp_queue_destroy(odp_queue_t queue)
Destroy ODP queue.
int odp_queue_info(odp_queue_t queue, odp_queue_info_t *info)
Retrieve information about a queue.
@ ODP_QUEUE_TYPE_SCHED
Scheduled queue.
#define ODP_SCHED_SYNC_PARALLEL
Parallel scheduled queues.
int odp_schedule_multi(odp_queue_t *from, uint64_t wait, odp_event_t events[], int num)
Schedule multiple events.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
uint64_t odp_schedule_wait_time(uint64_t ns)
Schedule wait time.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
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.
void odp_sys_info_print(void)
Print system info.
int odp_thread_id(void)
Get thread identifier.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
odp_time_t odp_time_local(void)
Current local time.
#define ODP_TIME_MSEC_IN_NS
A millisecond in nanoseconds.
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
The OpenDataPlane API.
Dummy type for strong typing.
Class of service parameters Used to communicate class of service creation options.
odp_queue_t queue
Mapping used when num_queue = 1, hashing is disabled in this case and application has to configure th...
odp_pool_t pool
Pool associated with CoS.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
Packet input queue parameters.
odp_bool_t classifier_enable
Enable classifier.
Packet IO capabilities.
odp_pktio_set_op_t set_op
Supported set operations.
uint32_t max_output_queues
Maximum number of output queues.
Packet IO configuration options.
odp_pktio_parser_config_t parser
Packet input parser configuration.
Packet IO parameters.
odp_pktin_mode_t in_mode
Packet input mode.
odp_proto_layer_t layer
Protocol parsing level in packet input.
Packet output queue parameters.
uint32_t num_queues
Number of output queues to be created.
Packet Matching Rule parameter structure.
uint32_t offset
Offset to the value.
uint32_t val_sz
Size of the value to be matched.
struct odp_pmr_param_t::@8::@10 match
Parameters for single-valued matches.
odp_cls_pmr_term_t term
Packet Matching Rule term.
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...
Queue information Retrieve information about a queue with odp_queue_info()
const char * name
queue name
ODP Queue parameters.
odp_schedule_param_t sched
Scheduler parameters.
odp_queue_type_t type
Queue type.
odp_schedule_group_t group
Thread group.
odp_schedule_sync_t sync
Synchronization method.
struct odp_pktio_set_op_t::@104 op
Operation flags.
uint32_t promisc_mode
Promiscuous mode.