2 * Copyright (c) 2015 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
34 * @file HelenOS service implementation
39 #include <inet/endpoint.h>
41 #include <ipc/services.h>
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
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
,
75 udp_crcv_queue_entry_t
*rqe
;
77 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "udp_cassoc_queue_msg(%p, %p, %p)",
80 rqe
= calloc(1, sizeof(udp_crcv_queue_entry_t
));
84 link_initialize(&rqe
->link
);
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);
98 /** Send 'data' event to client.
100 * @param client Client
102 static void udp_ev_data(udp_client_t
*client
)
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
);
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
;
131 cassoc
= calloc(1, sizeof(udp_cassoc_t
));
135 /* Allocate new ID */
137 list_foreach (client
->cassoc
, lclient
, udp_cassoc_t
, cassoc
) {
138 if (cassoc
->id
>= id
)
143 cassoc
->client
= client
;
144 cassoc
->assoc
= assoc
;
146 list_append(&cassoc
->lclient
, &client
->cassoc
);
151 /** Destroy client association.
153 * @param cassoc Client association
155 static void udp_cassoc_destroy(udp_cassoc_t
*cassoc
)
157 list_remove(&cassoc
->lclient
);
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
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
) {
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
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
,
213 udp_cassoc_t
*cassoc
;
216 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "udp_assoc_create_impl");
218 assoc
= udp_assoc_new(epp
, NULL
, NULL
);
222 if (epp
->local_link
!= 0)
223 udp_assoc_set_iplink(assoc
, epp
->local_link
);
225 rc
= udp_cassoc_create(client
, assoc
, &cassoc
);
227 assert(rc
== ENOMEM
);
228 udp_assoc_delete(assoc
);
232 assoc
->cb
= &udp_cassoc_cb
;
233 assoc
->cb_arg
= cassoc
;
235 rc
= udp_assoc_add(assoc
);
237 udp_cassoc_destroy(cassoc
);
238 udp_assoc_delete(assoc
);
242 *rassoc_id
= cassoc
->id
;
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
;
259 rc
= udp_cassoc_get(client
, assoc_id
, &cassoc
);
261 assert(rc
== ENOENT
);
265 udp_assoc_remove(cassoc
->assoc
);
266 udp_assoc_reset(cassoc
->assoc
);
267 udp_assoc_delete(cassoc
->assoc
);
268 udp_cassoc_destroy(cassoc
);
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
;
285 rc
= udp_cassoc_get(client
, assoc_id
, &cassoc
);
287 assert(rc
== ENOENT
);
291 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Setting nolocal to true");
292 cassoc
->assoc
->nolocal
= true;
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
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
)
313 udp_cassoc_t
*cassoc
;
316 rc
= udp_cassoc_get(client
, assoc_id
, &cassoc
);
321 msg
.data_size
= size
;
322 rc
= udp_assoc_send(cassoc
->assoc
, dest
, &msg
);
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
338 udp_callback_create_srv(udp_client_t
*client
, cap_call_handle_t icall_handle
,
341 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "udp_callback_create_srv()");
343 async_sess_t
*sess
= async_callback_receive(EXCHANGE_SERIALIZE
);
345 async_answer_0(icall_handle
, ENOMEM
);
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
362 void udp_assoc_create_srv(udp_client_t
*client
, cap_call_handle_t icall_handle
,
365 cap_call_handle_t chandle
;
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
);
379 if (size
!= sizeof(inet_ep2_t
)) {
380 async_answer_0(chandle
, EINVAL
);
381 async_answer_0(icall_handle
, EINVAL
);
385 rc
= async_data_write_finalize(chandle
, &epp
, size
);
387 async_answer_0(chandle
, rc
);
388 async_answer_0(icall_handle
, rc
);
392 rc
= udp_assoc_create_impl(client
, &epp
, &assoc_id
);
394 async_answer_0(icall_handle
, rc
);
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
410 udp_assoc_destroy_srv(udp_client_t
*client
, cap_call_handle_t icall_handle
,
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
432 udp_assoc_set_nolocal_srv(udp_client_t
*client
, cap_call_handle_t icall_handle
,
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
454 udp_assoc_send_msg_srv(udp_client_t
*client
, cap_call_handle_t icall_handle
,
457 cap_call_handle_t chandle
;
464 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "udp_assoc_send_msg_srv()");
468 if (!async_data_write_receive(&chandle
, &size
)) {
469 async_answer_0(chandle
, EREFUSED
);
470 async_answer_0(icall_handle
, EREFUSED
);
474 if (size
!= sizeof(inet_ep_t
)) {
475 async_answer_0(chandle
, EINVAL
);
476 async_answer_0(icall_handle
, EINVAL
);
480 rc
= async_data_write_finalize(chandle
, &dest
, size
);
482 async_answer_0(chandle
, rc
);
483 async_answer_0(icall_handle
, rc
);
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
);
495 if (size
> MAX_MSG_SIZE
) {
496 async_answer_0(chandle
, EINVAL
);
497 async_answer_0(icall_handle
, EINVAL
);
503 async_answer_0(chandle
, ENOMEM
);
504 async_answer_0(icall_handle
, ENOMEM
);
507 rc
= async_data_write_finalize(chandle
, data
, size
);
509 async_answer_0(chandle
, rc
);
510 async_answer_0(icall_handle
, rc
);
515 assoc_id
= IPC_GET_ARG1(*icall
);
517 rc
= udp_assoc_send_msg_impl(client
, assoc_id
, &dest
, data
, size
);
519 async_answer_0(icall_handle
, rc
);
524 async_answer_0(icall_handle
, EOK
);
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
)
537 link
= list_first(&client
->crcv_queue
);
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
553 udp_rmsg_info_srv(udp_client_t
*client
, cap_call_handle_t icall_handle
,
556 cap_call_handle_t chandle
;
558 udp_crcv_queue_entry_t
*enext
;
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
);
572 async_answer_0(chandle
, ENOENT
);
573 async_answer_0(icall_handle
, ENOENT
);
577 rc
= async_data_read_finalize(chandle
, &enext
->epp
.remote
,
578 max(size
, (size_t)sizeof(inet_ep_t
)));
580 async_answer_0(icall_handle
, rc
);
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
601 udp_rmsg_read_srv(udp_client_t
*client
, cap_call_handle_t icall_handle
,
604 cap_call_handle_t chandle
;
606 udp_crcv_queue_entry_t
*enext
;
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
);
624 async_answer_0(chandle
, ENOENT
);
625 async_answer_0(icall_handle
, ENOENT
);
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
);
638 rc
= async_data_read_finalize(chandle
, data
, min(msg_size
- off
, size
));
640 async_answer_0(icall_handle
, rc
);
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
653 * @param client UDP client
654 * @param icall_handle Async request call handle
655 * @param icall Async request data
658 udp_rmsg_discard_srv(udp_client_t
*client
, cap_call_handle_t icall_handle
,
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
);
667 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "usg_rmsg_discard_srv: enext==NULL");
668 async_answer_0(icall_handle
, ENOENT
);
672 list_remove(&enext
->link
);
673 udp_msg_delete(enext
->msg
);
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
,
690 /* Accept the connection */
691 async_answer_0(icall_handle
, EOK
);
693 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "udp_client_conn()");
696 list_initialize(&client
.cassoc
);
697 list_initialize(&client
.crcv_queue
);
700 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "udp_client_conn: wait req");
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",
708 /* The other side has hung up */
709 async_answer_0(chandle
, EOK
);
714 case UDP_CALLBACK_CREATE
:
715 udp_callback_create_srv(&client
, chandle
, &call
);
717 case UDP_ASSOC_CREATE
:
718 udp_assoc_create_srv(&client
, chandle
, &call
);
720 case UDP_ASSOC_DESTROY
:
721 udp_assoc_destroy_srv(&client
, chandle
, &call
);
723 case UDP_ASSOC_SET_NOLOCAL
:
724 udp_assoc_set_nolocal_srv(&client
, chandle
, &call
);
726 case UDP_ASSOC_SEND_MSG
:
727 udp_assoc_send_msg_srv(&client
, chandle
, &call
);
730 udp_rmsg_info_srv(&client
, chandle
, &call
);
733 udp_rmsg_read_srv(&client
, chandle
, &call
);
735 case UDP_RMSG_DISCARD
:
736 udp_rmsg_discard_srv(&client
, chandle
, &call
);
739 async_answer_0(chandle
, ENOTSUP
);
744 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "udp_client_conn: terminated");
746 n
= list_count(&client
.cassoc
);
748 log_msg(LOG_DEFAULT
, LVL_WARN
, "udp_client_conn: "
749 "Client with %lu active associations closed session.", n
);
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)
768 async_set_fallback_port_handler(udp_client_conn
, NULL
);
770 rc
= loc_server_register(NAME
);
772 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed registering server.");
776 rc
= loc_service_register(SERVICE_NAME_UDP
, &sid
);
778 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed registering service.");