source: mainline/uspace/lib/inet/src/udp.c

Last change on this file was b9be9b0, checked in by Jiri Svoboda <jiri@…>, 3 years ago

Fix handling of UDP default destination in udp_assoc_send_msg()

Fixes netecho failing to send any message.

  • Property mode set to 100644
File size: 11.7 KB
RevLine 
[fab2746]1/*
2 * Copyright (c) 2015 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
[edeee9f]29/** @addtogroup libinet
[fab2746]30 * @{
31 */
32/** @file UDP API
33 */
34
35#include <errno.h>
36#include <inet/endpoint.h>
37#include <inet/udp.h>
38#include <ipc/services.h>
39#include <ipc/udp.h>
40#include <loc.h>
41#include <stdlib.h>
42
[984a9ba]43static void udp_cb_conn(ipc_call_t *, void *);
[fab2746]44
[b10460a]45/** Create callback connection from UDP service.
46 *
47 * @param udp UDP service
[cde999a]48 * @return EOK on success or an error code
[b10460a]49 */
[b7fd2a0]50static errno_t udp_callback_create(udp_t *udp)
[fab2746]51{
52 async_exch_t *exch = async_exchange_begin(udp->sess);
53
54 aid_t req = async_send_0(exch, UDP_CALLBACK_CREATE, NULL);
[a35b458]55
[78bb04b]56 port_id_t port;
[b7fd2a0]57 errno_t rc = async_create_callback_port(exch, INTERFACE_UDP_CB, 0, 0,
[78bb04b]58 udp_cb_conn, udp, &port);
[a35b458]59
[fab2746]60 async_exchange_end(exch);
61
62 if (rc != EOK)
63 return rc;
64
[b7fd2a0]65 errno_t retval;
[fab2746]66 async_wait_for(req, &retval);
67
68 return retval;
69}
70
[b10460a]71/** Create UDP client instance.
72 *
73 * @param rudp Place to store pointer to new UDP client
74 * @return EOK on success, ENOMEM if out of memory, EIO if service
75 * cannot be contacted
76 */
[b7fd2a0]77errno_t udp_create(udp_t **rudp)
[fab2746]78{
79 udp_t *udp;
80 service_id_t udp_svcid;
[b7fd2a0]81 errno_t rc;
[fab2746]82
83 udp = calloc(1, sizeof(udp_t));
84 if (udp == NULL) {
85 rc = ENOMEM;
86 goto error;
87 }
88
89 list_initialize(&udp->assoc);
[1f2b07a]90 fibril_mutex_initialize(&udp->lock);
91 fibril_condvar_initialize(&udp->cv);
[fab2746]92
93 rc = loc_service_get_id(SERVICE_NAME_UDP, &udp_svcid,
94 IPC_FLAG_BLOCKING);
95 if (rc != EOK) {
96 rc = EIO;
97 goto error;
98 }
99
[f9b2cb4c]100 udp->sess = loc_service_connect(udp_svcid, INTERFACE_UDP,
[fab2746]101 IPC_FLAG_BLOCKING);
102 if (udp->sess == NULL) {
103 rc = EIO;
104 goto error;
105 }
106
107 rc = udp_callback_create(udp);
108 if (rc != EOK) {
109 rc = EIO;
110 goto error;
111 }
112
113 *rudp = udp;
114 return EOK;
115error:
116 free(udp);
117 return rc;
118}
119
[b10460a]120/** Destroy UDP client instance.
121 *
122 * @param udp UDP client
123 */
[fab2746]124void udp_destroy(udp_t *udp)
125{
126 if (udp == NULL)
127 return;
128
129 async_hangup(udp->sess);
[1f2b07a]130
131 fibril_mutex_lock(&udp->lock);
132 while (!udp->cb_done)
133 fibril_condvar_wait(&udp->cv, &udp->lock);
134 fibril_mutex_unlock(&udp->lock);
135
[fab2746]136 free(udp);
137}
138
[b10460a]139/** Create new UDP association.
140 *
141 * Create a UDP association that allows sending and receiving messages.
142 *
143 * @a epp may specify remote address and port, in which case only messages
144 * from that remote endpoint will be received. Also, that remote endpoint
145 * is used as default when @c NULL is passed as destination to
146 * udp_assoc_send_msg.
147 *
148 * @a epp may specify a local link or address. If it does not, the association
149 * will listen on all local links/addresses. If @a epp does not specify
150 * a local port number, a free dynamic port number will be allocated.
151 *
152 * The caller is informed about incoming data by invoking @a cb->recv_msg
153 *
154 * @param udp UDP client
155 * @param epp Internet endpoint pair
156 * @param cb Callbacks
157 * @param arg Argument to callbacks
158 * @param rassoc Place to store pointer to new association
159 *
[cde999a]160 * @return EOK on success or an error code.
[b10460a]161 */
[b7fd2a0]162errno_t udp_assoc_create(udp_t *udp, inet_ep2_t *epp, udp_cb_t *cb, void *arg,
[fab2746]163 udp_assoc_t **rassoc)
164{
165 async_exch_t *exch;
166 udp_assoc_t *assoc;
167 ipc_call_t answer;
168
169 assoc = calloc(1, sizeof(udp_assoc_t));
170 if (assoc == NULL)
171 return ENOMEM;
172
173 exch = async_exchange_begin(udp->sess);
174 aid_t req = async_send_0(exch, UDP_ASSOC_CREATE, &answer);
[b7fd2a0]175 errno_t rc = async_data_write_start(exch, (void *)epp,
[fab2746]176 sizeof(inet_ep2_t));
177 async_exchange_end(exch);
178
179 if (rc != EOK) {
[b7fd2a0]180 errno_t rc_orig;
[fab2746]181 async_wait_for(req, &rc_orig);
182 if (rc_orig != EOK)
183 rc = rc_orig;
184 goto error;
185 }
186
187 async_wait_for(req, &rc);
188 if (rc != EOK)
189 goto error;
190
191 assoc->udp = udp;
[fafb8e5]192 assoc->id = ipc_get_arg1(&answer);
[fab2746]193 assoc->cb = cb;
194 assoc->cb_arg = arg;
195
196 list_append(&assoc->ludp, &udp->assoc);
197 *rassoc = assoc;
198
199 return EOK;
200error:
201 free(assoc);
[b7fd2a0]202 return (errno_t) rc;
[fab2746]203}
204
[b10460a]205/** Destroy UDP association.
206 *
207 * Destroy UDP association. The caller should destroy all associations
208 * he created before destroying the UDP client and before terminating.
209 *
210 * @param assoc UDP association
211 */
[fab2746]212void udp_assoc_destroy(udp_assoc_t *assoc)
213{
214 async_exch_t *exch;
215
216 if (assoc == NULL)
217 return;
218
219 list_remove(&assoc->ludp);
220
221 exch = async_exchange_begin(assoc->udp->sess);
[b7fd2a0]222 errno_t rc = async_req_1_0(exch, UDP_ASSOC_DESTROY, assoc->id);
[fab2746]223 async_exchange_end(exch);
224
225 free(assoc);
226 (void) rc;
227}
228
[58e8646]229/** Set UDP association sending messages with no local address
230 *
231 * @param assoc Association
232 * @param flags Flags
233 */
[b7fd2a0]234errno_t udp_assoc_set_nolocal(udp_assoc_t *assoc)
[58e8646]235{
236 async_exch_t *exch;
237
238 exch = async_exchange_begin(assoc->udp->sess);
[b7fd2a0]239 errno_t rc = async_req_1_0(exch, UDP_ASSOC_SET_NOLOCAL, assoc->id);
[58e8646]240 async_exchange_end(exch);
241
242 return rc;
243}
244
[b10460a]245/** Send message via UDP association.
246 *
247 * @param assoc Association
248 * @param dest Destination endpoint or @c NULL to use association's remote ep.
249 * @param data Message data
250 * @param bytes Message size in bytes
251 *
[cde999a]252 * @return EOK on success or an error code
[b10460a]253 */
[b7fd2a0]254errno_t udp_assoc_send_msg(udp_assoc_t *assoc, inet_ep_t *dest, void *data,
[fab2746]255 size_t bytes)
256{
257 async_exch_t *exch;
[b9be9b0]258 inet_ep_t ddest;
[fab2746]259
260 exch = async_exchange_begin(assoc->udp->sess);
261 aid_t req = async_send_1(exch, UDP_ASSOC_SEND_MSG, assoc->id, NULL);
262
[b9be9b0]263 /* If dest is null, use default destination */
264 if (dest == NULL) {
265 inet_ep_init(&ddest);
266 dest = &ddest;
267 }
268
[b7fd2a0]269 errno_t rc = async_data_write_start(exch, (void *)dest,
[fab2746]270 sizeof(inet_ep_t));
271 if (rc != EOK) {
272 async_exchange_end(exch);
273 async_forget(req);
274 return rc;
275 }
276
277 rc = async_data_write_start(exch, data, bytes);
278 if (rc != EOK) {
279 async_forget(req);
280 return rc;
281 }
282
283 async_exchange_end(exch);
284
285 if (rc != EOK) {
286 async_forget(req);
287 return rc;
288 }
289
290 async_wait_for(req, &rc);
291 return rc;
292}
293
[b10460a]294/** Get the user/callback argument for an association.
295 *
296 * @param assoc UDP association
297 * @return User argument associated with association
298 */
[fab2746]299void *udp_assoc_userptr(udp_assoc_t *assoc)
300{
301 return assoc->cb_arg;
302}
303
[b10460a]304/** Get size of received message in bytes.
305 *
306 * Assuming jumbo messages can be received, the caller first needs to determine
307 * the size of the received message by calling this function, then they can
308 * read the message piece-wise using udp_rmsg_read().
309 *
310 * @param rmsg Received message
311 * @return Size of received message in bytes
312 */
[fab2746]313size_t udp_rmsg_size(udp_rmsg_t *rmsg)
314{
315 return rmsg->size;
316}
317
[b10460a]318/** Read part of received message.
319 *
320 * @param rmsg Received message
321 * @param off Start offset
322 * @param buf Buffer for storing data
323 * @param bsize Buffer size
324 *
[cde999a]325 * @return EOK on success or an error code.
[b10460a]326 */
[b7fd2a0]327errno_t udp_rmsg_read(udp_rmsg_t *rmsg, size_t off, void *buf, size_t bsize)
[fab2746]328{
329 async_exch_t *exch;
330 ipc_call_t answer;
331
332 exch = async_exchange_begin(rmsg->udp->sess);
333 aid_t req = async_send_1(exch, UDP_RMSG_READ, off, &answer);
[b7fd2a0]334 errno_t rc = async_data_read_start(exch, buf, bsize);
[fab2746]335 async_exchange_end(exch);
336
337 if (rc != EOK) {
338 async_forget(req);
339 return rc;
340 }
341
[b7fd2a0]342 errno_t retval;
[fab2746]343 async_wait_for(req, &retval);
344 if (retval != EOK) {
345 return retval;
346 }
347
348 return EOK;
349}
350
[b10460a]351/** Get remote endpoint of received message.
352 *
353 * Place the remote endpoint (the one from which the message was supposedly
354 * sent) to @a ep.
355 *
356 * @param rmsg Received message
357 * @param ep Place to store remote endpoint
358 */
[fab2746]359void udp_rmsg_remote_ep(udp_rmsg_t *rmsg, inet_ep_t *ep)
360{
361 *ep = rmsg->remote_ep;
362}
363
[b10460a]364/** Get type of received ICMP error message.
365 *
366 * @param rerr Received error message
367 * @return Error message type
368 */
[fab2746]369uint8_t udp_rerr_type(udp_rerr_t *rerr)
370{
371 return 0;
372}
373
[b10460a]374/** Get code of received ICMP error message.
375 *
376 * @param rerr Received error message
377 * @return Error message code
378 */
[fab2746]379uint8_t udp_rerr_code(udp_rerr_t *rerr)
380{
381 return 0;
382}
383
[b10460a]384/** Get information about the next received message from UDP service.
385 *
386 * @param udp UDP client
387 * @param rmsg Place to store message information
388 *
[cde999a]389 * @return EOK on success or an error code
[b10460a]390 */
[b7fd2a0]391static errno_t udp_rmsg_info(udp_t *udp, udp_rmsg_t *rmsg)
[fab2746]392{
393 async_exch_t *exch;
394 inet_ep_t ep;
395 ipc_call_t answer;
396
397 exch = async_exchange_begin(udp->sess);
398 aid_t req = async_send_0(exch, UDP_RMSG_INFO, &answer);
[b7fd2a0]399 errno_t rc = async_data_read_start(exch, &ep, sizeof(inet_ep_t));
[fab2746]400 async_exchange_end(exch);
401
402 if (rc != EOK) {
403 async_forget(req);
404 return rc;
405 }
406
[b7fd2a0]407 errno_t retval;
[fab2746]408 async_wait_for(req, &retval);
409 if (retval != EOK)
410 return retval;
411
412 rmsg->udp = udp;
[fafb8e5]413 rmsg->assoc_id = ipc_get_arg1(&answer);
414 rmsg->size = ipc_get_arg2(&answer);
[fab2746]415 rmsg->remote_ep = ep;
416 return EOK;
417}
418
[b10460a]419/** Discard next received message in UDP service.
420 *
421 * @param udp UDP client
[cde999a]422 * @return EOK on success or an error code
[b10460a]423 */
[b7fd2a0]424static errno_t udp_rmsg_discard(udp_t *udp)
[fab2746]425{
426 async_exch_t *exch;
427
428 exch = async_exchange_begin(udp->sess);
[b7fd2a0]429 errno_t rc = async_req_0_0(exch, UDP_RMSG_DISCARD);
[fab2746]430 async_exchange_end(exch);
431
432 return rc;
433}
434
[b10460a]435/** Get association based on its ID.
436 *
437 * @param udp UDP client
438 * @param id Association ID
439 * @param rassoc Place to store pointer to association
440 *
441 * @return EOK on success, EINVAL if no association with the given ID exists
442 */
[b7fd2a0]443static errno_t udp_assoc_get(udp_t *udp, sysarg_t id, udp_assoc_t **rassoc)
[fab2746]444{
445 list_foreach(udp->assoc, ludp, udp_assoc_t, assoc) {
446 if (assoc->id == id) {
447 *rassoc = assoc;
448 return EOK;
449 }
450 }
451
452 return EINVAL;
453}
454
[b10460a]455/** Handle 'data' event, i.e. some message(s) arrived.
456 *
457 * For each received message, get information about it, call @c recv_msg
458 * callback and discard it.
459 *
[984a9ba]460 * @param udp UDP client
[b10460a]461 * @param icall IPC message
[984a9ba]462 *
[b10460a]463 */
[984a9ba]464static void udp_ev_data(udp_t *udp, ipc_call_t *icall)
[fab2746]465{
466 udp_rmsg_t rmsg;
467 udp_assoc_t *assoc;
[b7fd2a0]468 errno_t rc;
[fab2746]469
470 while (true) {
471 rc = udp_rmsg_info(udp, &rmsg);
472 if (rc != EOK) {
473 break;
474 }
475
476 rc = udp_assoc_get(udp, rmsg.assoc_id, &assoc);
477 if (rc != EOK) {
478 continue;
479 }
480
481 if (assoc->cb != NULL && assoc->cb->recv_msg != NULL)
482 assoc->cb->recv_msg(assoc, &rmsg);
483
484 rc = udp_rmsg_discard(udp);
485 if (rc != EOK) {
486 break;
487 }
488 }
489
[984a9ba]490 async_answer_0(icall, EOK);
[fab2746]491}
492
[b10460a]493/** UDP service callback connection.
494 *
495 * @param icall Connect message
[984a9ba]496 * @param arg Argument, UDP client
497 *
[b10460a]498 */
[984a9ba]499static void udp_cb_conn(ipc_call_t *icall, void *arg)
[fab2746]500{
501 udp_t *udp = (udp_t *)arg;
502
503 while (true) {
504 ipc_call_t call;
[984a9ba]505 async_get_call(&call);
[fab2746]506
[fafb8e5]507 if (!ipc_get_imethod(&call)) {
[1f2b07a]508 /* Hangup */
[889cdb1]509 async_answer_0(&call, EOK);
[1f2b07a]510 goto out;
[fab2746]511 }
512
[fafb8e5]513 switch (ipc_get_imethod(&call)) {
[fab2746]514 case UDP_EV_DATA:
[984a9ba]515 udp_ev_data(udp, &call);
[fab2746]516 break;
517 default:
[984a9ba]518 async_answer_0(&call, ENOTSUP);
[fab2746]519 break;
520 }
521 }
[984a9ba]522
[1f2b07a]523out:
524 fibril_mutex_lock(&udp->lock);
525 udp->cb_done = true;
526 fibril_mutex_unlock(&udp->lock);
527 fibril_condvar_broadcast(&udp->cv);
[fab2746]528}
529
530/** @}
531 */
Note: See TracBrowser for help on using the repository browser.