net:apps: zperf application

zperf is a network traffic generator for Zephyr.
Same application is able to work dynamically in client or server mode.
It allows to assess network bandwidth.
zperf is compliant with iperf_2.0.5.
zperf can be run in micro or nano kernel.

Change-Id: Icbd69e1ad56ad29c678b098e7a2e07c44f90e48e
Signed-off-by: jgarcia <jeremie.garcia@intel.com>
This commit is contained in:
jgarcia 2016-04-14 17:44:23 +02:00 committed by Jukka Rissanen
parent c36daba63d
commit 7b8d89d921
13 changed files with 1213 additions and 0 deletions

View file

@ -0,0 +1,6 @@
MDEF_FILE = prj.mdef
KERNEL_TYPE = micro
BOARD ?= qemu_x86
CONF_FILE = prj.conf
include ${ZEPHYR_BASE}/Makefile.inc

7
samples/net/zperf/README Normal file
View file

@ -0,0 +1,7 @@
Desc:
=====
zperf is a network traffic generator for Zephyr.
Same application is able to work dynamically in client or server mode.
It allows to assess network bandwidth.
zperf is compliant with iperf_2.0.5.
zperf can be run in micro or nano kernel.

1
samples/net/zperf/prj.conf Symbolic link
View file

@ -0,0 +1 @@
prj_galileo_ethernet.conf

View file

@ -0,0 +1,5 @@
% Application : Zperf
% TASK NAME PRIO ENTRY STACK GROUPS
% ==================================
TASK MAIN 7 mainloop 2048 [EXE]

View file

@ -0,0 +1,30 @@
#
#console
#
CONFIG_STDOUT_CONSOLE=y
CONFIG_CONSOLE_HANDLER=y
CONFIG_CONSOLE_HANDLER_SHELL=y
CONFIG_PRINTK=y
CONFIG_MINIMAL_LIBC_EXTENDED=y
#
# networking
#
CONFIG_NETWORKING=y
CONFIG_IP_BUF_RX_SIZE=5
CONFIG_IP_BUF_TX_SIZE=5
CONFIG_NETWORKING_WITH_IPV4=y
#CONFIG_NETWORKING_WITH_LOGGING=y
#CONFIG_NETWORK_IP_STACK_DEBUG_NET_BUF=y
#CONFIG_NETWORK_IP_STACK_DEBUG_IPV6=y
#CONFIG_NETWORK_IP_STACK_DEBUG_IPV6_DS=y
#CONFIG_NETWORK_IP_STACK_DEBUG_IPV6_ICMPV6=y
#CONFIG_NETWORK_IP_STACK_DEBUG_IPV6_ND=y
#CONFIG_NETWORK_IP_STACK_DEBUG_IPV6_NBR_CACHE=y
#CONFIG_NETWORK_IP_STACK_DEBUG_IPV6_ROUTE=y
#
# Ethernet
#
CONFIG_ETHERNET=y
#CONFIG_ETHERNET_DEBUG=y
CONFIG_ETH_DW=y
CONFIG_PCI_ENUMERATION=y

View file

@ -0,0 +1,9 @@
ccflags-y += -I${srctree}/net/ip/contiki
ccflags-y += -I${srctree}/net/ip/contiki/os/lib
ccflags-y += -I${srctree}/net/ip/contiki/os
ccflags-y += -I${srctree}/net/ip
obj-y += zperf_shell.o
obj-y += shell_utils.o
obj-y += zperf_uploader.o
obj-y += zperf_receiver.o

View file

