source: mainline/uspace/lib/c/generic/inet/udp.c@ 514d561

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 514d561 was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • Property mode set to 100644
File size: 11.6 KB
Line 
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
29/** @addtogroup libc
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
43static void udp_cb_conn(ipc_call_t *, void *);
44
45/** Create callback connection from UDP service.
46 *
47 * @param udp UDP service
48 * @return EOK on success or an error code
49 */
50static errno_t udp_callback_create(udp_t *udp)
51{
52 async_exch_t *exch = async_exchange_begin(udp->sess);
53
54 aid_t req = async_send_0(exch, UDP_CALLBACK_CREATE, NULL);
55
56 port_id_t port;
57 errno_t rc = async_create_callback_port(exch, INTERFACE_UDP_CB, 0, 0,
58 udp_cb_conn, udp, &port);
59
60 async_exchange_end(exch);
61
62 if (rc != EOK)
63 return rc;
64
65 errno_t retval;
66 async_wait_for(req, &retval);
67
68 return retval;
69}
70
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 */
77errno_t udp_create(udp_t **rudp)
78{
79 udp_t *udp;
80 service_id_t udp_svcid;
81 errno_t rc;
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);
90 fibril_mutex_initialize(&udp->lock);
91 fibril_condvar_initialize(&udp->cv);
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
100 udp->sess = loc_service_connect(udp_svcid, INTERFACE_UDP,
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
120/** Destroy UDP client instance.
121 *
122 * @param udp UDP client
123 */
124void udp_destroy(udp_t *udp)
125{
126 if (udp == NULL)
127 return;
128
129 async_hangup(udp->sess);
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
136 free(udp);
137}
138
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 *
160 * @return EOK on success or an error code.
161 */
162errno_t udp_assoc_create(udp_t *udp, inet_ep2_t *epp, udp_cb_t *cb, void *arg,
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);
175 errno_t rc = async_data_write_start(exch, (void *)epp,
176 sizeof(inet_ep2_t));
177 async_exchange_end(exch);
178
179 if (rc != EOK) {
180 errno_t rc_orig;
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;
192 assoc->id = IPC_GET_ARG1(answer);
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);
202 return (errno_t) rc;
203}
204
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 */
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);
222 errno_t rc = async_req_1_0(exch, UDP_ASSOC_DESTROY, assoc->id);
223 async_exchange_end(exch);
224
225 free(assoc);
226 (void) rc;
227}
228
229/** Set UDP association sending messages with no local address
230 *
231 * @param assoc Association
232 * @param flags Flags
233 */
234errno_t udp_assoc_set_nolocal(udp_assoc_t *assoc)
235{
236 async_exch_t *exch;
237
238 exch = async_exchange_begin(assoc->udp->sess);
239 errno_t rc = async_req_1_0(exch, UDP_ASSOC_SET_NOLOCAL, assoc->id);
240 async_exchange_end(exch);
241
242 return rc;
243}
244
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 *
252 * @return EOK on success or an error code
253 */
254errno_t udp_assoc_send_msg(udp_assoc_t *assoc, inet_ep_t *dest, void *data,
255 size_t bytes)
256{
257 async_exch_t *exch;
258
259 exch = async_exchange_begin(assoc->udp->sess);
260 aid_t req = async_send_1(exch, UDP_ASSOC_SEND_MSG, assoc->id, NULL);
261
262 errno_t rc = async_data_write_start(exch, (void *)dest,
263 sizeof(inet_ep_t));
264 if (rc != EOK) {
265 async_exchange_end(exch);
266 async_forget(req);
267 return rc;
268 }
269
270 rc = async_data_write_start(exch, data, bytes);
271 if (rc != EOK) {
272 async_forget(req);
273 return rc;
274 }
275
276 async_exchange_end(exch);
277
278 if (rc != EOK) {
279 async_forget(req);
280 return rc;
281 }
282
283 async_wait_for(req, &rc);
284 return rc;
285}
286
287/** Get the user/callback argument for an association.
288 *
289 * @param assoc UDP association
290 * @return User argument associated with association
291 */
292void *udp_assoc_userptr(udp_assoc_t *assoc)
293{
294 return assoc->cb_arg;
295}
296
297/** Get size of received message in bytes.
298 *
299 * Assuming jumbo messages can be received, the caller first needs to determine
300 * the size of the received message by calling this function, then they can
301 * read the message piece-wise using udp_rmsg_read().
302 *
303 * @param rmsg Received message
304 * @return Size of received message in bytes
305 */
306size_t udp_rmsg_size(udp_rmsg_t *rmsg)
307{
308 return rmsg->size;
309}
310
311/** Read part of received message.
312 *
313 * @param rmsg Received message
314 * @param off Start offset
315 * @param buf Buffer for storing data
316 * @param bsize Buffer size
317 *
318 * @return EOK on success or an error code.
319 */
320errno_t udp_rmsg_read(udp_rmsg_t *rmsg, size_t off, void *buf, size_t bsize)
321{
322 async_exch_t *exch;
323 ipc_call_t answer;
324
325 exch = async_exchange_begin(rmsg->udp->sess);
326 aid_t req = async_send_1(exch, UDP_RMSG_READ, off, &answer);
327 errno_t rc = async_data_read_start(exch, buf, bsize);
328 async_exchange_end(exch);
329
330 if (rc != EOK) {
331 async_forget(req);
332 return rc;
333 }
334
335 errno_t retval;
336 async_wait_for(req, &retval);
337 if (retval != EOK) {
338 return retval;
339 }
340
341 return EOK;
342}
343
344/** Get remote endpoint of received message.
345 *
346 * Place the remote endpoint (the one from which the message was supposedly
347 * sent) to @a ep.
348 *
349 * @param rmsg Received message
350 * @param ep Place to store remote endpoint
351 */
352void udp_rmsg_remote_ep(udp_rmsg_t *rmsg, inet_ep_t *ep)
353{
354 *ep = rmsg->remote_ep;
355}
356
357/** Get type of received ICMP error message.
358 *
359 * @param rerr Received error message
360 * @return Error message type
361 */
362uint8_t udp_rerr_type(udp_rerr_t *rerr)
363{
364 return 0;
365}
366
367/** Get code of received ICMP error message.
368 *
369 * @param rerr Received error message
370 * @return Error message code
371 */
372uint8_t udp_rerr_code(udp_rerr_t *rerr)
373{
374 return 0;
375}
376
377/** Get information about the next received message from UDP service.
378 *
379 * @param udp UDP client
380 * @param rmsg Place to store message information
381 *
382 * @return EOK on success or an error code
383 */
384static errno_t udp_rmsg_info(udp_t *udp, udp_rmsg_t *rmsg)
385{
386 async_exch_t *exch;
387 inet_ep_t ep;
388 ipc_call_t answer;
389
390 exch = async_exchange_begin(udp->sess);
391 aid_t req = async_send_0(exch, UDP_RMSG_INFO, &answer);
392 errno_t rc = async_data_read_start(exch, &ep, sizeof(inet_ep_t));
393 async_exchange_end(exch);
394
395 if (rc != EOK) {
396 async_forget(req);
397 return rc;
398 }
399
400 errno_t retval;
401 async_wait_for(req, &retval);
402 if (retval != EOK)
403 return retval;
404
405 rmsg->udp = udp;
406 rmsg->assoc_id = IPC_GET_ARG1(answer);
407 rmsg->size = IPC_GET_ARG2(answer);
408 rmsg->remote_ep = ep;
409 return EOK;
410}
411
412/** Discard next received message in UDP service.
413 *
414 * @param udp UDP client
415 * @return EOK on success or an error code
416 */
417static errno_t udp_rmsg_discard(udp_t *udp)
418{
419 async_exch_t *exch;
420
421 exch = async_exchange_begin(udp->sess);
422 errno_t rc = async_req_0_0(exch, UDP_RMSG_DISCARD);
423 async_exchange_end(exch);
424
425 return rc;
426}
427
428/** Get association based on its ID.
429 *
430 * @param udp UDP client
431 * @param id Association ID
432 * @param rassoc Place to store pointer to association
433 *
434 * @return EOK on success, EINVAL if no association with the given ID exists
435 */
436static errno_t udp_assoc_get(udp_t *udp, sysarg_t id, udp_assoc_t **rassoc)
437{
438 list_foreach(udp->assoc, ludp, udp_assoc_t, assoc) {
439 if (assoc->id == id) {
440 *rassoc = assoc;
441 return EOK;
442 }
443 }
444
445 return EINVAL;
446}
447
448/** Handle 'data' event, i.e. some message(s) arrived.
449 *
450 * For each received message, get information about it, call @c recv_msg
451 * callback and discard it.
452 *
453 * @param udp UDP client
454 * @param icall IPC message
455 *
456 */
457static void udp_ev_data(udp_t *udp, ipc_call_t *icall)
458{
459 udp_rmsg_t rmsg;
460 udp_assoc_t *assoc;
461 errno_t rc;
462
463 while (true) {
464 rc = udp_rmsg_info(udp, &rmsg);
465 if (rc != EOK) {
466 break;
467 }
468
469 rc = udp_assoc_get(udp, rmsg.assoc_id, &assoc);
470 if (rc != EOK) {
471 continue;
472 }
473
474 if (assoc->cb != NULL && assoc->cb->recv_msg != NULL)
475 assoc->cb->recv_msg(assoc, &rmsg);
476
477 rc = udp_rmsg_discard(udp);
478 if (rc != EOK) {
479 break;
480 }
481 }
482
483 async_answer_0(icall, EOK);
484}
485
486/** UDP service callback connection.
487 *
488 * @param icall Connect message
489 * @param arg Argument, UDP client
490 *
491 */
492static void udp_cb_conn(ipc_call_t *icall, void *arg)
493{
494 udp_t *udp = (udp_t *)arg;
495
496 async_answer_0(icall, EOK);
497
498 while (true) {
499 ipc_call_t call;
500 async_get_call(&call);
501
502 if (!IPC_GET_IMETHOD(call)) {
503 /* Hangup */
504 goto out;
505 }
506
507 switch (IPC_GET_IMETHOD(call)) {
508 case UDP_EV_DATA:
509 udp_ev_data(udp, &call);
510 break;
511 default:
512 async_answer_0(&call, ENOTSUP);
513 break;
514 }
515 }
516
517out:
518 fibril_mutex_lock(&udp->lock);
519 udp->cb_done = true;
520 fibril_mutex_unlock(&udp->lock);
521 fibril_condvar_broadcast(&udp->cv);
522}
523
524/** @}
525 */
Note: See TracBrowser for help on using the repository browser.