1#include "utils.h"
2#include <pico_ipv4.h>
3#include <pico_ipv6.h>
4#include <pico_socket.h>
5
6/*** START UDP CLIENT ***/
7/*
8 * udpclient expects the following format: udpclient:dest_addr:sendto_port[:listen_port:datasize:loops:subloops]
9 * dest_addr: IP address to send datagrams to
10 * sendto_port: port number to send datagrams to
11 * listen_port [OPTIONAL]: port number on which the udpclient listens
12 * datasize [OPTIONAL]: size of the data given to the socket in one go
13 * loops [OPTIONAL]: number of intervals in which data is send
14 * subloops [OPTIONAL]: number of sends in one interval
15 *
16 * REMARK: once an optional parameter is given, all optional parameters need a value!
17 *
18 * 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
19 */
20
21struct udpclient_pas *udpclient_pas;
22
23static int exit_retry = 0;
24
25static void request_exit_echo(pico_time now, void *arg)
26{
27    struct pico_socket *s = (struct pico_socket *)arg;
28    char end[4] = "end";
29    pico_socket_send(s, end, 4);
30    if (exit_retry++ > 3) {
31        if (!pico_timer_add(1000, deferred_exit, udpclient_pas)) {
32            printf("Failed to start exit timer, exiting now\n");
33            exit(1);
34        }
35    } else {
36        if (!pico_timer_add(1000, request_exit_echo, s)) {
37            printf("Failed to start request_exit_echo timer, sending request now\n");
38            request_exit_echo((pico_time)0, NULL);
39            exit(1);
40        }
41        printf("%s: requested exit of echo\n", __FUNCTION__);
42    }
43}
44
45void udpclient_send(pico_time __attribute__((unused)) now, void __attribute__((unused))  *arg)
46{
47    struct pico_socket *s = udpclient_pas->s;
48    char *buf = NULL;
49    int i = 0, w = 0;
50    static uint16_t loop = 0;
51
52    if (++loop > udpclient_pas->loops) {
53        if (!pico_timer_add(1000, request_exit_echo, s)) {
54            printf("Failed to start request_exit_echo timer, sending request now\n");
55            request_exit_echo((pico_time)0, NULL);
56            exit(1);
57        }
58        return;
59    } else {
60        buf = calloc(1, udpclient_pas->datasize);
61        if (!buf) {
62            printf("%s: no memory available\n", __FUNCTION__);
63            return;
64        }
65
66        memset(buf, '1', udpclient_pas->datasize);
67        picoapp_dbg("%s: performing loop %u\n", __FUNCTION__, loop);
68        for (i = 0; i < udpclient_pas->subloops; i++) {
69            w =  pico_socket_send(s, buf, udpclient_pas->datasize);
70            if (w <= 0)
71                break;
72        }
73        picoapp_dbg("%s: written %u byte(s) in each of %u subloops\n", __FUNCTION__, udpclient_pas->datasize, i);
74        free(buf);
75    }
76
77    if (!pico_timer_add(100, udpclient_send, NULL)) {
78        printf("Failed to start send timer, sending exit request to echo and exiting\n");
79        request_exit_echo((pico_time)0, NULL);
80        exit(1);
81    }
82}
83
84void cb_udpclient(uint16_t ev, struct pico_socket *s)
85{
86    char *recvbuf = NULL;
87    int r = 0;
88
89    if (ev & PICO_SOCK_EV_RD) {
90        recvbuf = calloc(1, udpclient_pas->datasize);
91        if (!recvbuf) {
92            printf("%s: no memory available\n", __FUNCTION__);
93            return;
94        }
95
96        do {
97            r = pico_socket_recv(s, recvbuf, udpclient_pas->datasize);
98        } while ( r > 0);
99        free(recvbuf);
100    }
101
102    if (ev == PICO_SOCK_EV_ERR) {
103        printf("Socket Error received. Bailing out.\n");
104        free(udpclient_pas);
105        exit(7);
106    }
107}
108
109void app_udpclient(char *arg)
110{
111    char *daddr = NULL, *lport = NULL, *sport = NULL, *s_datasize = NULL, *s_loops = NULL, *s_subloops = NULL;
112    char *nxt = arg;
113    char sinaddr_any[40] = {
114        0
115    };
116    uint16_t listen_port = 0;
117    int ret = 0;
118
119    udpclient_pas = calloc(1, sizeof(struct udpclient_pas));
120    if (!udpclient_pas) {
121        printf("%s: no memory available\n", __FUNCTION__);
122        exit(255);
123    }
124
125    udpclient_pas->s = NULL;
126    udpclient_pas->loops = 100;
127    udpclient_pas->subloops = 10;
128    udpclient_pas->datasize = 1400;
129
130    /* start of argument parsing */
131    if (nxt) {
132        nxt = cpy_arg(&daddr, arg);
133        if (daddr) {
134            if (!IPV6_MODE)
135                pico_string_to_ipv4(daddr, &udpclient_pas->dst.ip4.addr);
136
137      #ifdef PICO_SUPPORT_IPV6
138            else
139                pico_string_to_ipv6(daddr, udpclient_pas->dst.ip6.addr);
140      #endif
141        } else {
142            goto out;
143        }
144    } else {
145        /* missing dest_addr */
146        goto out;
147    }
148
149    if (nxt) {
150        nxt = cpy_arg(&sport, nxt);
151        if (sport && atoi(sport)) {
152            udpclient_pas->sport = short_be(atoi(sport));
153        } else {
154            goto out;
155        }
156    } else {
157        /* missing send_port */
158        goto out;
159    }
160
161    if (nxt) {
162        nxt = cpy_arg(&lport, nxt);
163        if (lport && atoi(lport)) {
164            listen_port = short_be(atoi(lport));
165        } else {
166            goto out;
167        }
168    } else {
169        /* missing listen_port, use default */
170        listen_port = 0;
171    }
172
173    if (nxt) {
174        nxt = cpy_arg(&s_datasize, nxt);
175        if (s_datasize && atoi(s_datasize)) {
176            udpclient_pas->datasize = atoi(s_datasize);
177        } else {
178            goto out;
179        }
180    } else {
181        /* missing datasize, incomplete optional parameters? -> exit */
182        if (lport)
183            goto out;
184    }
185
186    if (nxt) {
187        nxt = cpy_arg(&s_loops, nxt);
188        if (s_loops && atoi(s_loops)) {
189            udpclient_pas->loops = atoi(s_loops);
190        } else {
191            goto out;
192        }
193    } else {
194        /* missing loops, incomplete optional parameters? -> exit */
195        if (s_datasize)
196            goto out;
197    }
198
199    if (nxt) {
200        nxt = cpy_arg(&s_subloops, nxt);
201        if (s_subloops && atoi(s_subloops)) {
202            udpclient_pas->subloops = atoi(s_subloops);
203        } else {
204            goto out;
205        }
206    } else {
207        /* missing subloops, incomplete optional parameters? -> exit */
208        if (s_loops)
209            goto out;
210    }
211
212    /* end of argument parsing */
213
214    if (!IPV6_MODE)
215        pico_ipv4_to_string(sinaddr_any, inaddr_any.addr);
216
217  #ifdef PICO_SUPPORT_IPV6
218    else
219        pico_ipv6_to_string(sinaddr_any, inaddr6_any.addr);
220  #endif
221
222    if (!IPV6_MODE)
223        udpclient_pas->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &cb_udpclient);
224    else
225        udpclient_pas->s = pico_socket_open(PICO_PROTO_IPV6, PICO_PROTO_UDP, &cb_udpclient);
226
227    if (!udpclient_pas->s) {
228        printf("%s: error opening socket: %s\n", __FUNCTION__, strerror(pico_err));
229        free(udpclient_pas);
230        exit(1);
231    }
232
233    if (!IPV6_MODE)
234        ret = pico_socket_bind(udpclient_pas->s, &inaddr_any, &listen_port);
235    else
236        ret = pico_socket_bind(udpclient_pas->s, &inaddr6_any, &listen_port);
237
238    if (ret < 0) {
239        free(udpclient_pas);
240        printf("%s: error binding socket to %s:%u: %s\n", __FUNCTION__, sinaddr_any, short_be(listen_port), strerror(pico_err));
241        exit(1);
242    }
243
244    if (!IPV6_MODE)
245        ret = pico_socket_connect(udpclient_pas->s, &udpclient_pas->dst.ip4, udpclient_pas->sport);
246    else
247        ret = pico_socket_connect(udpclient_pas->s, &udpclient_pas->dst.ip6, udpclient_pas->sport);
248
249    if (ret < 0) {
250        printf("%s: error connecting to [%s]:%u: %s\n", __FUNCTION__, daddr, short_be(udpclient_pas->sport), strerror(pico_err));
251        free(udpclient_pas);
252        exit(1);
253    }
254
255    printf("\n%s: UDP client launched. Sending packets of %u bytes in %u loops and %u subloops to %s:%u\n\n",
256           __FUNCTION__, udpclient_pas->datasize, udpclient_pas->loops, udpclient_pas->subloops, daddr, short_be(udpclient_pas->sport));
257
258    if (!pico_timer_add(100, udpclient_send, NULL)) {
259        printf("Failed to start send timer, sending exit request to echo and exiting\n");
260        request_exit_echo((pico_time)0, NULL);
261        exit(1);
262    }
263
264    /* free strdups */
265    if (daddr)
266        free (daddr);
267
268    if (lport)
269        free (lport);
270
271    if (sport)
272        free (sport);
273
274    if (s_datasize)
275        free (s_datasize);
276
277    if (s_loops)
278        free (s_loops);
279
280    if (s_subloops)
281        free (s_subloops);
282
283    return;
284
285out:
286    fprintf(stderr, "udpclient expects the following format: udpclient:dest_addr:dest_port[:listen_port:datasize:loops:subloops]\n");
287    free(udpclient_pas);
288    exit(255);
289}
290/*** END UDP CLIENT ***/
291