@ -0,0 +1,161 @@
/*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <ctype.h>
#include <misc/printk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <zephyr.h>
#include "shell_utils.h"
#ifdef CONFIG_NETWORKING_WITH_IPV6
#define BASE 16
#define SEPARATOR ':'
#else
#define BASE 10
#define SEPARATOR '.'
#endif
int parseIpString(char *str, int res[])
{
uint32_t index = 0;
if (str == NULL) {
printk("ERROR! null str detected\n");
return -1;
}
#ifdef CONFIG_NETWORKING_WITH_IPV4
if (strlen(str) > IPV4_STR_LEN_MAX
|| strlen(str) < IPV4_STR_LEN_MIN) {
printk("ERROR! invalid IP address\n");
return -1;
}
#endif
for (int i = 0; i < IP_INDEX_MAX; i++)
res[i] = 0;
while (*str) {
if (isdigit((unsigned char)*str)) {
res[index] *= BASE;
res[index] += *str - '0';
#ifdef CONFIG_NETWORKING_WITH_IPV4
if (res[index] > 255) {
printk("ERROR! invalid IP address\n");
return -1;
}
#endif
}
#ifdef CONFIG_NETWORKING_WITH_IPV6
else if (isalpha((unsigned char)*str)) {
int digit = *str - (isupper(*str) ? 'A' - 10 : 'a' - 10);
if (digit > BASE) {
printk("ERROR! invalid IP address\n");
return -1;
}
res[index] *= BASE;
res[index] += digit;
}
#endif
else if ((unsigned char) *str == SEPARATOR) {
#ifdef CONFIG_NETWORKING_WITH_IPV6
if (str[1] == SEPARATOR) {
printk("ERROR! I am sorry but I am not yet able to understand "
"the :: syntax!!\n");
printk("ERROR! Please expand your @IP or enhance me!!\n");
return -1;
}
#endif
index++;
if (index >= IP_INDEX_MAX) {
printk("ERROR! invalid IP address\n");
return -1;
}
} else {
printk("ERROR! invalid IP address format\n");
return -1;
}
str++;
}
return 0;
}
void print_address(int *value)
{
#ifdef CONFIG_NETWORKING_WITH_IPV6
printk("%x:%x:%x:%x:%x:%x:%x:%x",
value[0], value[1], value[2], value[3],
value[4], value[5], value[6], value[7]);
#else
printk("%d.%d.%d.%d", value[0], value[1], value[2], value[3]);
#endif
}
const int TIME_US[] = { 60 * 1000 * 1000, 1000 * 1000, 1000, 0 };
const char *TIME_US_UNIT[] = { "m", "s", "ms", "us" };
const int KBPS[] = { 1024, 0 };
const char *KBPS_UNIT[] = { "Mbps", "Kbps" };
const int K[] = { 1024 * 1024, 1024, 0 };
const char *K_UNIT[] = { "M", "K", "" };
void print_number(uint32_t value, const uint32_t *divisor,
const char **units)
{
const char **unit;
const uint32_t *div;
uint32_t dec, radix;
unit = units;
div = divisor;
while (value < *div) {
div++;
unit++;
}
if (*div != 0) {
radix = value / *div;
dec = (value % *div) * 100 / *div;
printk("%u.%s%u %s", radix, (dec < 10) ? "0" : "", dec, *unit);
} else {
printk("%u %s", value, *unit);
}
}
long parse_number(const char *string, const uint32_t *divisor,
const char **units)
{
const char **unit;
const uint32_t *div;
char *suffix;
long dec;
int cmp;
dec = strtoul(string, &suffix, 10);
unit = units;
div = divisor;
do {
cmp = strncasecmp(suffix, *unit++, 1);
} while (cmp != 0 && *++div != 0);
return (*div == 0) ? dec : dec * *div;
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __SHELL_UTILS_H
#define __SHELL_UTILS_H
#ifdef CONFIG_NETWORKING_WITH_IPV6
#define IP_INDEX_MAX 8
#else
#define IP_INDEX_MAX 4
#endif
#define IPV4_STR_LEN_MAX 15
#define IPV4_STR_LEN_MIN 7
extern const int TIME_US[];
extern const char *TIME_US_UNIT[];
extern const int KBPS[];
extern const char *KBPS_UNIT[];
extern const int K[];
extern const char *K_UNIT[];
extern int parseIpString(char *str, int res[]);
extern void print_address(int *value);
extern void print_number(uint32_t value, const uint32_t *divisor,
const char **units);
extern long parse_number(const char *string, const uint32_t *divisor,
const char **units);
#endif /* __SHELL_UTILS_H */

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __ZPERF_H
#define __ZPERF_H
#define VERSION "1.0"
typedef struct zperf_results {
uint32_t nb_packets_sent;
uint32_t nb_packets_rcvd;
uint32_t nb_packets_lost;
uint32_t nb_packets_outorder;
uint32_t nb_bytes_sent;
uint32_t time_in_us;
uint32_t jitter_in_us;
uint32_t client_time_in_us;
} zperf_results;
typedef void (*zperf_callback)(int status, zperf_results*);
#define IPV4_STR_LEN_MAX 15
#define IPV4_STR_LEN_MIN 7
#endif /* __ZPERF_H */

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __ZPERF_INTERNAL_H
#define __ZPERF_INTERNAL_H
#include <limits.h>
#include <misc/byteorder.h>
/* Constants */
#define PACKET_SIZE_MAX 1024
/* Macro */
#define HW_CYCLES_TO_USEC(__hw_cycle__) \
( \
((uint64_t)(__hw_cycle__) * (uint64_t)sys_clock_us_per_tick) / \
((uint64_t)sys_clock_hw_cycles_per_tick) \
)
#define HW_CYCLES_TO_SEC(__hw_cycle__) \
( \
((uint64_t)(HW_CYCLES_TO_USEC(__hw_cycle__))) / \
((uint64_t)USEC_PER_SEC) \
)
#define USEC_TO_HW_CYCLES(__usec__) \
( \
((uint64_t)(__usec__) * (uint64_t)sys_clock_hw_cycles_per_tick) / \
((uint64_t)sys_clock_us_per_tick) \
)
#define SEC_TO_HW_CYCLES(__sec__) \
USEC_TO_HW_CYCLES((uint64_t)(__sec__) * \
(uint64_t)USEC_PER_SEC)
#define MSEC_TO_HW_CYCLES(__msec__) \
USEC_TO_HW_CYCLES((uint64_t)(__msec__) * \
(uint64_t)MSEC_PER_SEC)
/* Types */
typedef struct zperf_udp_datagram {
int32_t id;
uint32_t tv_sec;
uint32_t tv_usec;
} zperf_udp_datagram;
typedef struct zperf_server_hdr {
int32_t flags;
int32_t total_len1;
int32_t total_len2;
int32_t stop_sec;
int32_t stop_usec;
int32_t error_cnt;
int32_t outorder_cnt;
int32_t datagrams;
int32_t jitter1;
int32_t jitter2;
} zperf_server_hdr;
/* Inline functions */
static inline uint32_t time_delta(uint32_t ts, uint32_t t)
{
return (t >= ts) ? (t - ts) : (ULONG_MAX - ts + t);
}
/* byte order */
#define z_htonl(val) sys_cpu_to_be32(val)
#define z_ntohl(val) sys_be32_to_cpu(val)
/* internal functions */
extern void zperf_upload(struct net_context *net_context,
unsigned int duration_in_ms, unsigned int packet_size,
unsigned int rate_in_kbps, zperf_results *results);
extern void zperf_upload_fin(struct net_context *net_context,
uint32_t nb_packets, uint32_t end_time, uint32_t packet_size,
zperf_results *results);
extern void zperf_upload_decode_stat(struct net_buf *net_stat,
zperf_results *results);
extern void zperf_receiver_init(int port);
extern void connect_ap(char *ssid);
#endif /* __ZPERF_INTERNAL_H */

