source: mainline/uspace/srv/net/udp/service.c@ 8d2dd7f2

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

Fix formatting strings.

  • Property mode set to 100644
File size: 16.5 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 udp
30 * @{
31 */
32
33/**
34 * @file HelenOS service implementation
35 */
36
37#include <async.h>
38#include <errno.h>
39#include <inet/endpoint.h>
40#include <io/log.h>
41#include <ipc/services.h>
42#include <ipc/udp.h>
43#include <loc.h>
44#include <macros.h>
45#include <stdlib.h>
46
47#include "assoc.h"
48#include "msg.h"
49#include "service.h"
50#include "udp_type.h"
51
52#define NAME "udp"
53
54/** Maximum message size */
55#define MAX_MSG_SIZE DATA_XFER_LIMIT
56
57static void udp_cassoc_recv_msg(void *, inet_ep2_t *, udp_msg_t *);
58
59/** Callbacks to tie us to association layer */
60static udp_assoc_cb_t udp_cassoc_cb = {
61 .recv_msg = udp_cassoc_recv_msg
62};
63
64/** Add message to client receive queue.
65 *
66 * @param cassoc Client association
67 * @param epp Endpoint pair on which message was received
68 * @param msg Message
69 *
70 * @return EOK on success, ENOMEM if out of memory
71 */
72static int udp_cassoc_queue_msg(udp_cassoc_t *cassoc, inet_ep2_t *epp,
73 udp_msg_t *msg)
74{
75 udp_crcv_queue_entry_t *rqe;
76
77 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_cassoc_queue_msg(%p, %p, %p)",
78 cassoc, epp, msg);
79
80 rqe = calloc(1, sizeof(udp_crcv_queue_entry_t));
81 if (rqe == NULL)
82 return ENOMEM;
83
84 link_initialize(&rqe->link);
85 rqe->epp = *epp;
86 rqe->msg = msg;
87 rqe->cassoc = cassoc;
88
89// fibril_mutex_lock(&assoc->lock);
90 list_append(&rqe->link, &cassoc->client->crcv_queue);
91// fibril_mutex_unlock(&assoc->lock);
92
93// fibril_condvar_broadcast(&assoc->rcv_queue_cv);
94
95 return EOK;
96}
97
98/** Send 'data' event to client.
99 *
100 * @param client Client
101 */
102static void udp_ev_data(udp_client_t *client)
103{
104 async_exch_t *exch;
105
106 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_ev_data()");
107
108 exch = async_exchange_begin(client->sess);
109 aid_t req = async_send_0(exch, UDP_EV_DATA, NULL);
110 async_exchange_end(exch);
111
112 async_forget(req);
113}
114
115/** Create client association.
116 *
117 * This effectively adds an association into a client's namespace.
118 *
119 * @param client Client
120 * @param assoc Association
121 * @param rcassoc Place to store pointer to new client association
122 *
123 * @return EOK on soccess, ENOMEM if out of memory
124 */
125static int udp_cassoc_create(udp_client_t *client, udp_assoc_t *assoc,
126 udp_cassoc_t **rcassoc)
127{
128 udp_cassoc_t *cassoc;
129 sysarg_t id;
130
131 cassoc = calloc(1, sizeof(udp_cassoc_t));
132 if (cassoc == NULL)
133 return ENOMEM;
134
135 /* Allocate new ID */
136 id = 0;
137 list_foreach (client->cassoc, lclient, udp_cassoc_t, cassoc) {
138 if (cassoc->id >= id)
139 id = cassoc->id + 1;
140 }
141
142 cassoc->id = id;
143 cassoc->client = client;
144 cassoc->assoc = assoc;
145
146 list_append(&cassoc->lclient, &client->cassoc);
147 *rcassoc = cassoc;
148 return EOK;
149}
150
151/** Destroy client association.
152 *
153 * @param cassoc Client association
154 */
155static void udp_cassoc_destroy(udp_cassoc_t *cassoc)
156{
157 list_remove(&cassoc->lclient);
158 free(cassoc);
159}
160
161/** Get client association by ID.
162 *
163 * @param client Client
164 * @param id Client association ID
165 * @param rcassoc Place to store pointer to client association
166 *
167 * @return EOK on success, ENOENT if no client association with the given ID
168 * is found.
169 */
170static int udp_cassoc_get(udp_client_t *client, sysarg_t id,
171 udp_cassoc_t **rcassoc)
172{
173 list_foreach (client->cassoc, lclient, udp_cassoc_t, cassoc) {
174 if (cassoc->id == id) {
175 *rcassoc = cassoc;
176 return EOK;
177 }
178 }
179
180 return ENOENT;
181}
182
183/** Message received on client association.
184 *
185 * Used as udp_assoc_cb.recv_msg callback.
186 *
187 * @param arg Callback argument, client association
188 * @param epp Endpoint pair where message was received
189 * @param msg Message
190 */
191static void udp_cassoc_recv_msg(void *arg, inet_ep2_t *epp, udp_msg_t *msg)
192{
193 udp_cassoc_t *cassoc = (udp_cassoc_t *) arg;
194
195 udp_cassoc_queue_msg(cassoc, epp, msg);
196 udp_ev_data(cassoc->client);
197}
198
199/** Create association.
200 *
201 * Handle client request to create association (with parameters unmarshalled).
202 *
203 * @param client UDP client
204 * @param epp Endpoint pair
205 * @param rassoc_id Place to store ID of new association
206 *
207 * @return EOK on success or negative error code
208 */
209static int udp_assoc_create_impl(udp_client_t *client, inet_ep2_t *epp,
210 sysarg_t *rassoc_id)
211{
212 udp_assoc_t *assoc;
213 udp_cassoc_t *cassoc;
214 int rc;
215
216 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_create_impl");
217
218 assoc = udp_assoc_new(epp, NULL, NULL);
219 if (assoc == NULL)
220 return EIO;
221
222 if (epp->local_link != 0)
223 udp_assoc_set_iplink(assoc, epp->local_link);
224
225 rc = udp_cassoc_create(client, assoc, &cassoc);
226 if (rc != EOK) {
227 assert(rc == ENOMEM);
228 udp_assoc_delete(assoc);
229 return ENOMEM;
230 }
231
232 assoc->cb = &udp_cassoc_cb;
233 assoc->cb_arg = cassoc;
234
235 rc = udp_assoc_add(assoc);
236 if (rc != EOK) {
237 udp_cassoc_destroy(cassoc);
238 udp_assoc_delete(assoc);
239 return rc;
240 }
241
242 *rassoc_id = cassoc->id;
243 return EOK;
244}
245
246/** Destroy association.
247 *
248 * Handle client request to destroy association (with parameters unmarshalled).
249 *
250 * @param client UDP client
251 * @param assoc_id Association ID
252 * @return EOK on success, ENOENT if no such association is found
253 */
254static int udp_assoc_destroy_impl(udp_client_t *client, sysarg_t assoc_id)
255{
256 udp_cassoc_t *cassoc;
257 int rc;
258
259 rc = udp_cassoc_get(client, assoc_id, &cassoc);
260 if (rc != EOK) {
261 assert(rc == ENOENT);
262 return ENOENT;
263 }
264
265 udp_assoc_remove(cassoc->assoc);
266 udp_assoc_reset(cassoc->assoc);
267 udp_assoc_delete(cassoc->assoc);
268 udp_cassoc_destroy(cassoc);
269 return EOK;
270}
271
272/** Send message via association.
273 *
274 * Handle client request to send message (with parameters unmarshalled).
275 *
276 * @param client UDP client
277 * @param assoc_id Association ID
278 * @param dest Destination endpoint or @c NULL to use the default from
279 * association
280 * @param data Message data
281 * @param size Message size
282 *
283 * @return EOK on success or negative error code
284 */
285static int udp_assoc_send_msg_impl(udp_client_t *client, sysarg_t assoc_id,
286 inet_ep_t *dest, void *data, size_t size)
287{
288 udp_msg_t msg;
289 udp_cassoc_t *cassoc;
290 int rc;
291
292 rc = udp_cassoc_get(client, assoc_id, &cassoc);
293 if (rc != EOK)
294 return rc;
295
296 msg.data = data;
297 msg.data_size = size;
298 rc = udp_assoc_send(cassoc->assoc, dest, &msg);
299 if (rc != EOK)
300 return rc;
301
302 return EOK;
303}
304
305/** Create callback session.
306 *
307 * Handle client request to create callback session.
308 *
309 * @param client UDP client
310 * @param iid Async request ID
311 * @param icall Async request data
312 */
313static void udp_callback_create_srv(udp_client_t *client, ipc_callid_t iid,
314 ipc_call_t *icall)
315{
316 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_callback_create_srv()");
317
318 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
319 if (sess == NULL) {
320 async_answer_0(iid, ENOMEM);
321 return;
322 }
323
324 client->sess = sess;
325 async_answer_0(iid, EOK);
326}
327
328/** Create association.
329 *
330 * Handle client request to create association.
331 *
332 * @param client UDP client
333 * @param iid Async request ID
334 * @param icall Async request data
335 */
336static void udp_assoc_create_srv(udp_client_t *client, ipc_callid_t iid,
337 ipc_call_t *icall)
338{
339 ipc_callid_t callid;
340 size_t size;
341 inet_ep2_t epp;
342 sysarg_t assoc_id;
343 int rc;
344
345 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_create_srv()");
346
347 if (!async_data_write_receive(&callid, &size)) {
348 async_answer_0(callid, EREFUSED);
349 async_answer_0(iid, EREFUSED);
350 return;
351 }
352
353 if (size != sizeof(inet_ep2_t)) {
354 async_answer_0(callid, EINVAL);
355 async_answer_0(iid, EINVAL);
356 return;
357 }
358
359 rc = async_data_write_finalize(callid, &epp, size);
360 if (rc != EOK) {
361 async_answer_0(callid, rc);
362 async_answer_0(iid, rc);
363 return;
364 }
365
366 rc = udp_assoc_create_impl(client, &epp, &assoc_id);
367 if (rc != EOK) {
368 async_answer_0(iid, rc);
369 return;
370 }
371
372 async_answer_1(iid, EOK, assoc_id);
373}
374
375/** Destroy association.
376 *
377 * Handle client request to destroy association.
378 *
379 * @param client UDP client
380 * @param iid Async request ID
381 * @param icall Async request data
382 */
383static void udp_assoc_destroy_srv(udp_client_t *client, ipc_callid_t iid,
384 ipc_call_t *icall)
385{
386 sysarg_t assoc_id;
387 int rc;
388
389 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_destroy_srv()");
390
391 assoc_id = IPC_GET_ARG1(*icall);
392 rc = udp_assoc_destroy_impl(client, assoc_id);
393 async_answer_0(iid, rc);
394}
395
396/** Send message via association.
397 *
398 * Handle client request to send message.
399 *
400 * @param client UDP client
401 * @param iid Async request ID
402 * @param icall Async request data
403 */
404static void udp_assoc_send_msg_srv(udp_client_t *client, ipc_callid_t iid,
405 ipc_call_t *icall)
406{
407 ipc_callid_t callid;
408 size_t size;
409 inet_ep_t dest;
410 sysarg_t assoc_id;
411 void *data;
412 int rc;
413
414 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send_msg_srv()");
415
416 /* Receive dest */
417
418 if (!async_data_write_receive(&callid, &size)) {
419 async_answer_0(callid, EREFUSED);
420 async_answer_0(iid, EREFUSED);
421 return;
422 }
423
424 if (size != sizeof(inet_ep_t)) {
425 async_answer_0(callid, EINVAL);
426 async_answer_0(iid, EINVAL);
427 return;
428 }
429
430 rc = async_data_write_finalize(callid, &dest, size);
431 if (rc != EOK) {
432 async_answer_0(callid, rc);
433 async_answer_0(iid, rc);
434 return;
435 }
436
437 /* Receive message data */
438
439 if (!async_data_write_receive(&callid, &size)) {
440 async_answer_0(callid, EREFUSED);
441 async_answer_0(iid, EREFUSED);
442 return;
443 }
444
445 if (size > MAX_MSG_SIZE) {
446 async_answer_0(callid, EINVAL);
447 async_answer_0(iid, EINVAL);
448 return;
449 }
450
451 data = malloc(size);
452 if (data == NULL) {
453 async_answer_0(callid, ENOMEM);
454 async_answer_0(iid, ENOMEM);
455 }
456
457 rc = async_data_write_finalize(callid, data, size);
458 if (rc != EOK) {
459 async_answer_0(callid, rc);
460 async_answer_0(iid, rc);
461 free(data);
462 return;
463 }
464
465 assoc_id = IPC_GET_ARG1(*icall);
466
467 rc = udp_assoc_send_msg_impl(client, assoc_id, &dest, data, size);
468 if (rc != EOK) {
469 async_answer_0(iid, rc);
470 free(data);
471 return;
472 }
473
474 async_answer_0(iid, EOK);
475 free(data);
476}
477
478/** Get next received message.
479 *
480 * @param client UDP Client
481 * @return Pointer to queue entry for next received message
482 */
483static udp_crcv_queue_entry_t *udp_rmsg_get_next(udp_client_t *client)
484{
485 link_t *link;
486
487 link = list_first(&client->crcv_queue);
488 if (link == NULL)
489 return NULL;
490
491 return list_get_instance(link, udp_crcv_queue_entry_t, link);
492}
493
494/** Get info on first received message.
495 *
496 * Handle client request to get information on received message.
497 *
498 * @param client UDP client
499 * @param iid Async request ID
500 * @param icall Async request data
501 */
502static void udp_rmsg_info_srv(udp_client_t *client, ipc_callid_t iid,
503 ipc_call_t *icall)
504{
505 ipc_callid_t callid;
506 size_t size;
507 udp_crcv_queue_entry_t *enext;
508 sysarg_t assoc_id;
509 int rc;
510
511 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_info_srv()");
512 enext = udp_rmsg_get_next(client);
513
514 if (!async_data_read_receive(&callid, &size)) {
515 async_answer_0(callid, EREFUSED);
516 async_answer_0(iid, EREFUSED);
517 return;
518 }
519
520 if (enext == NULL) {
521 async_answer_0(callid, ENOENT);
522 async_answer_0(iid, ENOENT);
523 return;
524 }
525
526 rc = async_data_read_finalize(callid, &enext->epp.remote,
527 max(size, (ssize_t)sizeof(inet_ep_t)));
528 if (rc != EOK) {
529 async_answer_0(iid, rc);
530 return;
531 }
532
533 assoc_id = enext->cassoc->id;
534 size = enext->msg->data_size;
535
536 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_info_srv(): assoc_id=%zu, "
537 "size=%zu", assoc_id, size);
538 async_answer_2(iid, EOK, assoc_id, size);
539}
540
541/** Read data from first received message.
542 *
543 * Handle client request to read data from first received message.
544 *
545 * @param client UDP client
546 * @param iid Async request ID
547 * @param icall Async request data
548 */
549static void udp_rmsg_read_srv(udp_client_t *client, ipc_callid_t iid,
550 ipc_call_t *icall)
551{
552 ipc_callid_t callid;
553 ssize_t msg_size;
554 udp_crcv_queue_entry_t *enext;
555 void *data;
556 size_t size;
557 ssize_t off;
558 int rc;
559
560 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_read_srv()");
561 off = IPC_GET_ARG1(*icall);
562
563 enext = udp_rmsg_get_next(client);
564
565 if (!async_data_read_receive(&callid, &size)) {
566 async_answer_0(callid, EREFUSED);
567 async_answer_0(iid, EREFUSED);
568 return;
569 }
570
571 if (enext == NULL) {
572 async_answer_0(callid, ENOENT);
573 async_answer_0(iid, ENOENT);
574 return;
575 }
576
577 data = enext->msg->data + off;
578 msg_size = enext->msg->data_size;
579
580 rc = async_data_read_finalize(callid, data, max(msg_size - off,
581 (ssize_t)size));
582 if (rc != EOK) {
583 async_answer_0(iid, rc);
584 return;
585 }
586
587 async_answer_0(iid, EOK);
588 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_read_srv(): OK");
589}
590
591/** Discard first received message.
592 *
593 * Handle client request to discard first received message, advancing
594 * to the next one.
595 *
596 * @param client UDP client
597 * @param iid Async request ID
598 * @param icall Async request data
599 */
600static void udp_rmsg_discard_srv(udp_client_t *client, ipc_callid_t iid,
601 ipc_call_t *icall)
602{
603 udp_crcv_queue_entry_t *enext;
604
605 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_discard_srv()");
606
607 enext = udp_rmsg_get_next(client);
608 if (enext == NULL) {
609 log_msg(LOG_DEFAULT, LVL_DEBUG, "usg_rmsg_discard_srv: enext==NULL");
610 async_answer_0(iid, ENOENT);
611 return;
612 }
613
614 list_remove(&enext->link);
615 udp_msg_delete(enext->msg);
616 free(enext);
617 async_answer_0(iid, EOK);
618}
619
620/** Handle UDP client connection.
621 *
622 * @param iid Connect call ID
623 * @param icall Connect call data
624 * @param arg Connection argument
625 */
626static void udp_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
627{
628 udp_client_t client;
629 unsigned long n;
630
631 /* Accept the connection */
632 async_answer_0(iid, EOK);
633
634 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn()");
635
636 client.sess = NULL;
637 list_initialize(&client.cassoc);
638 list_initialize(&client.crcv_queue);
639
640 while (true) {
641 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: wait req");
642 ipc_call_t call;
643 ipc_callid_t callid = async_get_call(&call);
644 sysarg_t method = IPC_GET_IMETHOD(call);
645
646 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: method=%d",
647 (int)method);
648 if (!method) {
649 /* The other side has hung up */
650 async_answer_0(callid, EOK);
651 break;
652 }
653
654 switch (method) {
655 case UDP_CALLBACK_CREATE:
656 udp_callback_create_srv(&client, callid, &call);
657 break;
658 case UDP_ASSOC_CREATE:
659 udp_assoc_create_srv(&client, callid, &call);
660 break;
661 case UDP_ASSOC_DESTROY:
662 udp_assoc_destroy_srv(&client, callid, &call);
663 break;
664 case UDP_ASSOC_SEND_MSG:
665 udp_assoc_send_msg_srv(&client, callid, &call);
666 break;
667 case UDP_RMSG_INFO:
668 udp_rmsg_info_srv(&client, callid, &call);
669 break;
670 case UDP_RMSG_READ:
671 udp_rmsg_read_srv(&client, callid, &call);
672 break;
673 case UDP_RMSG_DISCARD:
674 udp_rmsg_discard_srv(&client, callid, &call);
675 break;
676 default:
677 async_answer_0(callid, ENOTSUP);
678 break;
679 }
680 }
681
682 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: terminated");
683
684 n = list_count(&client.cassoc);
685 if (n != 0) {
686 log_msg(LOG_DEFAULT, LVL_WARN, "udp_client_conn: "
687 "Client with %lu active associations closed session.", n);
688 /* XXX Clean up */
689 }
690
691 /* XXX Clean up client receive queue */
692
693 if (client.sess != NULL)
694 async_hangup(client.sess);
695}
696
697/** Initialize UDP service.
698 *
699 * @return EOK on success or negative error code.
700 */
701int udp_service_init(void)
702{
703 int rc;
704 service_id_t sid;
705
706 async_set_fallback_port_handler(udp_client_conn, NULL);
707
708 rc = loc_server_register(NAME);
709 if (rc != EOK) {
710 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server.");
711 return EIO;
712 }
713
714 rc = loc_service_register(SERVICE_NAME_UDP, &sid);
715 if (rc != EOK) {
716 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service.");
717 return EIO;
718 }
719
720 return EOK;
721}
722
723/**
724 * @}
725 */
Note: See TracBrowser for help on using the repository browser.