Strip trailing whitespace.
[helenos.git] / uspace / srv / net / udp / service.c
blobf89b754e486ed98c4b2f50914a0453a8eb897cf3
1 /*
2 * Copyright (c) 2015 Jiri Svoboda
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
29 /** @addtogroup udp
30 * @{
33 /**
34 * @file HelenOS service implementation
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>
47 #include "assoc.h"
48 #include "msg.h"
49 #include "service.h"
50 #include "udp_type.h"
52 #define NAME "udp"
54 /** Maximum message size */
55 #define MAX_MSG_SIZE DATA_XFER_LIMIT
57 static void udp_cassoc_recv_msg(void *, inet_ep2_t *, udp_msg_t *);
59 /** Callbacks to tie us to association layer */
60 static udp_assoc_cb_t udp_cassoc_cb = {
61 .recv_msg = udp_cassoc_recv_msg
64 /** Add message to client receive queue.
66 * @param cassoc Client association
67 * @param epp Endpoint pair on which message was received
68 * @param msg Message
70 * @return EOK on success, ENOMEM if out of memory
72 static errno_t udp_cassoc_queue_msg(udp_cassoc_t *cassoc, inet_ep2_t *epp,
73 udp_msg_t *msg)
75 udp_crcv_queue_entry_t *rqe;
77 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_cassoc_queue_msg(%p, %p, %p)",
78 cassoc, epp, msg);
80 rqe = calloc(1, sizeof(udp_crcv_queue_entry_t));
81 if (rqe == NULL)
82 return ENOMEM;
84 link_initialize(&rqe->link);
85 rqe->epp = *epp;
86 rqe->msg = msg;
87 rqe->cassoc = cassoc;
89 // fibril_mutex_lock(&assoc->lock);
90 list_append(&rqe->link, &cassoc->client->crcv_queue);
91 // fibril_mutex_unlock(&assoc->lock);
93 // fibril_condvar_broadcast(&assoc->rcv_queue_cv);
95 return EOK;
98 /** Send 'data' event to client.
100 * @param client Client
102 static void udp_ev_data(udp_client_t *client)
104 async_exch_t *exch;
106 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_ev_data()");
108 exch = async_exchange_begin(client->sess);
109 aid_t req = async_send_0(exch, UDP_EV_DATA, NULL);
110 async_exchange_end(exch);
112 async_forget(req);
115 /** Create client association.
117 * This effectively adds an association into a client's namespace.
119 * @param client Client
120 * @param assoc Association
121 * @param rcassoc Place to store pointer to new client association
123 * @return EOK on soccess, ENOMEM if out of memory
125 static errno_t udp_cassoc_create(udp_client_t *client, udp_assoc_t *assoc,
126 udp_cassoc_t **rcassoc)
128 udp_cassoc_t *cassoc;
129 sysarg_t id;
131 cassoc = calloc(1, sizeof(udp_cassoc_t));
132 if (cassoc == NULL)
133 return ENOMEM;
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;
142 cassoc->id = id;
143 cassoc->client = client;
144 cassoc->assoc = assoc;
146 list_append(&cassoc->lclient, &client->cassoc);
147 *rcassoc = cassoc;
148 return EOK;
151 /** Destroy client association.
153 * @param cassoc Client association
155 static void udp_cassoc_destroy(udp_cassoc_t *cassoc)
157 list_remove(&cassoc->lclient);
158 free(cassoc);
161 /** Get client association by ID.
163 * @param client Client
164 * @param id Client association ID
165 * @param rcassoc Place to store pointer to client association
167 * @return EOK on success, ENOENT if no client association with the given ID
168 * is found.
170 static errno_t udp_cassoc_get(udp_client_t *client, sysarg_t id,
171 udp_cassoc_t **rcassoc)
173 list_foreach (client->cassoc, lclient, udp_cassoc_t, cassoc) {
174 if (cassoc->id == id) {
175 *rcassoc = cassoc;
176 return EOK;
180 return ENOENT;
183 /** Message received on client association.
185 * Used as udp_assoc_cb.recv_msg callback.
187 * @param arg Callback argument, client association
188 * @param epp Endpoint pair where message was received
189 * @param msg Message
191 static void udp_cassoc_recv_msg(void *arg, inet_ep2_t *epp, udp_msg_t *msg)
193 udp_cassoc_t *cassoc = (udp_cassoc_t *) arg;
195 udp_cassoc_queue_msg(cassoc, epp, msg);
196 udp_ev_data(cassoc->client);
199 /** Create association.
201 * Handle client request to create association (with parameters unmarshalled).
203 * @param client UDP client
204 * @param epp Endpoint pair
205 * @param rassoc_id Place to store ID of new association
207 * @return EOK on success or an error code
209 static errno_t udp_assoc_create_impl(udp_client_t *client, inet_ep2_t *epp,
210 sysarg_t *rassoc_id)
212 udp_assoc_t *assoc;
213 udp_cassoc_t *cassoc;
214 errno_t rc;
216 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_create_impl");
218 assoc = udp_assoc_new(epp, NULL, NULL);
219 if (assoc == NULL)
220 return EIO;
222 if (epp->local_link != 0)
223 udp_assoc_set_iplink(assoc, epp->local_link);
225 rc = udp_cassoc_create(client, assoc, &cassoc);
226 if (rc != EOK) {
227 assert(rc == ENOMEM);
228 udp_assoc_delete(assoc);
229 return ENOMEM;
232 assoc->cb = &udp_cassoc_cb;
233 assoc->cb_arg = cassoc;
235 rc = udp_assoc_add(assoc);
236 if (rc != EOK) {
237 udp_cassoc_destroy(cassoc);
238 udp_assoc_delete(assoc);
239 return rc;
242 *rassoc_id = cassoc->id;
243 return EOK;
246 /** Destroy association.
248 * Handle client request to destroy association (with parameters unmarshalled).
250 * @param client UDP client
251 * @param assoc_id Association ID
252 * @return EOK on success, ENOENT if no such association is found
254 static errno_t udp_assoc_destroy_impl(udp_client_t *client, sysarg_t assoc_id)
256 udp_cassoc_t *cassoc;
257 errno_t rc;
259 rc = udp_cassoc_get(client, assoc_id, &cassoc);
260 if (rc != EOK) {
261 assert(rc == ENOENT);
262 return ENOENT;
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;
272 /** Set association sending messages with no local address.
274 * Handle client request to set nolocal flag (with parameters unmarshalled).
276 * @param client UDP client
277 * @param assoc_id Association ID
278 * @return EOK on success, ENOENT if no such association is found
280 static errno_t udp_assoc_set_nolocal_impl(udp_client_t *client, sysarg_t assoc_id)
282 udp_cassoc_t *cassoc;
283 errno_t rc;
285 rc = udp_cassoc_get(client, assoc_id, &cassoc);
286 if (rc != EOK) {
287 assert(rc == ENOENT);
288 return ENOENT;
291 log_msg(LOG_DEFAULT, LVL_NOTE, "Setting nolocal to true");
292 cassoc->assoc->nolocal = true;
293 return EOK;
296 /** Send message via association.
298 * Handle client request to send message (with parameters unmarshalled).
300 * @param client UDP client
301 * @param assoc_id Association ID
302 * @param dest Destination endpoint or @c NULL to use the default from
303 * association
304 * @param data Message data
305 * @param size Message size
307 * @return EOK on success or an error code
309 static errno_t udp_assoc_send_msg_impl(udp_client_t *client, sysarg_t assoc_id,
310 inet_ep_t *dest, void *data, size_t size)
312 udp_msg_t msg;
313 udp_cassoc_t *cassoc;
314 errno_t rc;
316 rc = udp_cassoc_get(client, assoc_id, &cassoc);
317 if (rc != EOK)
318 return rc;
320 msg.data = data;
321 msg.data_size = size;
322 rc = udp_assoc_send(cassoc->assoc, dest, &msg);
323 if (rc != EOK)
324 return rc;
326 return EOK;
329 /** Create callback session.
331 * Handle client request to create callback session.
333 * @param client UDP client
334 * @param icall_handle Async request call handle
335 * @param icall Async request data
337 static void
338 udp_callback_create_srv(udp_client_t *client, cap_call_handle_t icall_handle,
339 ipc_call_t *icall)
341 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_callback_create_srv()");
343 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
344 if (sess == NULL) {
345 async_answer_0(icall_handle, ENOMEM);
346 return;
349 client->sess = sess;
350 async_answer_0(icall_handle, EOK);
353 /** Create association.
355 * Handle client request to create association.
357 * @param client UDP client
358 * @param icall_handle Async request call handle
359 * @param icall Async request data
361 static
362 void udp_assoc_create_srv(udp_client_t *client, cap_call_handle_t icall_handle,
363 ipc_call_t *icall)
365 cap_call_handle_t chandle;
366 size_t size;
367 inet_ep2_t epp;
368 sysarg_t assoc_id;
369 errno_t rc;
371 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_create_srv()");
373 if (!async_data_write_receive(&chandle, &size)) {
374 async_answer_0(chandle, EREFUSED);
375 async_answer_0(icall_handle, EREFUSED);
376 return;
379 if (size != sizeof(inet_ep2_t)) {
380 async_answer_0(chandle, EINVAL);
381 async_answer_0(icall_handle, EINVAL);
382 return;
385 rc = async_data_write_finalize(chandle, &epp, size);
386 if (rc != EOK) {
387 async_answer_0(chandle, rc);
388 async_answer_0(icall_handle, rc);
389 return;
392 rc = udp_assoc_create_impl(client, &epp, &assoc_id);
393 if (rc != EOK) {
394 async_answer_0(icall_handle, rc);
395 return;
398 async_answer_1(icall_handle, EOK, assoc_id);
401 /** Destroy association.
403 * Handle client request to destroy association.
405 * @param client UDP client
406 * @param icall_handle Async request call handle
407 * @param icall Async request data
409 static void
410 udp_assoc_destroy_srv(udp_client_t *client, cap_call_handle_t icall_handle,
411 ipc_call_t *icall)
413 sysarg_t assoc_id;
414 errno_t rc;
416 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_destroy_srv()");
418 assoc_id = IPC_GET_ARG1(*icall);
419 rc = udp_assoc_destroy_impl(client, assoc_id);
420 async_answer_0(icall_handle, rc);
423 /** Set association with no local address.
425 * Handle client request to set no local address flag.
427 * @param client UDP client
428 * @param icall_handle Async request call handle
429 * @param icall Async request data
431 static void
432 udp_assoc_set_nolocal_srv(udp_client_t *client, cap_call_handle_t icall_handle,
433 ipc_call_t *icall)
435 sysarg_t assoc_id;
436 errno_t rc;
438 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_set_nolocal_srv()");
440 assoc_id = IPC_GET_ARG1(*icall);
441 rc = udp_assoc_set_nolocal_impl(client, assoc_id);
442 async_answer_0(icall_handle, rc);
445 /** Send message via association.
447 * Handle client request to send message.
449 * @param client UDP client
450 * @param icall_handle Async request call handle
451 * @param icall Async request data
453 static void
454 udp_assoc_send_msg_srv(udp_client_t *client, cap_call_handle_t icall_handle,
455 ipc_call_t *icall)
457 cap_call_handle_t chandle;
458 size_t size;
459 inet_ep_t dest;
460 sysarg_t assoc_id;
461 void *data;
462 errno_t rc;
464 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send_msg_srv()");
466 /* Receive dest */
468 if (!async_data_write_receive(&chandle, &size)) {
469 async_answer_0(chandle, EREFUSED);
470 async_answer_0(icall_handle, EREFUSED);
471 return;
474 if (size != sizeof(inet_ep_t)) {
475 async_answer_0(chandle, EINVAL);
476 async_answer_0(icall_handle, EINVAL);
477 return;
480 rc = async_data_write_finalize(chandle, &dest, size);
481 if (rc != EOK) {
482 async_answer_0(chandle, rc);
483 async_answer_0(icall_handle, rc);
484 return;
487 /* Receive message data */
489 if (!async_data_write_receive(&chandle, &size)) {
490 async_answer_0(chandle, EREFUSED);
491 async_answer_0(icall_handle, EREFUSED);
492 return;
495 if (size > MAX_MSG_SIZE) {
496 async_answer_0(chandle, EINVAL);
497 async_answer_0(icall_handle, EINVAL);
498 return;
501 data = malloc(size);
502 if (data == NULL) {
503 async_answer_0(chandle, ENOMEM);
504 async_answer_0(icall_handle, ENOMEM);
507 rc = async_data_write_finalize(chandle, data, size);
508 if (rc != EOK) {
509 async_answer_0(chandle, rc);
510 async_answer_0(icall_handle, rc);
511 free(data);
512 return;
515 assoc_id = IPC_GET_ARG1(*icall);
517 rc = udp_assoc_send_msg_impl(client, assoc_id, &dest, data, size);
518 if (rc != EOK) {
519 async_answer_0(icall_handle, rc);
520 free(data);
521 return;
524 async_answer_0(icall_handle, EOK);
525 free(data);
528 /** Get next received message.
530 * @param client UDP Client
531 * @return Pointer to queue entry for next received message
533 static udp_crcv_queue_entry_t *udp_rmsg_get_next(udp_client_t *client)
535 link_t *link;
537 link = list_first(&client->crcv_queue);
538 if (link == NULL)
539 return NULL;
541 return list_get_instance(link, udp_crcv_queue_entry_t, link);
544 /** Get info on first received message.
546 * Handle client request to get information on received message.
548 * @param client UDP client
549 * @param icall_handle Async request call handle
550 * @param icall Async request data
552 static void
553 udp_rmsg_info_srv(udp_client_t *client, cap_call_handle_t icall_handle,
554 ipc_call_t *icall)
556 cap_call_handle_t chandle;
557 size_t size;
558 udp_crcv_queue_entry_t *enext;
559 sysarg_t assoc_id;
560 errno_t rc;
562 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_info_srv()");
563 enext = udp_rmsg_get_next(client);
565 if (!async_data_read_receive(&chandle, &size)) {
566 async_answer_0(chandle, EREFUSED);
567 async_answer_0(icall_handle, EREFUSED);
568 return;
571 if (enext == NULL) {
572 async_answer_0(chandle, ENOENT);
573 async_answer_0(icall_handle, ENOENT);
574 return;
577 rc = async_data_read_finalize(chandle, &enext->epp.remote,
578 max(size, (size_t)sizeof(inet_ep_t)));
579 if (rc != EOK) {
580 async_answer_0(icall_handle, rc);
581 return;
584 assoc_id = enext->cassoc->id;
585 size = enext->msg->data_size;
587 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_info_srv(): assoc_id=%zu, "
588 "size=%zu", assoc_id, size);
589 async_answer_2(icall_handle, EOK, assoc_id, size);
592 /** Read data from first received message.
594 * Handle client request to read data from first received message.
596 * @param client UDP client
597 * @param icall_handle Async request call handle
598 * @param icall Async request data
600 static void
601 udp_rmsg_read_srv(udp_client_t *client, cap_call_handle_t icall_handle,
602 ipc_call_t *icall)
604 cap_call_handle_t chandle;
605 size_t msg_size;
606 udp_crcv_queue_entry_t *enext;
607 void *data;
608 size_t size;
609 size_t off;
610 errno_t rc;
612 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_read_srv()");
613 off = IPC_GET_ARG1(*icall);
615 enext = udp_rmsg_get_next(client);
617 if (!async_data_read_receive(&chandle, &size)) {
618 async_answer_0(chandle, EREFUSED);
619 async_answer_0(icall_handle, EREFUSED);
620 return;
623 if (enext == NULL) {
624 async_answer_0(chandle, ENOENT);
625 async_answer_0(icall_handle, ENOENT);
626 return;
629 data = enext->msg->data + off;
630 msg_size = enext->msg->data_size;
632 if (off > msg_size) {
633 async_answer_0(chandle, EINVAL);
634 async_answer_0(icall_handle, EINVAL);
635 return;
638 rc = async_data_read_finalize(chandle, data, min(msg_size - off, size));
639 if (rc != EOK) {
640 async_answer_0(icall_handle, rc);
641 return;
644 async_answer_0(icall_handle, EOK);
645 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_read_srv(): OK");
648 /** Discard first received message.
650 * Handle client request to discard first received message, advancing
651 * to the next one.
653 * @param client UDP client
654 * @param icall_handle Async request call handle
655 * @param icall Async request data
657 static void
658 udp_rmsg_discard_srv(udp_client_t *client, cap_call_handle_t icall_handle,
659 ipc_call_t *icall)
661 udp_crcv_queue_entry_t *enext;
663 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_discard_srv()");
665 enext = udp_rmsg_get_next(client);
666 if (enext == NULL) {
667 log_msg(LOG_DEFAULT, LVL_DEBUG, "usg_rmsg_discard_srv: enext==NULL");
668 async_answer_0(icall_handle, ENOENT);
669 return;
672 list_remove(&enext->link);
673 udp_msg_delete(enext->msg);
674 free(enext);
675 async_answer_0(icall_handle, EOK);
678 /** Handle UDP client connection.
680 * @param icall_handle Connect call handle
681 * @param icall Connect call data
682 * @param arg Connection argument
684 static void udp_client_conn(cap_call_handle_t icall_handle, ipc_call_t *icall,
685 void *arg)
687 udp_client_t client;
688 unsigned long n;
690 /* Accept the connection */
691 async_answer_0(icall_handle, EOK);
693 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn()");
695 client.sess = NULL;
696 list_initialize(&client.cassoc);
697 list_initialize(&client.crcv_queue);
699 while (true) {
700 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: wait req");
701 ipc_call_t call;
702 cap_call_handle_t chandle = async_get_call(&call);
703 sysarg_t method = IPC_GET_IMETHOD(call);
705 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: method=%d",
706 (int)method);
707 if (!method) {
708 /* The other side has hung up */
709 async_answer_0(chandle, EOK);
710 break;
713 switch (method) {
714 case UDP_CALLBACK_CREATE:
715 udp_callback_create_srv(&client, chandle, &call);
716 break;
717 case UDP_ASSOC_CREATE:
718 udp_assoc_create_srv(&client, chandle, &call);
719 break;
720 case UDP_ASSOC_DESTROY:
721 udp_assoc_destroy_srv(&client, chandle, &call);
722 break;
723 case UDP_ASSOC_SET_NOLOCAL:
724 udp_assoc_set_nolocal_srv(&client, chandle, &call);
725 break;
726 case UDP_ASSOC_SEND_MSG:
727 udp_assoc_send_msg_srv(&client, chandle, &call);
728 break;
729 case UDP_RMSG_INFO:
730 udp_rmsg_info_srv(&client, chandle, &call);
731 break;
732 case UDP_RMSG_READ:
733 udp_rmsg_read_srv(&client, chandle, &call);
734 break;
735 case UDP_RMSG_DISCARD:
736 udp_rmsg_discard_srv(&client, chandle, &call);
737 break;
738 default:
739 async_answer_0(chandle, ENOTSUP);
740 break;
744 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: terminated");
746 n = list_count(&client.cassoc);
747 if (n != 0) {
748 log_msg(LOG_DEFAULT, LVL_WARN, "udp_client_conn: "
749 "Client with %lu active associations closed session.", n);
750 /* XXX Clean up */
753 /* XXX Clean up client receive queue */
755 if (client.sess != NULL)
756 async_hangup(client.sess);
759 /** Initialize UDP service.
761 * @return EOK on success or an error code.
763 errno_t udp_service_init(void)
765 errno_t rc;
766 service_id_t sid;
768 async_set_fallback_port_handler(udp_client_conn, NULL);
770 rc = loc_server_register(NAME);
771 if (rc != EOK) {
772 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server.");
773 return EIO;
776 rc = loc_service_register(SERVICE_NAME_UDP, &sid);
777 if (rc != EOK) {
778 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service.");
779 return EIO;
782 return EOK;
786 * @}