View file

@ -0,0 +1,305 @@
/*
* Copyright (c) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <misc/printk.h>
#include <net/net_core.h>
#include <net/net_socket.h>
#include <net/ip_buf.h>
#include <net/net_ip.h>
#include <sections.h>
#include <toolchain.h>
#include <zephyr.h>
#include "zperf.h"
#include "zperf_internal.h"
#include "shell_utils.h"
#define TAG "[udp.download] "
#define RX_FIBER_STACK_SIZE 1024
#define SESSION_MAX 4
/* Type definition */
enum state {
STATE_NULL, /* Session has not yet started */
STATE_ONGOING, /* 1st packet has been received, last packet not yet */
STATE_LAST_PACKET_RECEIVED, /* Last packet has been received */
STATE_COMPLETED /* Session completed, stats pkt can be sent if needed */
};
struct session {
/* Tuple */
uint16_t port;
uip_ipaddr_t ip;
enum state state;
/* Stat data */
uint32_t counter;
uint32_t next_id;
uint32_t outorder;
uint32_t error;
uint64_t length;
uint32_t start_time;
uint32_t last_time;
int32_t jitter;
int32_t last_transit_time;
/* Stats packet*/
struct zperf_server_hdr stat;
};
/* Static data */
static char __noinit __stack zperf_rx_fiber_stack[RX_FIBER_STACK_SIZE];
static struct net_addr in_addr_any = {
#ifdef CONFIG_NETWORKING_WITH_IPV6
.family = AF_INET6,
.in6_addr = IN6ADDR_ANY_INIT
#else
.family = AF_INET, .in_addr = { { { 0 } } }
#endif
};
static struct net_addr in_addr_my = {
#ifdef CONFIG_NETWORKING_WITH_IPV6
.family = AF_INET6,
.in6_addr = IN6ADDR_ANY_INIT
#else
.family = AF_INET, .in_addr = { { { 0 } } }
#endif
};
static struct session sessions[SESSION_MAX];
/* Get session from a given packet */
static struct session *get_session(struct net_buf *buf)
{
uint16_t port;
uip_ipaddr_t ip;
int i = 0;
struct session *active = NULL;
struct session *free = NULL;
/* Get tuple of the remote connection */
port = NET_BUF_UDP(buf)->srcport;
uip_ipaddr_copy(&ip, &NET_BUF_IP(buf)->srcipaddr);
/* Check whether we already have an active session */
while (active == NULL && i < SESSION_MAX) {
struct session *ptr = &sessions[i];
if (ptr->port == port && uip_ipaddr_cmp(&ptr->ip, &ip)) {
/* We found an active session */
active = ptr;
} else if (free == NULL
&& (ptr->state == STATE_NULL
|| ptr->state == STATE_COMPLETED)) {
/* We found a free slot - just in case */
free = ptr;
}
i++;
}
/* If no active session then create a new one */
if (active == NULL && free != NULL) {
active = free;
active->port = port;
uip_ipaddr_copy(&active->ip, &ip);
}
return active;
}
/* Send statistic to the remote client */
static int zperf_receiver_send_stat(struct net_context *net_context,
struct net_buf *buf, zperf_server_hdr *stat)
{
/* Fill the packet header */
memcpy(ip_buf_appdata(buf) + sizeof(zperf_udp_datagram), stat,
sizeof(zperf_server_hdr));
/* Send the packet */
return net_reply(net_context, buf);
}
/* RX fiber entry point */
static void zperf_rx_fiber(int port)
{
struct net_context *net_context = net_context_get(IPPROTO_UDP, &in_addr_any,
0, &in_addr_my, port);
if (!net_context) {
printk(TAG "ERROR! Cannot get network context.\n");
return;
}
while (1) {
struct net_buf *buf = net_receive(net_context, TICKS_UNLIMITED);
if (buf == NULL) {
continue;
} else if (ip_buf_appdatalen(buf) < sizeof(zperf_udp_datagram)) {
/* Release the buffer */
ip_buf_unref(buf);
} else {
uint32_t time = sys_cycle_get_32();
int32_t id;
int32_t transit_time;
struct session *session = get_session(buf);
if (session == NULL) {
printk(TAG "ERROR! cannot create a session!\n");
continue;
}
zperf_udp_datagram *hdr = (zperf_udp_datagram *) ip_buf_appdata(buf);
id = z_ntohl(hdr->id);
/* Check last packet flags */
if (id < 0) {
printk(TAG "End of session!\n");
if (session->state == STATE_COMPLETED) {
/* Session is already completed: Resend the stat buffer
* and continue
*/
if (zperf_receiver_send_stat(net_context, buf,
&session->stat) < 0) {
printk(TAG "ERROR! Failed to send the buffer\n");
/* Free the buffer */
ip_buf_unref(buf);
}
continue;
} else {
session->state = STATE_LAST_PACKET_RECEIVED;
id = -id;
}
} else if (session->state != STATE_ONGOING) {
/* Start a new session! */
printk(TAG "New session started.\n");
/* Reset statistic */
session->state = STATE_ONGOING;
session->counter = 0;
session->start_time = time;
session->next_id = 0;
session->length = 0;
session->outorder = 0;
session->error = 0;
session->jitter = 0;
session->last_transit_time = 0;
}
/* Check header id */
if (id != session->next_id) {
if (id < session->next_id) {
session->outorder++;
} else {
session->error += id - session->next_id;
session->next_id = id + 1;
}
} else {
session->next_id++;
}
/* Update counter */
session->counter++;
session->length += ip_buf_appdatalen(buf);
/* Compute jitter */
transit_time = time_delta(HW_CYCLES_TO_USEC(time),
z_ntohl(hdr->tv_sec) * USEC_PER_SEC + z_ntohl(hdr->tv_usec));
if (session->last_transit_time != 0) {
int32_t delta_transit = transit_time
- session->last_transit_time;
delta_transit =
(delta_transit < 0) ? -delta_transit : delta_transit;
session->jitter += (delta_transit - session->jitter) / 16;
}
session->last_transit_time = transit_time;
/* If necessary send statistic */
if (session->state == STATE_LAST_PACKET_RECEIVED) {
uint32_t rate_in_kbps;
uint32_t duration = HW_CYCLES_TO_USEC(
time_delta(session->start_time, time));
/* Update state machine */
session->state = STATE_COMPLETED;
/* Compute baud rate */
if (duration != 0)
rate_in_kbps = (uint32_t) (((uint64_t) session->length
* (uint64_t) 8 * (uint64_t) USEC_PER_SEC)
/ ((uint64_t) duration * 1024));
else
rate_in_kbps = 0;
/* Fill static */
session->stat.flags = z_htonl(0x80000000);
session->stat.total_len1 = z_htonl(session->length >> 32);
session->stat.total_len2 = z_htonl(
session->length % 0xFFFFFFFF);
session->stat.stop_sec = z_htonl(duration / USEC_PER_SEC);
session->stat.stop_usec = z_htonl(duration % USEC_PER_SEC);
session->stat.error_cnt = z_htonl(session->error);
session->stat.outorder_cnt = z_htonl(session->outorder);
session->stat.datagrams = z_htonl(session->counter);
session->stat.jitter1 = 0;
session->stat.jitter2 = z_htonl(session->jitter);
if (zperf_receiver_send_stat(net_context, buf, &session->stat)
< 0) {
printk(TAG "ERROR! Failed to send the buffer\n");
/* Free the buffer */
ip_buf_unref(buf);
}
printk(TAG " duration:\t\t");
print_number(duration, TIME_US, TIME_US_UNIT);
printk("\n");
printk(TAG " received packets:\t%u\n", session->counter);
printk(TAG " nb packets lost:\t%u\n", session->outorder);
printk(TAG " nb packets outorder:\t%u\n", session->error);
printk(TAG " jitter:\t\t\t");
print_number(session->jitter, TIME_US, TIME_US_UNIT);
printk("\n");
printk(TAG " rate:\t\t\t");
print_number(rate_in_kbps, KBPS, KBPS_UNIT);
printk("\n");
} else {
/* Free the buffer */
ip_buf_unref(buf);
}
}
}
}
void zperf_receiver_init(int port)
{
for (int i = 0; i < SESSION_MAX; i++)
sessions[i].state = STATE_NULL;
fiber_start(zperf_rx_fiber_stack, sizeof(zperf_rx_fiber_stack),
(nano_fiber_entry_t) zperf_rx_fiber, port, 0, 7, 0);
}

