diff --git a/components/eppp_link/CMakeLists.txt b/components/eppp_link/CMakeLists.txt index bd6123139..e7e9c1432 100644 --- a/components/eppp_link/CMakeLists.txt +++ b/components/eppp_link/CMakeLists.txt @@ -1,3 +1,3 @@ idf_component_register(SRCS "eppp_link.c" INCLUDE_DIRS "include" - PRIV_REQUIRES esp_netif esp_driver_spi esp_driver_gpio esp_timer) + PRIV_REQUIRES esp_netif esp_driver_spi esp_driver_gpio esp_timer driver) diff --git a/components/eppp_link/eppp_link.c b/components/eppp_link/eppp_link.c index b7253e478..1dc0f2dc2 100644 --- a/components/eppp_link/eppp_link.c +++ b/components/eppp_link/eppp_link.c @@ -8,6 +8,7 @@ #include "sdkconfig.h" #include "esp_log.h" #include "esp_netif.h" +#include "esp_check.h" #include "esp_event.h" #include "esp_netif_ppp.h" #include "eppp_link_types.h" @@ -17,6 +18,8 @@ #include "driver/spi_slave.h" #include "driver/gpio.h" #include "esp_timer.h" +#elif CONFIG_EPPP_LINK_DEVICE_UART +#include "driver/uart.h" #endif static const int GOT_IPV4 = BIT0; @@ -44,7 +47,11 @@ enum eppp_type { struct eppp_handle { QueueHandle_t out_queue; +#if CONFIG_EPPP_LINK_DEVICE_SPI QueueHandle_t ready_semaphore; +#elif CONFIG_EPPP_LINK_DEVICE_UART + QueueHandle_t uart_event_queue; +#endif esp_netif_t *netif; enum eppp_type role; }; @@ -57,7 +64,7 @@ struct packet { static esp_err_t transmit(void *h, void *buffer, size_t len) { -#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_SPI +#if CONFIG_EPPP_LINK_DEVICE_SPI #define MAX_PAYLOAD 1600 struct eppp_handle *handle = h; struct packet buf = { }; @@ -76,6 +83,8 @@ static esp_err_t transmit(void *h, void *buffer, size_t len) ESP_LOGE(TAG, "Failed to queue packet to slave!"); } } while (remaining > 0); +#elif CONFIG_EPPP_LINK_DEVICE_UART + uart_write_bytes(UART_NUM_1, buffer, len); #endif return ESP_OK; } @@ -101,6 +110,7 @@ static esp_netif_t *netif_init(enum eppp_type role) return NULL; } h->role = role; +#if CONFIG_EPPP_LINK_DEVICE_SPI if (role == EPPP_CLIENT) { h->ready_semaphore = xSemaphoreCreateBinary(); if (!h->ready_semaphore) { @@ -110,6 +120,7 @@ static esp_netif_t *netif_init(enum eppp_type role) return NULL; } } +#endif esp_netif_driver_ifconfig_t driver_cfg = { .handle = h, @@ -119,7 +130,7 @@ static esp_netif_t *netif_init(enum eppp_type role) esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_PPP(); char if_key[] = "EPPP0"; // netif key needs to be unique - if_key[sizeof(if_key) - 1] += s_eppp_netif_count++; + if_key[sizeof(if_key) - 2 /* 2 = two chars before the terminator */ ] += s_eppp_netif_count++; base_netif_cfg.if_key = if_key; if (role == EPPP_CLIENT) { base_netif_cfg.if_desc = "pppos_client"; @@ -470,7 +481,56 @@ _Noreturn static void ppp_task(void *args) } } } -#endif // CONFIG_EPPP_LINK_DEVICE_SPI +#elif CONFIG_EPPP_LINK_DEVICE_UART +#define BUF_SIZE (1024) +#define UART_TX_CLIENT_TO_SERVER 10 +#define UART_TX_SERVER_TO_CLIENT 11 +#define UART_BAUDRATE 4000000 +#define UART_QUEUE_SIZE 16 + +static esp_err_t init_uart(struct eppp_handle *h) +{ + uart_config_t uart_config = {}; + uart_config.baud_rate = UART_BAUDRATE; + uart_config.data_bits = UART_DATA_8_BITS; + uart_config.parity = UART_PARITY_DISABLE; + uart_config.stop_bits = UART_STOP_BITS_1; + uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; + uart_config.source_clk = UART_SCLK_DEFAULT; + + ESP_RETURN_ON_ERROR(uart_driver_install(UART_NUM_1, BUF_SIZE, 0, UART_QUEUE_SIZE, &h->uart_event_queue, 0), TAG, "Failed to install UART"); + ESP_RETURN_ON_ERROR(uart_param_config(UART_NUM_1, &uart_config), TAG, "Failed to set params"); + int tx_io_num = h->role == EPPP_CLIENT ? UART_TX_CLIENT_TO_SERVER : UART_TX_SERVER_TO_CLIENT; + int rx_io_num = h->role == EPPP_CLIENT ? UART_TX_SERVER_TO_CLIENT : UART_TX_CLIENT_TO_SERVER; + ESP_RETURN_ON_ERROR(uart_set_pin(UART_NUM_1, tx_io_num, rx_io_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), TAG, "Failed to set UART pins"); + ESP_RETURN_ON_ERROR(uart_set_rx_timeout(UART_NUM_1, 1), TAG, "Failed to set UART Rx timeout"); + return ESP_OK; +} + +_Noreturn static void ppp_task(void *args) +{ + static uint8_t buffer[BUF_SIZE] = {}; + + esp_netif_t *netif = args; + struct eppp_handle *h = esp_netif_get_io_driver(netif); + uart_event_t event; + while (1) { + xQueueReceive(h->uart_event_queue, &event, pdMS_TO_TICKS(pdMS_TO_TICKS(100))); + if (event.type == UART_DATA) { + size_t len; + uart_get_buffered_data_len(UART_NUM_1, &len); + if (len) { + len = uart_read_bytes(UART_NUM_1, buffer, BUF_SIZE, 0); + ESP_LOG_BUFFER_HEXDUMP("ppp_uart_recv", buffer, len, ESP_LOG_VERBOSE); + esp_netif_receive(netif, buffer, len, NULL); + } + } else { + ESP_LOGW(TAG, "Received UART event: %d", event.type); + } + } + +} +#endif // CONFIG_EPPP_LINK_DEVICE_SPI / UART static esp_netif_t *default_setup(enum eppp_type role) @@ -498,6 +558,8 @@ static esp_netif_t *default_setup(enum eppp_type role) } else { init_slave(&s_spi_device, netif); } +#elif CONFIG_EPPP_LINK_DEVICE_UART + init_uart(esp_netif_get_io_driver(netif)); #endif netif_start(netif); diff --git a/components/eppp_link/examples/host/CMakeLists.txt b/components/eppp_link/examples/host/CMakeLists.txt new file mode 100644 index 000000000..3405abc52 --- /dev/null +++ b/components/eppp_link/examples/host/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/iperf) + + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(pppos_host) diff --git a/components/eppp_link/examples/host/README.md b/components/eppp_link/examples/host/README.md new file mode 100644 index 000000000..a6592627c --- /dev/null +++ b/components/eppp_link/examples/host/README.md @@ -0,0 +1,9 @@ + +# Client side demo of ESP-PPP-Link + +This is a basic demo of using esp-mqtt library, but connects to the internet using a PPPoS client. To run this example, you would need a PPP server that provides connectivity to the MQTT broker used in this example (by default a public broker accessible on the internet). + +If configured, this example could also run a ping session and an iperf console. + + +The PPP server could be a Linux computer with `pppd` service or an ESP32 acting like a connection gateway with PPPoS server (see the "slave" project). diff --git a/components/eppp_link/examples/host/main/CMakeLists.txt b/components/eppp_link/examples/host/main/CMakeLists.txt new file mode 100644 index 000000000..0198ddd32 --- /dev/null +++ b/components/eppp_link/examples/host/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS app_main.c register_iperf.c + INCLUDE_DIRS ".") diff --git a/components/eppp_link/examples/host/main/Kconfig.projbuild b/components/eppp_link/examples/host/main/Kconfig.projbuild new file mode 100644 index 000000000..02881e10a --- /dev/null +++ b/components/eppp_link/examples/host/main/Kconfig.projbuild @@ -0,0 +1,43 @@ +menu "Example Configuration" + + config EXAMPLE_GLOBAL_DNS + hex "Set global DNS server" + range 0 0xFFFFFFFF + default 0x08080808 + help + Global DNS server address. + + config EXAMPLE_MQTT + bool "Run mqtt example" + default y + help + Run MQTT client after startup. + + config EXAMPLE_BROKER_URL + string "Broker URL" + depends on EXAMPLE_MQTT + default "mqtt://mqtt.eclipseprojects.io" + help + URL of the broker to connect to. + + config EXAMPLE_ICMP_PING + bool "Run ping example" + default y + help + Ping configured address after startup. + + config EXAMPLE_PING_ADDR + hex "Ping IPv4 address" + depends on EXAMPLE_ICMP_PING + range 0 0xFFFFFFFF + default 0x08080808 + help + Address to send ping requests. + + config EXAMPLE_IPERF + bool "Run iperf" + default y + help + Init and run iperf console. + +endmenu diff --git a/components/eppp_link/examples/host/main/app_main.c b/components/eppp_link/examples/host/main/app_main.c new file mode 100644 index 000000000..765589c55 --- /dev/null +++ b/components/eppp_link/examples/host/main/app_main.c @@ -0,0 +1,203 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include +#include "esp_system.h" +#include "nvs_flash.h" +#include "esp_event.h" +#include "esp_netif.h" +#include "eppp_link.h" +#include "lwip/sockets.h" +#include "esp_log.h" +#include "mqtt_client.h" +#include "ping/ping_sock.h" +#include "esp_console.h" + +void register_iperf(void); + +static const char *TAG = "eppp_host_example"; + +#if CONFIG_EXAMPLE_MQTT +static void mqtt_event_handler(void *args, esp_event_base_t base, int32_t event_id, void *event_data) +{ + ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32 "", base, event_id); + esp_mqtt_event_handle_t event = event_data; + esp_mqtt_client_handle_t client = event->client; + int msg_id; + switch ((esp_mqtt_event_id_t)event_id) { + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); + msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0); + ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); + ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); + break; + + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); + ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_DATA: + ESP_LOGI(TAG, "MQTT_EVENT_DATA"); + printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); + printf("DATA=%.*s\r\n", event->data_len, event->data); + break; + case MQTT_EVENT_ERROR: + ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { + ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno)); + } + break; + default: + ESP_LOGI(TAG, "Other event id:%d", event->event_id); + break; + } +} + +static void mqtt_app_start(void) +{ + esp_mqtt_client_config_t mqtt_cfg = { + .broker.address.uri = CONFIG_EXAMPLE_BROKER_URL, + }; + + esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); + /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ + esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); + esp_mqtt_client_start(client); +} +#endif // MQTT + +#if CONFIG_EXAMPLE_ICMP_PING +static void test_on_ping_success(esp_ping_handle_t hdl, void *args) +{ + uint8_t ttl; + uint16_t seqno; + uint32_t elapsed_time, recv_len; + ip_addr_t target_addr; + esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); + esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len)); + esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time)); + printf("%" PRId32 "bytes from %s icmp_seq=%d ttl=%d time=%" PRId32 " ms\n", + recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time); +} + +static void test_on_ping_timeout(esp_ping_handle_t hdl, void *args) +{ + uint16_t seqno; + ip_addr_t target_addr; + esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + printf("From %s icmp_seq=%d timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno); +} + +static void test_on_ping_end(esp_ping_handle_t hdl, void *args) +{ + uint32_t transmitted; + uint32_t received; + uint32_t total_time_ms; + esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted)); + esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received)); + esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms)); + printf("%" PRId32 " packets transmitted, %" PRId32 " received, time %" PRId32 "ms\n", transmitted, received, total_time_ms); + +} +#endif // PING + +void app_main(void) +{ + ESP_LOGI(TAG, "[APP] Startup.."); + ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); + ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); + + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + /* Sets up the default EPPP-connection + */ + esp_netif_t *eppp_netif = eppp_connect(); + if (eppp_netif == NULL) { + ESP_LOGE(TAG, "Failed to connect"); + return ; + } + // Setup global DNS + esp_netif_dns_info_t dns; + dns.ip.u_addr.ip4.addr = esp_netif_htonl(CONFIG_EXAMPLE_GLOBAL_DNS); + dns.ip.type = ESP_IPADDR_TYPE_V4; + ESP_ERROR_CHECK(esp_netif_set_dns_info(eppp_netif, ESP_NETIF_DNS_MAIN, &dns)); + +#if CONFIG_EXAMPLE_IPERF + esp_console_repl_t *repl = NULL; + esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); + esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); + repl_config.prompt = "iperf>"; + // init console REPL environment + ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl)); + + register_iperf(); + + printf("\n =======================================================\n"); + printf(" | Steps to Test PPP Client Bandwidth |\n"); + printf(" | |\n"); + printf(" | 1. Enter 'help', check all supported commands |\n"); + printf(" | 2. Start PPP server on host system |\n"); + printf(" | - pppd /dev/ttyUSB1 115200 192.168.11.1:192.168.11.2 modem local noauth debug nocrtscts nodetach +ipv6\n"); + printf(" | 3. Wait ESP32 to get IP from PPP server |\n"); + printf(" | 4. Enter 'pppd info' (optional) |\n"); + printf(" | 5. Server: 'iperf -u -s -i 3' |\n"); + printf(" | 6. Client: 'iperf -u -c SERVER_IP -t 60 -i 3' |\n"); + printf(" | |\n"); + printf(" =======================================================\n\n"); + + // start console REPL + ESP_ERROR_CHECK(esp_console_start_repl(repl)); +#endif + +#if CONFIG_EXAMPLE_ICMP_PING + ip_addr_t target_addr = { .type = IPADDR_TYPE_V4, .u_addr.ip4.addr = esp_netif_htonl(CONFIG_EXAMPLE_PING_ADDR) }; + + esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG(); + ping_config.timeout_ms = 2000; + ping_config.interval_ms = 20, + ping_config.target_addr = target_addr; + ping_config.count = 100; // ping in infinite mode + /* set callback functions */ + esp_ping_callbacks_t cbs; + cbs.on_ping_success = test_on_ping_success; + cbs.on_ping_timeout = test_on_ping_timeout; + cbs.on_ping_end = test_on_ping_end; + esp_ping_handle_t ping; + esp_ping_new_session(&ping_config, &cbs, &ping); + /* start ping */ + esp_ping_start(ping); +#endif // PING + +#if CONFIG_EXAMPLE_MQTT + mqtt_app_start(); +#endif +} diff --git a/components/eppp_link/examples/host/main/idf_component.yml b/components/eppp_link/examples/host/main/idf_component.yml new file mode 100644 index 000000000..7ecb517e8 --- /dev/null +++ b/components/eppp_link/examples/host/main/idf_component.yml @@ -0,0 +1,4 @@ +dependencies: + espressif/eppp_link: + version: "*" + override_path: "../../.." diff --git a/components/eppp_link/examples/host/main/register_iperf.c b/components/eppp_link/examples/host/main/register_iperf.c new file mode 100644 index 000000000..d2169c376 --- /dev/null +++ b/components/eppp_link/examples/host/main/register_iperf.c @@ -0,0 +1,183 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "sys/socket.h" // for INADDR_ANY +#include "esp_netif.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_netif.h" +#include "esp_netif_ppp.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" + +#include "esp_console.h" +#include "esp_event.h" +#include "esp_bit_defs.h" +#include "argtable3/argtable3.h" +#include "iperf.h" +#include "sdkconfig.h" + +/* "iperf" command */ + +static struct { + struct arg_str *ip; + struct arg_lit *server; + struct arg_lit *udp; + struct arg_lit *version; + struct arg_int *port; + struct arg_int *length; + struct arg_int *interval; + struct arg_int *time; + struct arg_int *bw_limit; + struct arg_lit *abort; + struct arg_end *end; +} iperf_args; + +static int ppp_cmd_iperf(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **)&iperf_args); + iperf_cfg_t cfg; + + if (nerrors != 0) { + arg_print_errors(stderr, iperf_args.end, argv[0]); + return 0; + } + + memset(&cfg, 0, sizeof(cfg)); + + // ethernet iperf only support IPV4 address + cfg.type = IPERF_IP_TYPE_IPV4; + + /* iperf -a */ + if (iperf_args.abort->count != 0) { + iperf_stop(); + return 0; + } + + if (((iperf_args.ip->count == 0) && (iperf_args.server->count == 0)) || + ((iperf_args.ip->count != 0) && (iperf_args.server->count != 0))) { + ESP_LOGE(__func__, "Wrong mode! ESP32 should run in client or server mode"); + return 0; + } + + /* iperf -s */ + if (iperf_args.ip->count == 0) { + cfg.flag |= IPERF_FLAG_SERVER; + } + /* iperf -c SERVER_ADDRESS */ + else { + cfg.destination_ip4 = esp_ip4addr_aton(iperf_args.ip->sval[0]); + cfg.flag |= IPERF_FLAG_CLIENT; + } + + if (iperf_args.length->count == 0) { + cfg.len_send_buf = 0; + } else { + cfg.len_send_buf = iperf_args.length->ival[0]; + } + + cfg.source_ip4 = INADDR_ANY; + + /* iperf -u */ + if (iperf_args.udp->count == 0) { + cfg.flag |= IPERF_FLAG_TCP; + } else { + cfg.flag |= IPERF_FLAG_UDP; + } + + /* iperf -p */ + if (iperf_args.port->count == 0) { + cfg.sport = IPERF_DEFAULT_PORT; + cfg.dport = IPERF_DEFAULT_PORT; + } else { + if (cfg.flag & IPERF_FLAG_SERVER) { + cfg.sport = iperf_args.port->ival[0]; + cfg.dport = IPERF_DEFAULT_PORT; + } else { + cfg.sport = IPERF_DEFAULT_PORT; + cfg.dport = iperf_args.port->ival[0]; + } + } + + /* iperf -i */ + if (iperf_args.interval->count == 0) { + cfg.interval = IPERF_DEFAULT_INTERVAL; + } else { + cfg.interval = iperf_args.interval->ival[0]; + if (cfg.interval <= 0) { + cfg.interval = IPERF_DEFAULT_INTERVAL; + } + } + + /* iperf -t */ + if (iperf_args.time->count == 0) { + cfg.time = IPERF_DEFAULT_TIME; + } else { + cfg.time = iperf_args.time->ival[0]; + if (cfg.time <= cfg.interval) { + cfg.time = cfg.interval; + } + } + + /* iperf -b */ + if (iperf_args.bw_limit->count == 0) { + cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT; + } else { + cfg.bw_lim = iperf_args.bw_limit->ival[0]; + if (cfg.bw_lim <= 0) { + cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT; + } + } + + printf("mode=%s-%s sip=" IPSTR ":%" PRIu16 ", dip=%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ":%" PRIu16 ", interval=%" PRIu32 ", time=%" PRIu32 "\r\n", + cfg.flag & IPERF_FLAG_TCP ? "tcp" : "udp", + cfg.flag & IPERF_FLAG_SERVER ? "server" : "client", + (uint16_t) cfg.source_ip4 & 0xFF, + (uint16_t) (cfg.source_ip4 >> 8) & 0xFF, + (uint16_t) (cfg.source_ip4 >> 16) & 0xFF, + (uint16_t) (cfg.source_ip4 >> 24) & 0xFF, + cfg.sport, + cfg.destination_ip4 & 0xFF, (cfg.destination_ip4 >> 8) & 0xFF, + (cfg.destination_ip4 >> 16) & 0xFF, (cfg.destination_ip4 >> 24) & 0xFF, cfg.dport, + cfg.interval, cfg.time); + + iperf_start(&cfg); + return 0; +} + +void register_iperf(void) +{ + + iperf_args.ip = arg_str0("c", "client", "", + "run in client mode, connecting to "); + iperf_args.server = arg_lit0("s", "server", "run in server mode"); + iperf_args.udp = arg_lit0("u", "udp", "use UDP rather than TCP"); + iperf_args.version = arg_lit0("V", "ipv6_domain", "use IPV6 address rather than IPV4"); + iperf_args.port = arg_int0("p", "port", "", + "server port to listen on/connect to"); + iperf_args.length = arg_int0("l", "len", "", "set read/write buffer size"); + iperf_args.interval = arg_int0("i", "interval", "", + "seconds between periodic bandwidth reports"); + iperf_args.time = arg_int0("t", "time", "