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