View file

@ -0,0 +1,295 @@
/*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <ctype.h>
#include <misc/printk.h>
#include <misc/shell.h>
#include <net/ip_buf.h>
#include <net/net_core.h>
#include <net/net_socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <zephyr.h>
#include "zperf.h"
#include "zperf_internal.h"
#include "shell_utils.h"
#define DEVICE_NAME "zperf shell"
static const char *CONFIG = ""
#ifdef CONFIG_MICROKERNEL
"microkernel "
#else
"nanokernel "
#endif
#ifdef CONFIG_ETH_DW
"ethernet "
#endif
#ifdef CONFIG_NETWORKING_WITH_IPV4
"ipv4 "
#endif
#ifdef CONFIG_NETWORKING_WITH_IPV6
"ipv6 "
#endif
"";
static struct net_addr in_addr_my = {
#ifdef CONFIG_NETWORKING_WITH_IPV6
.family = AF_INET6,
.in6_addr = IN6ADDR_ANY_INIT
#else
.family = AF_INET,
.in_addr = { { { 0 } } }
#endif
};
#define MY_SRC_PORT 50000
static void shell_cmd_setip(int argc, char *argv[])
{
#ifdef CONFIG_NETWORKING_WITH_IPV4
uint32_t value[4] = {0};
if (argc != 2) {
/* Print usage */
printk("\nsetip:\n");
printk("Usage:\tsetip <my ip>\n");
printk("\nExample setip 10.237.164.178\n");
return;
}
if (parseIpString(argv[1], value) == 0) {
printk("[setip] Setting IP address %d.%d.%d.%d\n",
value[0], value[1], value[2], value[3]);
uip_ipaddr_t uip_ipaddr_local;
uip_ipaddr(&uip_ipaddr_local,
value[0],
value[1],
value[2],
value[3]);
uip_sethostaddr(&uip_ipaddr_local);
} else {
printk("[setip] ERROR! Unable to set IP\n");
}
#endif
}
static void shell_cmd_udp_download(int argc, char *argv[])
{
static bool stopped = true;
int port;
if (argc == 1) {
/* Print usage */
printk("\nudp.download:\n");
printk("Usage:\tudp.download <port>\n");
printk("\nExample udp.download 5001\n");
return;
}
if (argc > 1) {
port = strtoul(argv[1], NULL, 10);
} else {
port = 5001;
}
if (stopped == false) {
printk("[udp.download] ERROR! UDP server already started!\n");
return;
}
zperf_receiver_init(port);
stopped = false;
printk("[udp.download] UDP server started on port %u\n", port);
}
static void shell_cmd_udp_upload(int argc, char *argv[])
{
int value[IP_INDEX_MAX] = { 0 };
static struct net_addr net_addr_remote = { .family = AF_INET, .in_addr = { {
{ 192, 168, 43, 181 } } } };
unsigned int duration_in_ms, packet_size, rate_in_kbps, client_rate_in_kbps;
uint16_t port;
zperf_results results = { 0 };
struct net_context *net_context;
if (argc == 1) {
/* Print usage */
printk("\nudp.upload:\n");
printk(
"Usage:\tudp.upload <dest ip> <dest port> <duration> <packet "
"size>[K] <baud rate>[K|M]\n");
printk("\t<dest ip>:\tIP destination\n");
printk("\t<dest port>:\tport destination\n");
printk("\t<duration>:\t of the test in seconds\n");
printk(
"\t<packet size>:\tSize of the paket in byte or kilobyte "
"(with suffix K)\n");
printk("\t<baud rate>:\tBaudrate in kilobyte or megabyte\n");
printk("\nExample udp.upload 10.237.164.178 1111 1 1K 1M\n");
return;
}
if (argc > 1 && parseIpString(argv[1], value) == 0) {
printk("[udp.upload] Remote IP address is ");
print_address(value);
printk("\n");
#ifdef CONFIG_NETWORKING_WITH_IPV6
for (int i = 0; i < IP_INDEX_MAX; i++) {
net_addr_remote.in6_addr.s6_addr[i] = (value[i] & 0xFF00) >> 8;
net_addr_remote.in6_addr.s6_addr[i] = value[i] & 0xFF;
}
#else
net_addr_remote.in_addr.s4_addr[0] = value[0];
net_addr_remote.in_addr.s4_addr[1] = value[1];
net_addr_remote.in_addr.s4_addr[2] = value[2];
net_addr_remote.in_addr.s4_addr[3] = value[3];
#endif
} else {
printk(
"[udp.upload] ERROR! Please specify the IP address of the"
" remote server\n");
}
if (argc > 2) {
port = strtoul(argv[2], NULL, 10);
printk("[udp.upload] Remote port is %u\n", port);
} else {
port = 5001;
}
net_context = net_context_get(IPPROTO_UDP, &net_addr_remote, port,
&in_addr_my, MY_SRC_PORT);
if (!net_context) {
printk("[udp.upload] ERROR! Fail to retrieve a net context\n");
return;
}
if (argc > 3)
duration_in_ms = strtoul(argv[3], NULL, 10) * MSEC_PER_SEC;
else
duration_in_ms = 1000;
if (argc > 4)
packet_size = parse_number(argv[4], K, K_UNIT);
else
packet_size = 256;
if (argc > 5)
rate_in_kbps = (parse_number(argv[5], K, K_UNIT) + 1023) / 1024;
else
rate_in_kbps = 10;
/* Print settings */
printk("[udp.upload] duration:\t\t");
print_number(duration_in_ms * USEC_PER_MSEC, TIME_US, TIME_US_UNIT);
printk("\n");
printk("[udp.upload] packet size:\t%u bytes\n", packet_size);
printk("[udp.upload] rate:\t\t");
print_number(rate_in_kbps, KBPS, KBPS_UNIT);
printk("\n");
printk("[udp.upload] start...\n");
/* Perform upload test */
zperf_upload(net_context, duration_in_ms, packet_size, rate_in_kbps,
&results);
printk("[udp.upload] completed!\n");
/* Print results */
if (results.time_in_us != 0)
rate_in_kbps = (uint32_t) (((uint64_t) results.nb_bytes_sent
* (uint64_t) 8 * (uint64_t) USEC_PER_SEC)
/ ((uint64_t) results.time_in_us * 1024));
else
rate_in_kbps = 0;
if (results.client_time_in_us != 0)
client_rate_in_kbps = (uint32_t) (((uint64_t) results.nb_packets_sent
* (uint64_t) packet_size * (uint64_t) 8
* (uint64_t) USEC_PER_SEC)
/ ((uint64_t) results.client_time_in_us * 1024));
else
client_rate_in_kbps = 0;
if (!rate_in_kbps)
printk("[udp.upload] LAST PACKET NOT RECEIVED!!!\n");
printk("[udp.upload] statistic:\t\t\tserver\t(client)\n");
printk("[udp.upload] duration:\t\t\t");
print_number(results.time_in_us, TIME_US, TIME_US_UNIT);
printk("\t(");
print_number(results.client_time_in_us, TIME_US, TIME_US_UNIT);
printk(")\n");
printk("[udp.upload] nb packets:\t\t%u\t(%u)\n", results.nb_packets_rcvd,
results.nb_packets_sent);
printk("[udp.upload] nb packets outorder:\t%u\n",
results.nb_packets_outorder);
printk("[udp.upload] nb packets lost:\t\t%u\n", results.nb_packets_lost);
printk("[udp.upload] jitter:\t\t\t");
print_number(results.jitter_in_us, TIME_US, TIME_US_UNIT);
printk("\n");
printk("[udp.upload] rate:\t\t\t");
print_number(rate_in_kbps, KBPS, KBPS_UNIT);
printk("\t(");
print_number(client_rate_in_kbps, KBPS, KBPS_UNIT);
printk(")\n");
/* release net context */
net_context_put(net_context);
}
static void shell_cmd_connectap(int argc, char *argv[])
{
printk("[connectap] Zephyr has not been built with Wi-Fi support.\n");
}
static void shell_cmd_version(int argc, char *argv[])
{
printk("\nZPerf version: %s config: %s\n", VERSION, CONFIG);
}
struct shell_cmd commands[] = {
{ "setip", shell_cmd_setip },
{ "connectap", shell_cmd_connectap },
{ "version", shell_cmd_version },
{ "udp.upload", shell_cmd_udp_upload },
{ "udp.download", shell_cmd_udp_download },
{ NULL, NULL } };
#ifdef CONFIG_MICROKERNEL
void mainloop(void)
{
#else
void main(void)
{
#endif
printk("\nZPerf version: %s config: %s\n", VERSION, CONFIG);
shell_init("zperf> ", commands);
net_init();
}

View file

@ -0,0 +1,222 @@
/*
* Copyright (c) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <misc/printk.h>
#include <net/ip_buf.h>
#include <net/net_ip.h>
#include <net/net_core.h>
#include <net/net_socket.h>
#include <zephyr.h>
#include "zperf.h"
#include "zperf_internal.h"
#define TAG "[udp.upload] "
void zperf_upload(struct net_context *net_context,
unsigned int duration_in_ms,
unsigned int packet_size, unsigned int rate_in_kbps,
zperf_results *results)
{
uint32_t print_interval = SEC_TO_HW_CYCLES(1);
uint32_t duration = MSEC_TO_HW_CYCLES(duration_in_ms);
uint32_t packet_duration = (uint32_t) (((uint64_t) packet_size
* SEC_TO_HW_CYCLES(1) * 8) / (uint64_t) (rate_in_kbps * 1024));
uint32_t nb_packets = 0;
uint32_t start_time, last_print_time, last_loop_time, end_time;
uint32_t delay = packet_duration;
if (packet_size > PACKET_SIZE_MAX) {
printk(TAG "WARNING! packet size too large! max size: %u\n",
PACKET_SIZE_MAX);
packet_size = PACKET_SIZE_MAX;
} else if (packet_size < sizeof(zperf_udp_datagram)) {
printk(TAG "WARNING! packet size set to the min size: %u\n",
sizeof(zperf_udp_datagram));
packet_size = sizeof(zperf_udp_datagram);
}
/* Start the loop */
start_time = sys_cycle_get_32();
last_print_time = start_time;
last_loop_time = start_time;
do {
int32_t adjust;
uint32_t loop_time;
zperf_udp_datagram *datagram;
/* Timestamp */
loop_time = sys_cycle_get_32();
/* Algorithm to maintain a given baud rate */
if (last_loop_time != loop_time) {
adjust = packet_duration - time_delta(last_loop_time, loop_time);
} else {
adjust = 0; /* It's the first iteration so no need for adjustment */
}
if (adjust >= 0 || -adjust < delay) {
delay += adjust;
} else {
delay = 0; /* delay should never be a negative value */
}
last_loop_time = loop_time;
/* Get a new TX buffer */
struct net_buf *buf = ip_buf_get_tx(net_context);
if (!buf) {
printk(TAG "ERROR! Failed to retrieve a buffer\n");
continue;
}
/* Fill the packet header */
datagram = (zperf_udp_datagram *) net_buf_add(buf,
sizeof(zperf_udp_datagram));
datagram->id = z_htonl(nb_packets);
datagram->tv_sec = z_htonl(HW_CYCLES_TO_SEC(loop_time));
datagram->tv_usec = z_htonl(
HW_CYCLES_TO_USEC(loop_time) % USEC_PER_SEC);
/* Fill the remain part of the datagram */
if (packet_size > sizeof(zperf_udp_datagram)) {
int size = packet_size - sizeof(zperf_udp_datagram);
uint8_t *ptr = net_buf_add(buf, size);
memset(ptr, 'z', size);
}
/* Send the packet */
if (net_send(buf) < 0) {
printk(TAG "ERROR! Failed to send the buffer\n");
ip_buf_unref(buf);
continue;
} else {
nb_packets++;
}
/* Print log every seconds */
if (time_delta(last_print_time, loop_time) > print_interval) {
printk(TAG "nb_packets=%u\tdelay=%u\tadjust=%d\n", nb_packets,
delay, adjust);
last_print_time = loop_time;
}
/* Wait */
while (time_delta(loop_time, sys_cycle_get_32()) < delay) {
fiber_yield();
}
} while (time_delta(start_time, last_loop_time) < duration);
end_time = sys_cycle_get_32();
zperf_upload_fin(net_context, nb_packets, end_time, packet_size, results);
/* Add result coming from the client */
results->nb_packets_sent = nb_packets;
results->client_time_in_us = HW_CYCLES_TO_USEC(
time_delta(start_time, end_time));
}
void zperf_upload_fin(struct net_context *net_context, uint32_t nb_packets,
uint32_t end_time, uint32_t packet_size, zperf_results *results)
{
struct net_buf *net_stat = NULL;
int loop = 2;
while (net_stat == NULL && loop-- > 0) {
zperf_udp_datagram *datagram;
/* Get a new TX buffer */
struct net_buf *buf = ip_buf_get_tx(net_context);
if (!buf) {
printk(TAG "ERROR! Failed to retrieve a buffer\n");
continue;
}
/* Fill the packet header */
datagram = (zperf_udp_datagram *) net_buf_add(buf,
sizeof(zperf_udp_datagram));
datagram->id = z_htonl(-nb_packets);
datagram->tv_sec = z_htonl(HW_CYCLES_TO_SEC(end_time));
datagram->tv_usec = z_htonl(HW_CYCLES_TO_USEC(end_time) % USEC_PER_SEC);
/* Fill the remain part of the datagram */
if (packet_size > sizeof(zperf_udp_datagram)) {
int size = packet_size - sizeof(zperf_udp_datagram);
uint8_t *ptr = net_buf_add(buf, size);
memset(ptr, 'z', size);
}
/* Send the packet */
if (net_send(buf) < 0) {
printk(TAG "ERROR! Failed to send the buffer\n");
ip_buf_unref(buf);
continue;
}
/* Receive statistic */
net_stat = net_receive(net_context, 2 * sys_clock_ticks_per_sec);
}
/* Decode statistic */
if (net_stat != NULL) {
zperf_upload_decode_stat(net_stat, results);
/* Free the buffer */
ip_buf_unref(net_stat);
}
/* Drain RX fifo */
while (net_stat != NULL) {
net_stat = net_receive(net_context, 2 * sys_clock_ticks_per_sec);
ip_buf_unref(net_stat);
if (net_stat != NULL)
printk(TAG "Drain one spurious stat packet!\n");
}
}
void zperf_upload_decode_stat(struct net_buf *net_stat,
zperf_results *results)
{
/* Decode stat */
if (net_stat == NULL) {
printk(TAG "ERROR! Failed to receive statistic\n");
} else if (ip_buf_appdatalen(net_stat)
< sizeof(zperf_server_hdr) + sizeof(zperf_udp_datagram)) {
printk(TAG "ERROR! Statistic too small\n");
} else {
zperf_server_hdr *hdr;
hdr = (zperf_server_hdr *) (ip_buf_appdata(net_stat)
+ sizeof(zperf_udp_datagram));
/* Fill the results struct */
results->nb_packets_rcvd = z_ntohl(hdr->datagrams);
results->nb_packets_lost = z_ntohl(hdr->error_cnt);
results->nb_packets_outorder = z_ntohl(hdr->outorder_cnt);
results->nb_bytes_sent = z_ntohl(hdr->total_len2);
results->time_in_us = z_ntohl(
hdr->stop_usec) + z_ntohl(hdr->stop_sec) * USEC_PER_SEC;
results->jitter_in_us = z_ntohl(
hdr->jitter2) + z_ntohl(hdr->jitter1) * USEC_PER_SEC;
}
}