291 lines
8.2 KiB
C
291 lines
8.2 KiB
C
#include "utils.h"
|
|
#include <pico_ipv4.h>
|
|
#include <pico_ipv6.h>
|
|
#include <pico_socket.h>
|
|
|
|
/*** START UDP CLIENT ***/
|
|
/*
|
|
* udpclient expects the following format: udpclient:dest_addr:sendto_port[:listen_port:datasize:loops:subloops]
|
|
* dest_addr: IP address to send datagrams to
|
|
* sendto_port: port number to send datagrams to
|
|
* listen_port [OPTIONAL]: port number on which the udpclient listens
|
|
* datasize [OPTIONAL]: size of the data given to the socket in one go
|
|
* loops [OPTIONAL]: number of intervals in which data is send
|
|
* subloops [OPTIONAL]: number of sends in one interval
|
|
*
|
|
* REMARK: once an optional parameter is given, all optional parameters need a value!
|
|
*
|
|
* f.e.: ./build/test/picoapp.elf --vde pic0:/tmp/pic0.ctl:10.40.0.2:255.255.255.0: -a udpclient:10.40.0.3:6667:6667:1400:100:10
|
|
*/
|
|
|
|
struct udpclient_pas *udpclient_pas;
|
|
|
|
static int exit_retry = 0;
|
|
|
|
static void request_exit_echo(pico_time now, void *arg)
|
|
{
|
|
struct pico_socket *s = (struct pico_socket *)arg;
|
|
char end[4] = "end";
|
|
pico_socket_send(s, end, 4);
|
|
if (exit_retry++ > 3) {
|
|
if (!pico_timer_add(1000, deferred_exit, udpclient_pas)) {
|
|
printf("Failed to start exit timer, exiting now\n");
|
|
exit(1);
|
|
}
|
|
} else {
|
|
if (!pico_timer_add(1000, request_exit_echo, s)) {
|
|
printf("Failed to start request_exit_echo timer, sending request now\n");
|
|
request_exit_echo((pico_time)0, NULL);
|
|
exit(1);
|
|
}
|
|
printf("%s: requested exit of echo\n", __FUNCTION__);
|
|
}
|
|
}
|
|
|
|
void udpclient_send(pico_time __attribute__((unused)) now, void __attribute__((unused)) *arg)
|
|
{
|
|
struct pico_socket *s = udpclient_pas->s;
|
|
char *buf = NULL;
|
|
int i = 0, w = 0;
|
|
static uint16_t loop = 0;
|
|
|
|
if (++loop > udpclient_pas->loops) {
|
|
if (!pico_timer_add(1000, request_exit_echo, s)) {
|
|
printf("Failed to start request_exit_echo timer, sending request now\n");
|
|
request_exit_echo((pico_time)0, NULL);
|
|
exit(1);
|
|
}
|
|
return;
|
|
} else {
|
|
buf = calloc(1, udpclient_pas->datasize);
|
|
if (!buf) {
|
|
printf("%s: no memory available\n", __FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
memset(buf, '1', udpclient_pas->datasize);
|
|
picoapp_dbg("%s: performing loop %u\n", __FUNCTION__, loop);
|
|
for (i = 0; i < udpclient_pas->subloops; i++) {
|
|
w = pico_socket_send(s, buf, udpclient_pas->datasize);
|
|
if (w <= 0)
|
|
break;
|
|
}
|
|
picoapp_dbg("%s: written %u byte(s) in each of %u subloops\n", __FUNCTION__, udpclient_pas->datasize, i);
|
|
free(buf);
|
|
}
|
|
|
|
if (!pico_timer_add(100, udpclient_send, NULL)) {
|
|
printf("Failed to start send timer, sending exit request to echo and exiting\n");
|
|
request_exit_echo((pico_time)0, NULL);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void cb_udpclient(uint16_t ev, struct pico_socket *s)
|
|
{
|
|
char *recvbuf = NULL;
|
|
int r = 0;
|
|
|
|
if (ev & PICO_SOCK_EV_RD) {
|
|
recvbuf = calloc(1, udpclient_pas->datasize);
|
|
if (!recvbuf) {
|
|
printf("%s: no memory available\n", __FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
do {
|
|
r = pico_socket_recv(s, recvbuf, udpclient_pas->datasize);
|
|
} while ( r > 0);
|
|
free(recvbuf);
|
|
}
|
|
|
|
if (ev == PICO_SOCK_EV_ERR) {
|
|
printf("Socket Error received. Bailing out.\n");
|
|
free(udpclient_pas);
|
|
exit(7);
|
|
}
|
|
}
|
|
|
|
void app_udpclient(char *arg)
|
|
{
|
|
char *daddr = NULL, *lport = NULL, *sport = NULL, *s_datasize = NULL, *s_loops = NULL, *s_subloops = NULL;
|
|
char *nxt = arg;
|
|
char sinaddr_any[40] = {
|
|
0
|
|
};
|
|
uint16_t listen_port = 0;
|
|
int ret = 0;
|
|
|
|
udpclient_pas = calloc(1, sizeof(struct udpclient_pas));
|
|
if (!udpclient_pas) {
|
|
printf("%s: no memory available\n", __FUNCTION__);
|
|
exit(255);
|
|
}
|
|
|
|
udpclient_pas->s = NULL;
|
|
udpclient_pas->loops = 100;
|
|
udpclient_pas->subloops = 10;
|
|
udpclient_pas->datasize = 1400;
|
|
|
|
/* start of argument parsing */
|
|
if (nxt) {
|
|
nxt = cpy_arg(&daddr, arg);
|
|
if (daddr) {
|
|
if (!IPV6_MODE)
|
|
pico_string_to_ipv4(daddr, &udpclient_pas->dst.ip4.addr);
|
|
|
|
#ifdef PICO_SUPPORT_IPV6
|
|
else
|
|
pico_string_to_ipv6(daddr, udpclient_pas->dst.ip6.addr);
|
|
#endif
|
|
} else {
|
|
goto out;
|
|
}
|
|
} else {
|
|
/* missing dest_addr */
|
|
goto out;
|
|
}
|
|
|
|
if (nxt) {
|
|
nxt = cpy_arg(&sport, nxt);
|
|
if (sport && atoi(sport)) {
|
|
udpclient_pas->sport = short_be(atoi(sport));
|
|
} else {
|
|
goto out;
|
|
}
|
|
} else {
|
|
/* missing send_port */
|
|
goto out;
|
|
}
|
|
|
|
if (nxt) {
|
|
nxt = cpy_arg(&lport, nxt);
|
|
if (lport && atoi(lport)) {
|
|
listen_port = short_be(atoi(lport));
|
|
} else {
|
|
goto out;
|
|
}
|
|
} else {
|
|
/* missing listen_port, use default */
|
|
listen_port = 0;
|
|
}
|
|
|
|
if (nxt) {
|
|
nxt = cpy_arg(&s_datasize, nxt);
|
|
if (s_datasize && atoi(s_datasize)) {
|
|
udpclient_pas->datasize = atoi(s_datasize);
|
|
} else {
|
|
goto out;
|
|
}
|
|
} else {
|
|
/* missing datasize, incomplete optional parameters? -> exit */
|
|
if (lport)
|
|
goto out;
|
|
}
|
|
|
|
if (nxt) {
|
|
nxt = cpy_arg(&s_loops, nxt);
|
|
if (s_loops && atoi(s_loops)) {
|
|
udpclient_pas->loops = atoi(s_loops);
|
|
} else {
|
|
goto out;
|
|
}
|
|
} else {
|
|
/* missing loops, incomplete optional parameters? -> exit */
|
|
if (s_datasize)
|
|
goto out;
|
|
}
|
|
|
|
if (nxt) {
|
|
nxt = cpy_arg(&s_subloops, nxt);
|
|
if (s_subloops && atoi(s_subloops)) {
|
|
udpclient_pas->subloops = atoi(s_subloops);
|
|
} else {
|
|
goto out;
|
|
}
|
|
} else {
|
|
/* missing subloops, incomplete optional parameters? -> exit */
|
|
if (s_loops)
|
|
goto out;
|
|
}
|
|
|
|
/* end of argument parsing */
|
|
|
|
if (!IPV6_MODE)
|
|
pico_ipv4_to_string(sinaddr_any, inaddr_any.addr);
|
|
|
|
#ifdef PICO_SUPPORT_IPV6
|
|
else
|
|
pico_ipv6_to_string(sinaddr_any, inaddr6_any.addr);
|
|
#endif
|
|
|
|
if (!IPV6_MODE)
|
|
udpclient_pas->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &cb_udpclient);
|
|
else
|
|
udpclient_pas->s = pico_socket_open(PICO_PROTO_IPV6, PICO_PROTO_UDP, &cb_udpclient);
|
|
|
|
if (!udpclient_pas->s) {
|
|
printf("%s: error opening socket: %s\n", __FUNCTION__, strerror(pico_err));
|
|
free(udpclient_pas);
|
|
exit(1);
|
|
}
|
|
|
|
if (!IPV6_MODE)
|
|
ret = pico_socket_bind(udpclient_pas->s, &inaddr_any, &listen_port);
|
|
else
|
|
ret = pico_socket_bind(udpclient_pas->s, &inaddr6_any, &listen_port);
|
|
|
|
if (ret < 0) {
|
|
free(udpclient_pas);
|
|
printf("%s: error binding socket to %s:%u: %s\n", __FUNCTION__, sinaddr_any, short_be(listen_port), strerror(pico_err));
|
|
exit(1);
|
|
}
|
|
|
|
if (!IPV6_MODE)
|
|
ret = pico_socket_connect(udpclient_pas->s, &udpclient_pas->dst.ip4, udpclient_pas->sport);
|
|
else
|
|
ret = pico_socket_connect(udpclient_pas->s, &udpclient_pas->dst.ip6, udpclient_pas->sport);
|
|
|
|
if (ret < 0) {
|
|
printf("%s: error connecting to [%s]:%u: %s\n", __FUNCTION__, daddr, short_be(udpclient_pas->sport), strerror(pico_err));
|
|
free(udpclient_pas);
|
|
exit(1);
|
|
}
|
|
|
|
printf("\n%s: UDP client launched. Sending packets of %u bytes in %u loops and %u subloops to %s:%u\n\n",
|
|
__FUNCTION__, udpclient_pas->datasize, udpclient_pas->loops, udpclient_pas->subloops, daddr, short_be(udpclient_pas->sport));
|
|
|
|
if (!pico_timer_add(100, udpclient_send, NULL)) {
|
|
printf("Failed to start send timer, sending exit request to echo and exiting\n");
|
|
request_exit_echo((pico_time)0, NULL);
|
|
exit(1);
|
|
}
|
|
|
|
/* free strdups */
|
|
if (daddr)
|
|
free (daddr);
|
|
|
|
if (lport)
|
|
free (lport);
|
|
|
|
if (sport)
|
|
free (sport);
|
|
|
|
if (s_datasize)
|
|
free (s_datasize);
|
|
|
|
if (s_loops)
|
|
free (s_loops);
|
|
|
|
if (s_subloops)
|
|
free (s_subloops);
|
|
|
|
return;
|
|
|
|
out:
|
|
fprintf(stderr, "udpclient expects the following format: udpclient:dest_addr:dest_port[:listen_port:datasize:loops:subloops]\n");
|
|
free(udpclient_pas);
|
|
exit(255);
|
|
}
|
|
/*** END UDP CLIENT ***/
|