source: mainline/uspace/srv/net/udp/service.c@ 59ff52d

Last change on this file since 59ff52d was 59ff52d, checked in by Jakub Jermar <jakub@…>, 7 years ago

Add async_accept_0() for accepting connections

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