diff options
Diffstat (limited to 'example/ping/odp_ping.c')
-rw-r--r-- | example/ping/odp_ping.c | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/example/ping/odp_ping.c b/example/ping/odp_ping.c new file mode 100644 index 000000000..97b856895 --- /dev/null +++ b/example/ping/odp_ping.c @@ -0,0 +1,766 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2019-2023 Nokia + */ + +/** + * @example odp_ping.c + * + * This application replies to IPv4 ping requests. It can be used to test + * connectivity with standard ping utility. ARP table needs to be setup manually + * on the sender side as the application does not reply to ARP requests. + * + * @cond _ODP_HIDE_FROM_DOXYGEN_ + */ + +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <getopt.h> + +#include <odp_api.h> +#include <odp/helper/odph_api.h> + +#define MAX_PKTIOS 32 +#define MAX_PKTIO_NAME 255 +#define MAX_PKT_NUM 1024 + +ODP_STATIC_ASSERT(MAX_PKTIOS < UINT8_MAX, "MAX_PKTIOS too large for index lookup"); + +typedef struct test_options_t { + uint64_t num_packet; + uint32_t timeout; + int promisc; + int verbose; + int num_pktio; + char pktio_name[MAX_PKTIOS][MAX_PKTIO_NAME + 1]; + +} test_options_t; + +typedef struct test_global_t { + test_options_t opt; + uint64_t rx_packets; + uint64_t tx_replies; + odp_pool_t pool; + odp_atomic_u32_t stop; + + struct { + odph_ethaddr_t eth_addr; + odp_pktio_t pktio; + odp_pktout_queue_t pktout; + int started; + + } pktio[MAX_PKTIOS]; + + /* Pktio index lookup table */ + uint8_t pktio_from_idx[ODP_PKTIO_MAX_INDEX + 1]; + +} test_global_t; + +static test_global_t test_global; + +static void sig_handler(int signo) +{ + (void)signo; + + odp_atomic_store_u32(&test_global.stop, 1); +} + +static void print_usage(void) +{ + printf("\n" + "ODP ping example. Replies to ICMPv4 ping requests.\n" + "\n" + "OPTIONS:\n" + " -i, --interface <name> Packet IO interfaces (comma-separated, no spaces)\n" + " -n, --num_packet <number> Exit after this many packets. Use 0 to run infinitely. Default 0.\n" + " -t, --timeout <sec> Exit after this many seconds. Use 0 to run infinitely. Default 0.\n" + " -p, --promisc Set interface into promiscuous mode.\n" + " -v, --verbose Print extra packet information.\n" + " -h, --help Display help and exit.\n\n"); +} + +static int parse_options(int argc, char *argv[], test_global_t *global) +{ + int i, opt, long_index; + char *name, *str; + int len, str_len; + + const struct option longopts[] = { + {"interface", required_argument, NULL, 'i'}, + {"num_packet", required_argument, NULL, 'n'}, + {"timeout", required_argument, NULL, 't'}, + {"promisc", no_argument, NULL, 'p'}, + {"verbose", no_argument, NULL, 'v'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + const char *shortopts = "+i:n:t:pvh"; + int ret = 0; + + while (1) { + opt = getopt_long(argc, argv, shortopts, longopts, &long_index); + + if (opt == -1) + break; /* No more options */ + + switch (opt) { + case 'i': + i = 0; + str = optarg; + str_len = strlen(str); + + while (str_len > 0) { + len = strcspn(str, ","); + str_len -= len + 1; + + if (i == MAX_PKTIOS) { + ODPH_ERR("Too many interfaces\n"); + ret = -1; + break; + } + + if (len > MAX_PKTIO_NAME) { + ODPH_ERR("Too long interface name: %s\n", str); + ret = -1; + break; + } + + name = global->opt.pktio_name[i]; + memcpy(name, str, len); + str += len + 1; + i++; + } + + global->opt.num_pktio = i; + + break; + case 'n': + global->opt.num_packet = atoll(optarg); + break; + case 't': + global->opt.timeout = atoi(optarg); + break; + case 'p': + global->opt.promisc = 1; + break; + case 'v': + global->opt.verbose = 1; + break; + case 'h': + default: + print_usage(); + return -1; + } + } + + if (global->opt.num_pktio == 0) { + ODPH_ERR("At least one pktio interface needed\n"); + ret = -1; + } + + return ret; +} + +static int open_pktios(test_global_t *global) +{ + odp_pool_param_t pool_param; + odp_pktio_param_t pktio_param; + odp_pool_t pool; + odp_pool_capability_t pool_capa; + odp_pktio_capability_t pktio_capa; + odp_pktio_t pktio; + odp_pktio_config_t pktio_config; + odp_pktin_queue_param_t pktin_param; + odp_pktout_queue_param_t pktout_param; + odp_pktout_queue_t pktout; + char *name; + int i, num_pktio; + uint32_t num_pkt = MAX_PKT_NUM; + + num_pktio = global->opt.num_pktio; + + if (odp_pool_capability(&pool_capa)) { + ODPH_ERR("Pool capability failed\n"); + return -1; + } + + if (pool_capa.pkt.max_num < MAX_PKT_NUM) + num_pkt = pool_capa.pkt.max_num; + + odp_pool_param_init(&pool_param); + pool_param.pkt.num = num_pkt; + pool_param.type = ODP_POOL_PACKET; + + pool = odp_pool_create("packet pool", &pool_param); + + global->pool = pool; + + if (pool == ODP_POOL_INVALID) { + ODPH_ERR("Pool create failed\n"); + return -1; + } + + odp_pktio_param_init(&pktio_param); + pktio_param.in_mode = ODP_PKTIN_MODE_SCHED; + pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT; + + for (i = 0; i < num_pktio; i++) + global->pktio[i].pktio = ODP_PKTIO_INVALID; + + /* Open and configure interfaces */ + for (i = 0; i < num_pktio; i++) { + name = global->opt.pktio_name[i]; + pktio = odp_pktio_open(name, pool, &pktio_param); + + if (pktio == ODP_PKTIO_INVALID) { + ODPH_ERR("Pktio open failed: %s\n", name); + return -1; + } + + global->pktio[i].pktio = pktio; + + if (odp_pktio_capability(pktio, &pktio_capa)) { + ODPH_ERR("Packet IO capability failed\n"); + return -1; + } + + if (odp_pktio_mac_addr(pktio, + &global->pktio[i].eth_addr.addr, + ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) { + ODPH_ERR("MAC address read failed: %s\n", name); + return -1; + } + + odp_pktio_config_init(&pktio_config); + pktio_config.pktin.bit.ts_all = 1; + pktio_config.parser.layer = ODP_PROTO_LAYER_ALL; + + odp_pktio_config(pktio, &pktio_config); + + odp_pktin_queue_param_init(&pktin_param); + + pktin_param.queue_param.sched.prio = odp_schedule_default_prio(); + pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC; + pktin_param.queue_param.sched.group = ODP_SCHED_GROUP_ALL; + pktin_param.num_queues = 1; + + if (odp_pktin_queue_config(pktio, &pktin_param)) { + ODPH_ERR("Pktin config failed: %s\n", name); + return -1; + } + + odp_pktout_queue_param_init(&pktout_param); + pktout_param.num_queues = 1; + + if (odp_pktout_queue_config(pktio, &pktout_param)) { + ODPH_ERR("Pktout config failed: %s\n", name); + return -1; + } + + if (odp_pktout_queue(pktio, &pktout, 1) != 1) { + ODPH_ERR("Pktout queue request failed: %s\n", name); + return -1; + } + + global->pktio[i].pktout = pktout; + + if (global->opt.promisc && odp_pktio_promisc_mode(pktio) != 1) { + if (pktio_capa.set_op.op.promisc_mode == 0) { + ODPH_ERR("Promiscuous mode cannot be set: %s\n", name); + return -1; + } + + if (odp_pktio_promisc_mode_set(pktio, 1)) { + ODPH_ERR("Promiscuous mode set failed: %s\n", name); + return -1; + } + } + + odp_pktio_print(pktio); + } + + return 0; +} + +static int init_pktio_lookup_tbl(test_global_t *global) +{ + for (int i = 0; i < global->opt.num_pktio; i++) { + odp_pktio_t pktio = global->pktio[i].pktio; + int pktio_idx = odp_pktio_index(pktio); + + if (pktio_idx < 0) { + ODPH_ERR("odp_pktio_index() failed: %s\n", global->opt.pktio_name[i]); + return -1; + } + + global->pktio_from_idx[pktio_idx] = i; + } + return 0; +} + +static int start_pktios(test_global_t *global) +{ + int i; + + for (i = 0; i < global->opt.num_pktio; i++) { + if (odp_pktio_start(global->pktio[i].pktio)) { + ODPH_ERR("Pktio start failed: %s\n", global->opt.pktio_name[i]); + return -1; + } + + global->pktio[i].started = 1; + } + + return 0; +} + +static int stop_pktios(test_global_t *global) +{ + odp_pktio_t pktio; + int i, ret = 0; + + for (i = 0; i < global->opt.num_pktio; i++) { + pktio = global->pktio[i].pktio; + + if (pktio == ODP_PKTIO_INVALID || global->pktio[i].started == 0) + continue; + + if (odp_pktio_stop(pktio)) { + ODPH_ERR("Pktio stop failed: %s\n", global->opt.pktio_name[i]); + ret = -1; + } + } + + return ret; +} + +static void empty_queues(void) +{ + odp_event_t ev; + uint64_t wait_time = odp_schedule_wait_time(ODP_TIME_SEC_IN_NS / 2); + + /* Drop all events from all queues */ + while (1) { + ev = odp_schedule(NULL, wait_time); + + if (ev == ODP_EVENT_INVALID) + break; + + odp_event_free(ev); + } +} + +static int close_pktios(test_global_t *global) +{ + odp_pktio_t pktio; + odp_pool_t pool; + int i, ret = 0; + + for (i = 0; i < global->opt.num_pktio; i++) { + pktio = global->pktio[i].pktio; + + if (pktio == ODP_PKTIO_INVALID) + continue; + + if (odp_pktio_close(pktio)) { + ODPH_ERR("Pktio close failed: %s\n", global->opt.pktio_name[i]); + ret = -1; + } + } + + pool = global->pool; + + if (pool == ODP_POOL_INVALID) + return ret; + + if (odp_pool_destroy(pool)) { + ODPH_ERR("Pool destroy failed\n"); + ret = -1; + } + + return ret; +} + +static void print_mac_addr(uint8_t *addr) +{ + printf("%02x:%02x:%02x:%02x:%02x:%02x\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); +} + +static void print_ipv4_addr(uint8_t *addr) +{ + printf("%u.%u.%u.%u\n", + addr[0], addr[1], addr[2], addr[3]); +} + +static void print_data(odp_packet_t pkt, uint32_t offset, uint32_t len) +{ + const uint32_t bytes_per_row = 16; + const uint32_t num_char = 1 + (bytes_per_row * 3) + 1; + uint8_t data[bytes_per_row]; + char row[num_char]; + uint32_t copy_len, i, j; + uint32_t data_len = odp_packet_len(pkt); + + if (offset > data_len) + return; + + if (offset + len > data_len) + len = data_len - offset; + + while (len) { + i = 0; + + if (len > bytes_per_row) + copy_len = bytes_per_row; + else + copy_len = len; + + odp_packet_copy_to_mem(pkt, offset, copy_len, data); + + i += snprintf(&row[i], num_char - i, " "); + + for (j = 0; j < copy_len; j++) + i += snprintf(&row[i], num_char - i, " %02x", data[j]); + + row[i] = 0; + printf("%s\n", row); + + len -= copy_len; + offset += copy_len; + } +} + +static void print_packet(odp_packet_t pkt, uint64_t num_packet) +{ + odp_pktio_t pktio; + odp_pktio_info_t pktio_info; + odp_time_t time; + uint64_t sec, nsec; + uint32_t offset; + uint8_t *data = odp_packet_data(pkt); + uint32_t seg_len = odp_packet_seg_len(pkt); + uint32_t l2_offset = odp_packet_l2_offset(pkt); + uint32_t l3_offset = odp_packet_l3_offset(pkt); + uint32_t l4_offset = odp_packet_l4_offset(pkt); + uint32_t data_len = odp_packet_len(pkt); + int icmp = odp_packet_has_icmp(pkt); + int ipv4 = odp_packet_has_ipv4(pkt); + + if (odp_packet_has_ts(pkt)) + time = odp_packet_ts(pkt); + else + time = odp_time_local(); + + nsec = odp_time_to_ns(time); + sec = nsec / ODP_TIME_SEC_IN_NS; + nsec = nsec - (sec * ODP_TIME_SEC_IN_NS); + pktio = odp_packet_input(pkt); + + printf("PACKET [%" PRIu64 "]\n", num_packet); + printf(" time: %" PRIu64 ".%09" PRIu64 " sec\n", sec, nsec); + if (odp_pktio_info(pktio, &pktio_info) == 0) + printf(" interface name: %s\n", pktio_info.name); + else + printf(" interface name: n/a\n"); + printf(" packet length: %u bytes\n", odp_packet_len(pkt)); + + /* L2 */ + if (odp_packet_has_eth(pkt)) { + printf(" Ethernet offset: %u bytes\n", l2_offset); + offset = l2_offset; + if (offset + 6 <= seg_len) { + printf(" dst address: "); + print_mac_addr(data + offset); + } + + offset = l2_offset + 6; + if (offset + 6 <= seg_len) { + printf(" src address: "); + print_mac_addr(data + offset); + } + } else if (odp_packet_has_l2(pkt)) { + printf(" L2 (%i) offset: %u bytes\n", + odp_packet_l2_type(pkt), l2_offset); + } + + /* L3 */ + if (ipv4) { + printf(" IPv4 offset: %u bytes\n", l3_offset); + offset = l3_offset + 12; + if (offset + 4 <= seg_len) { + printf(" src address: "); + print_ipv4_addr(data + offset); + } + + offset = l3_offset + 16; + if (offset + 4 <= seg_len) { + printf(" dst address: "); + print_ipv4_addr(data + offset); + } + } else if (odp_packet_has_ipv6(pkt)) { + printf(" IPv6 offset: %u bytes\n", l3_offset); + } else if (odp_packet_has_l3(pkt)) { + printf(" L3 (%i) offset: %u bytes\n", + odp_packet_l3_type(pkt), l3_offset); + } + + /* L4 */ + if (icmp) { + printf(" ICMP offset: %u bytes\n", l4_offset); + if (ipv4) { + uint32_t len; + uint8_t *u8 = odp_packet_l4_ptr(pkt, &len); + + if (u8 && len >= 2) { + printf(" type: %u\n", u8[0]); + printf(" code: %u\n", u8[1]); + } + } + } else if (odp_packet_has_l4(pkt)) { + printf(" L4 (%i) offset: %u bytes\n", + odp_packet_l4_type(pkt), l4_offset); + } + + print_data(pkt, 0, data_len); + + printf("\n"); +} + +/* Updated checksum when a 16 bit word has been changed from old to new */ +static uint16_t update_chksum(uint16_t chksum, uint16_t old, uint16_t new) +{ + uint16_t chksum_comp = ~chksum; + uint16_t old_comp = ~old; + uint32_t sum = chksum_comp + old_comp + new; + + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return ~sum; +} + +static void icmp_reply(test_global_t *global, odp_packet_t pkt) +{ + uint32_t dst_ip; + odph_ipv4hdr_t *ip_hdr; + odph_ethhdr_t *eth_hdr; + uint16_t old, new; + uint32_t len = 0; + int index = global->pktio_from_idx[odp_packet_input_index(pkt)]; + odp_pktout_queue_t pktout = global->pktio[index].pktout; + odph_ethaddr_t *eth_addr = &global->pktio[index].eth_addr; + int icmp = odp_packet_has_icmp(pkt); + int ipv4 = odp_packet_has_ipv4(pkt); + int eth = odp_packet_has_eth(pkt); + odph_icmphdr_t *icmp_hdr = odp_packet_l4_ptr(pkt, &len); + + if (odp_packet_has_error(pkt)) + goto error; + + if (eth == 0 || ipv4 == 0 || icmp == 0) + goto error; + + /* ICMP type, code and chksum fields are located in the first 4 bytes */ + if (icmp_hdr == NULL || len < 4) + goto error; + + if (icmp_hdr->type != ODPH_ICMP_ECHO || icmp_hdr->code != 0) + goto error; + + /* Echo reply */ + old = *(uint16_t *)(uintptr_t)icmp_hdr; + icmp_hdr->type = ODPH_ICMP_ECHOREPLY; + new = *(uint16_t *)(uintptr_t)icmp_hdr; + icmp_hdr->chksum = update_chksum(icmp_hdr->chksum, old, new); + + /* Swap IP addresses */ + ip_hdr = odp_packet_l3_ptr(pkt, &len); + if (ip_hdr == NULL || len < 20) + goto error; + + dst_ip = ip_hdr->dst_addr; + ip_hdr->dst_addr = ip_hdr->src_addr; + ip_hdr->src_addr = dst_ip; + + /* Swap Ethernet addresses */ + eth_hdr = odp_packet_l2_ptr(pkt, &len); + if (eth_hdr == NULL || len < 14) + goto error; + + eth_hdr->dst = eth_hdr->src; + eth_hdr->src = *eth_addr; + + if (odp_pktout_send(pktout, &pkt, 1) != 1) + goto error; + + global->tx_replies++; + return; + +error: + odp_packet_free(pkt); +} + +static void print_stat(test_global_t *global, uint64_t rx_packets, + uint64_t diff_ns) +{ + uint64_t prev = global->rx_packets; + double per_sec = 1000000000.0 * (rx_packets - prev) / diff_ns; + + printf("Received %" PRIu64 " packets (%.1f / sec). " + "Sent %" PRIu64 " replies.\n", + rx_packets, per_sec, global->tx_replies); + + global->rx_packets = rx_packets; +} + +static int receive_packets(test_global_t *global) +{ + odp_event_t ev; + odp_packet_t pkt; + uint64_t diff_ns; + int print = 0; + uint64_t num_packet = 0; + uint64_t timeout_ns = global->opt.timeout * ODP_TIME_SEC_IN_NS; + uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS); + odp_time_t start = odp_time_local(); + odp_time_t cur = start; + odp_time_t prev = start; + + while (!odp_atomic_load_u32(&global->stop)) { + ev = odp_schedule(NULL, wait); + + cur = odp_time_local(); + diff_ns = odp_time_diff_ns(cur, prev); + if (diff_ns >= ODP_TIME_SEC_IN_NS) { + prev = cur; + print = 1; + } + + if (ev == ODP_EVENT_INVALID) { + if (print) { + print_stat(global, num_packet, diff_ns); + print = 0; + } + + if (timeout_ns) { + if (odp_time_diff_ns(cur, start) >= timeout_ns) + break; + } + + continue; + } + + if (odp_event_type(ev) != ODP_EVENT_PACKET) { + printf("Bad event type: %i\n", odp_event_type(ev)); + odp_event_free(ev); + continue; + } + + pkt = odp_packet_from_event(ev); + + if (global->opt.verbose) + print_packet(pkt, num_packet); + + /* Reply or drop packet */ + icmp_reply(global, pkt); + + num_packet++; + if (print) { + print_stat(global, num_packet, diff_ns); + print = 0; + } + + if (global->opt.num_packet && num_packet >= global->opt.num_packet) + break; + } + + /* Timeout before num packets received */ + if (global->opt.num_packet && num_packet < global->opt.num_packet) { + ODPH_ERR("Received %" PRIu64 "/%" PRIu64 " packets\n", + num_packet, global->opt.num_packet); + return -1; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + odp_instance_t instance; + test_global_t *global; + int ret = 0; + + global = &test_global; + memset(global, 0, sizeof(test_global_t)); + odp_atomic_init_u32(&global->stop, 0); + + signal(SIGINT, sig_handler); + + if (parse_options(argc, argv, global)) + return -1; + + /* Init ODP before calling anything else */ + if (odp_init_global(&instance, NULL, NULL)) { + ODPH_ERR("Global init failed\n"); + return -1; + } + + /* Init this thread */ + if (odp_init_local(instance, ODP_THREAD_CONTROL)) { + ODPH_ERR("Local init failed\n"); + return -1; + } + + global->pool = ODP_POOL_INVALID; + + odp_schedule_config(NULL); + + odp_sys_info_print(); + + if (open_pktios(global)) { + ODPH_ERR("Pktio open failed\n"); + return -1; + } + + if (init_pktio_lookup_tbl(global)) { + ODPH_ERR("Mapping pktio indexes failed\n"); + return -1; + } + + if (start_pktios(global)) { + ODPH_ERR("Pktio start failed\n"); + return -1; + } + + if (receive_packets(global)) { + ret = -1; + } + + if (stop_pktios(global)) { + ODPH_ERR("Pktio stop failed\n"); + return -1; + } + + empty_queues(); + + if (close_pktios(global)) { + ODPH_ERR("Pktio close failed\n"); + return -1; + } + + if (odp_term_local()) { + ODPH_ERR("Term local failed\n"); + return -1; + } + + if (odp_term_global(instance)) { + ODPH_ERR("Term global failed\n"); + return -1; + } + + return ret; +} |