source: mainline/uspace/lib/c/generic/inet/udp.c@ 2133e02

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2133e02 was b10460a, checked in by Jiri Svoboda <jiri@…>, 10 years ago

Add missing docblocks in network code.

  • Property mode set to 100644
File size: 11.3 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_callid_t, ipc_call_t *, void *);
44
45/** Create callback connection from UDP service.
46 *
47 * @param udp UDP service
48 * @return EOK on success or negative error code
49 */
50static int 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 int rc = async_connect_to_me(exch, 0, 0, 0, udp_cb_conn, udp);
56 async_exchange_end(exch);
57
58 if (rc != EOK)
59 return rc;
60
61 sysarg_t retval;
62 async_wait_for(req, &retval);
63
64 return retval;
65}
66
67/** Create UDP client instance.
68 *
69 * @param rudp Place to store pointer to new UDP client
70 * @return EOK on success, ENOMEM if out of memory, EIO if service
71 * cannot be contacted
72 */
73int udp_create(udp_t **rudp)
74{
75 udp_t *udp;
76 service_id_t udp_svcid;
77 int rc;
78
79 udp = calloc(1, sizeof(udp_t));
80 if (udp == NULL) {
81 rc = ENOMEM;
82 goto error;
83 }
84
85 list_initialize(&udp->assoc);
86 fibril_mutex_initialize(&udp->lock);
87 fibril_condvar_initialize(&udp->cv);
88
89 rc = loc_service_get_id(SERVICE_NAME_UDP, &udp_svcid,
90 IPC_FLAG_BLOCKING);
91 if (rc != EOK) {
92 rc = EIO;
93 goto error;
94 }
95
96 udp->sess = loc_service_connect(EXCHANGE_SERIALIZE, udp_svcid,
97 IPC_FLAG_BLOCKING);
98 if (udp->sess == NULL) {
99 rc = EIO;
100 goto error;
101 }
102
103 rc = udp_callback_create(udp);
104 if (rc != EOK) {
105 rc = EIO;
106 goto error;
107 }
108
109 *rudp = udp;
110 return EOK;
111error:
112 free(udp);
113 return rc;
114}
115
116/** Destroy UDP client instance.
117 *
118 * @param udp UDP client
119 */
120void udp_destroy(udp_t *udp)
121{
122 if (udp == NULL)
123 return;
124
125 async_hangup(udp->sess);
126
127 fibril_mutex_lock(&udp->lock);
128 while (!udp->cb_done)
129 fibril_condvar_wait(&udp->cv, &udp->lock);
130 fibril_mutex_unlock(&udp->lock);
131
132 free(udp);
133}
134
135/** Create new UDP association.
136 *
137 * Create a UDP association that allows sending and receiving messages.
138 *
139 * @a epp may specify remote address and port, in which case only messages
140 * from that remote endpoint will be received. Also, that remote endpoint
141 * is used as default when @c NULL is passed as destination to
142 * udp_assoc_send_msg.
143 *
144 * @a epp may specify a local link or address. If it does not, the association
145 * will listen on all local links/addresses. If @a epp does not specify
146 * a local port number, a free dynamic port number will be allocated.
147 *
148 * The caller is informed about incoming data by invoking @a cb->recv_msg
149 *
150 * @param udp UDP client
151 * @param epp Internet endpoint pair
152 * @param cb Callbacks
153 * @param arg Argument to callbacks
154 * @param rassoc Place to store pointer to new association
155 *
156 * @return EOK on success or negative error code.
157 */
158int udp_assoc_create(udp_t *udp, inet_ep2_t *epp, udp_cb_t *cb, void *arg,
159 udp_assoc_t **rassoc)
160{
161 async_exch_t *exch;
162 udp_assoc_t *assoc;
163 ipc_call_t answer;
164
165 assoc = calloc(1, sizeof(udp_assoc_t));
166 if (assoc == NULL)
167 return ENOMEM;
168
169 exch = async_exchange_begin(udp->sess);
170 aid_t req = async_send_0(exch, UDP_ASSOC_CREATE, &answer);
171 sysarg_t rc = async_data_write_start(exch, (void *)epp,
172 sizeof(inet_ep2_t));
173 async_exchange_end(exch);
174
175 if (rc != EOK) {
176 sysarg_t rc_orig;
177 async_wait_for(req, &rc_orig);
178 if (rc_orig != EOK)
179 rc = rc_orig;
180 goto error;
181 }
182
183 async_wait_for(req, &rc);
184 if (rc != EOK)
185 goto error;
186
187 assoc->udp = udp;
188 assoc->id = IPC_GET_ARG1(answer);
189 assoc->cb = cb;
190 assoc->cb_arg = arg;
191
192 list_append(&assoc->ludp, &udp->assoc);
193 *rassoc = assoc;
194
195 return EOK;
196error:
197 free(assoc);
198 return (int) rc;
199}
200
201/** Destroy UDP association.
202 *
203 * Destroy UDP association. The caller should destroy all associations
204 * he created before destroying the UDP client and before terminating.
205 *
206 * @param assoc UDP association
207 */
208void udp_assoc_destroy(udp_assoc_t *assoc)
209{
210 async_exch_t *exch;
211
212 if (assoc == NULL)
213 return;
214
215 list_remove(&assoc->ludp);
216
217 exch = async_exchange_begin(assoc->udp->sess);
218 sysarg_t rc = async_req_1_0(exch, UDP_ASSOC_DESTROY, assoc->id);
219 async_exchange_end(exch);
220
221 free(assoc);
222 (void) rc;
223}
224
225/** Send message via UDP association.
226 *
227 * @param assoc Association
228 * @param dest Destination endpoint or @c NULL to use association's remote ep.
229 * @param data Message data
230 * @param bytes Message size in bytes
231 *
232 * @return EOK on success or negative error code
233 */
234int udp_assoc_send_msg(udp_assoc_t *assoc, inet_ep_t *dest, void *data,
235 size_t bytes)
236{
237 async_exch_t *exch;
238
239 exch = async_exchange_begin(assoc->udp->sess);
240 aid_t req = async_send_1(exch, UDP_ASSOC_SEND_MSG, assoc->id, NULL);
241
242 sysarg_t rc = async_data_write_start(exch, (void *)dest,
243 sizeof(inet_ep_t));
244 if (rc != EOK) {
245 async_exchange_end(exch);
246 async_forget(req);
247 return rc;
248 }
249
250 rc = async_data_write_start(exch, data, bytes);
251 if (rc != EOK) {
252 async_forget(req);
253 return rc;
254 }
255
256 async_exchange_end(exch);
257
258 if (rc != EOK) {
259 async_forget(req);
260 return rc;
261 }
262
263 async_wait_for(req, &rc);
264 return rc;
265}
266
267/** Get the user/callback argument for an association.
268 *
269 * @param assoc UDP association
270 * @return User argument associated with association
271 */
272void *udp_assoc_userptr(udp_assoc_t *assoc)
273{
274 return assoc->cb_arg;
275}
276
277/** Get size of received message in bytes.
278 *
279 * Assuming jumbo messages can be received, the caller first needs to determine
280 * the size of the received message by calling this function, then they can
281 * read the message piece-wise using udp_rmsg_read().
282 *
283 * @param rmsg Received message
284 * @return Size of received message in bytes
285 */
286size_t udp_rmsg_size(udp_rmsg_t *rmsg)
287{
288 return rmsg->size;
289}
290
291/** Read part of received message.
292 *
293 * @param rmsg Received message
294 * @param off Start offset
295 * @param buf Buffer for storing data
296 * @param bsize Buffer size
297 *
298 * @return EOK on success or negative error code.
299 */
300int udp_rmsg_read(udp_rmsg_t *rmsg, size_t off, void *buf, size_t bsize)
301{
302 async_exch_t *exch;
303 ipc_call_t answer;
304
305 exch = async_exchange_begin(rmsg->udp->sess);
306 aid_t req = async_send_1(exch, UDP_RMSG_READ, off, &answer);
307 int rc = async_data_read_start(exch, buf, bsize);
308 async_exchange_end(exch);
309
310 if (rc != EOK) {
311 async_forget(req);
312 return rc;
313 }
314
315 sysarg_t retval;
316 async_wait_for(req, &retval);
317 if (retval != EOK) {
318 return retval;
319 }
320
321 return EOK;
322}
323
324/** Get remote endpoint of received message.
325 *
326 * Place the remote endpoint (the one from which the message was supposedly
327 * sent) to @a ep.
328 *
329 * @param rmsg Received message
330 * @param ep Place to store remote endpoint
331 */
332void udp_rmsg_remote_ep(udp_rmsg_t *rmsg, inet_ep_t *ep)
333{
334 *ep = rmsg->remote_ep;
335}
336
337/** Get type of received ICMP error message.
338 *
339 * @param rerr Received error message
340 * @return Error message type
341 */
342uint8_t udp_rerr_type(udp_rerr_t *rerr)
343{
344 return 0;
345}
346
347/** Get code of received ICMP error message.
348 *
349 * @param rerr Received error message
350 * @return Error message code
351 */
352uint8_t udp_rerr_code(udp_rerr_t *rerr)
353{
354 return 0;
355}
356
357/** Get information about the next received message from UDP service.
358 *
359 * @param udp UDP client
360 * @param rmsg Place to store message information
361 *
362 * @return EOK on success or negative error code
363 */
364static int udp_rmsg_info(udp_t *udp, udp_rmsg_t *rmsg)
365{
366 async_exch_t *exch;
367 inet_ep_t ep;
368 ipc_call_t answer;
369
370 exch = async_exchange_begin(udp->sess);
371 aid_t req = async_send_0(exch, UDP_RMSG_INFO, &answer);
372 int rc = async_data_read_start(exch, &ep, sizeof(inet_ep_t));
373 async_exchange_end(exch);
374
375 if (rc != EOK) {
376 async_forget(req);
377 return rc;
378 }
379
380 sysarg_t retval;
381 async_wait_for(req, &retval);
382 if (retval != EOK)
383 return retval;
384
385 rmsg->udp = udp;
386 rmsg->assoc_id = IPC_GET_ARG1(answer);
387 rmsg->size = IPC_GET_ARG2(answer);
388 rmsg->remote_ep = ep;
389 return EOK;
390}
391
392/** Discard next received message in UDP service.
393 *
394 * @param udp UDP client
395 * @return EOK on success or negative error code
396 */
397static int udp_rmsg_discard(udp_t *udp)
398{
399 async_exch_t *exch;
400
401 exch = async_exchange_begin(udp->sess);
402 sysarg_t rc = async_req_0_0(exch, UDP_RMSG_DISCARD);
403 async_exchange_end(exch);
404
405 return rc;
406}
407
408/** Get association based on its ID.
409 *
410 * @param udp UDP client
411 * @param id Association ID
412 * @param rassoc Place to store pointer to association
413 *
414 * @return EOK on success, EINVAL if no association with the given ID exists
415 */
416static int udp_assoc_get(udp_t *udp, sysarg_t id, udp_assoc_t **rassoc)
417{
418 list_foreach(udp->assoc, ludp, udp_assoc_t, assoc) {
419 if (assoc->id == id) {
420 *rassoc = assoc;
421 return EOK;
422 }
423 }
424
425 return EINVAL;
426}
427
428/** Handle 'data' event, i.e. some message(s) arrived.
429 *
430 * For each received message, get information about it, call @c recv_msg
431 * callback and discard it.
432 *
433 * @param udp UDP client
434 * @param iid IPC message ID
435 * @param icall IPC message
436 */
437static void udp_ev_data(udp_t *udp, ipc_callid_t iid, ipc_call_t *icall)
438{
439 udp_rmsg_t rmsg;
440 udp_assoc_t *assoc;
441 int rc;
442
443 while (true) {
444 rc = udp_rmsg_info(udp, &rmsg);
445 if (rc != EOK) {
446 break;
447 }
448
449 rc = udp_assoc_get(udp, rmsg.assoc_id, &assoc);
450 if (rc != EOK) {
451 continue;
452 }
453
454 if (assoc->cb != NULL && assoc->cb->recv_msg != NULL)
455 assoc->cb->recv_msg(assoc, &rmsg);
456
457 rc = udp_rmsg_discard(udp);
458 if (rc != EOK) {
459 break;
460 }
461 }
462
463 async_answer_0(iid, EOK);
464}
465
466/** UDP service callback connection.
467 *
468 * @param iid Connect message ID
469 * @param icall Connect message
470 * @param arg Argument, UDP client
471 */
472static void udp_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
473{
474 udp_t *udp = (udp_t *)arg;
475
476 async_answer_0(iid, EOK);
477
478 while (true) {
479 ipc_call_t call;
480 ipc_callid_t callid = async_get_call(&call);
481
482 if (!IPC_GET_IMETHOD(call)) {
483 /* Hangup */
484 goto out;
485 }
486
487 switch (IPC_GET_IMETHOD(call)) {
488 case UDP_EV_DATA:
489 udp_ev_data(udp, callid, &call);
490 break;
491 default:
492 async_answer_0(callid, ENOTSUP);
493 break;
494 }
495 }
496out:
497 fibril_mutex_lock(&udp->lock);
498 udp->cb_done = true;
499 fibril_mutex_unlock(&udp->lock);
500 fibril_condvar_broadcast(&udp->cv);
501}
502
503/** @}
504 */
Note: See TracBrowser for help on using the repository browser.