2
* Copyright (c) 2008 Lukas Mejdrech
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
* UDP module implementation.
39
#include <fibril_synch.h>
42
#include <ipc/services.h>
45
#include <ipc/socket.h>
46
#include <adt/dynamic_fifo.h>
49
#include <net/socket_codes.h>
50
#include <net/ip_protocols.h>
54
#include <net/modules.h>
56
#include <packet_client.h>
57
#include <packet_remote.h>
58
#include <net_checksum.h>
59
#include <ip_client.h>
60
#include <ip_interface.h>
61
#include <icmp_client.h>
62
#include <icmp_remote.h>
63
#include <net_interface.h>
64
#include <socket_core.h>
65
#include <tl_common.h>
66
#include <tl_remote.h>
70
#include "udp_header.h"
72
/** UDP module name. */
75
/** Default UDP checksum computing. */
76
#define NET_DEFAULT_UDP_CHECKSUM_COMPUTING true
78
/** Default UDP autobind when sending via unbound sockets. */
79
#define NET_DEFAULT_UDP_AUTOBINDING true
81
/** Maximum UDP fragment size. */
82
#define MAX_UDP_FRAGMENT_SIZE 65535
84
/** Free ports pool start. */
85
#define UDP_FREE_PORTS_START 1025
87
/** Free ports pool end. */
88
#define UDP_FREE_PORTS_END 65535
90
/** UDP global data. */
91
udp_globals_t udp_globals;
93
/** Releases the packet and returns the result.
95
* @param[in] packet The packet queue to be released.
96
* @param[in] result The result to be returned.
97
* @return The result parameter.
99
static int udp_release_and_return(packet_t *packet, int result)
101
pq_release_remote(udp_globals.net_sess, packet_get_id(packet));
105
/** Processes the received UDP packet queue.
107
* Notifies the destination socket application.
108
* Releases the packet on error or sends an ICMP error notification.
110
* @param[in] device_id The receiving device identifier.
111
* @param[in,out] packet The received packet queue.
112
* @param[in] error The packet error reporting service. Prefixes the
114
* @return EOK on success.
115
* @return EINVAL if the packet is not valid.
116
* @return EINVAL if the stored packet address is not the
118
* @return EINVAL if the packet does not contain any data.
119
* @return NO_DATA if the packet content is shorter than the user
121
* @return ENOMEM if there is not enough memory left.
122
* @return EADDRNOTAVAIL if the destination socket does not exist.
123
* @return Other error codes as defined for the
124
* ip_client_process_packet() function.
126
static int udp_process_packet(nic_device_id_t device_id, packet_t *packet,
132
udp_header_t *header;
133
socket_core_t *socket;
134
packet_t *next_packet;
138
packet_t *tmp_packet;
142
struct sockaddr *src;
143
struct sockaddr *dest;
144
packet_dimension_t *packet_dimension;
152
// length = icmp_client_header_length(packet);
155
result = icmp_client_process_packet(packet, &type,
158
return udp_release_and_return(packet, result);
159
length = (size_t) result;
160
rc = packet_trim(packet, length, 0);
162
return udp_release_and_return(packet, rc);
165
return udp_release_and_return(packet, ENOTSUP);
168
/* TODO process received ipopts? */
169
result = ip_client_process_packet(packet, NULL, NULL, NULL, NULL, NULL);
171
return udp_release_and_return(packet, result);
172
offset = (size_t) result;
174
length = packet_get_data_length(packet);
176
return udp_release_and_return(packet, EINVAL);
177
if (length < UDP_HEADER_SIZE + offset)
178
return udp_release_and_return(packet, NO_DATA);
180
/* Trim all but UDP header */
181
rc = packet_trim(packet, offset, 0);
183
return udp_release_and_return(packet, rc);
186
header = (udp_header_t *) packet_get_data(packet);
188
return udp_release_and_return(packet, NO_DATA);
190
/* Find the destination socket */
191
socket = socket_port_find(&udp_globals.sockets,
192
ntohs(header->destination_port), (uint8_t *) SOCKET_MAP_KEY_LISTENING, 0);
194
if (tl_prepare_icmp_packet(udp_globals.net_sess,
195
udp_globals.icmp_sess, packet, error) == EOK) {
196
icmp_destination_unreachable_msg(udp_globals.icmp_sess,
197
ICMP_PORT_UNREACH, 0, packet);
199
return EADDRNOTAVAIL;
202
/* Count the received packet fragments */
203
next_packet = packet;
205
total_length = ntohs(header->total_length);
207
/* Compute header checksum if set */
208
if (header->checksum && !error) {
209
result = packet_get_addr(packet, (uint8_t **) &src,
212
return udp_release_and_return(packet, result);
214
rc = ip_client_get_pseudo_header(IPPROTO_UDP, src, result, dest,
215
result, total_length, &ip_header, &length);
217
return udp_release_and_return(packet, rc);
219
checksum = compute_checksum(0, ip_header, length);
221
* The udp header checksum will be added with the first
227
header->checksum = 0;
233
length = packet_get_data_length(next_packet);
235
return udp_release_and_return(packet, NO_DATA);
237
if (total_length < length) {
238
rc = packet_trim(next_packet, 0, length - total_length);
240
return udp_release_and_return(packet, rc);
242
/* Add partial checksum if set */
243
if (header->checksum) {
244
checksum = compute_checksum(checksum,
245
packet_get_data(packet),
246
packet_get_data_length(packet));
249
/* Relese the rest of the packet fragments */
250
tmp_packet = pq_next(next_packet);
252
next_packet = pq_detach(tmp_packet);
253
pq_release_remote(udp_globals.net_sess,
254
packet_get_id(tmp_packet));
255
tmp_packet = next_packet;
261
total_length -= length;
263
/* Add partial checksum if set */
264
if (header->checksum) {
265
checksum = compute_checksum(checksum,
266
packet_get_data(packet),
267
packet_get_data_length(packet));
270
} while ((next_packet = pq_next(next_packet)) && (total_length > 0));
272
/* Verify checksum */
273
if (header->checksum) {
274
if (flip_checksum(compact_checksum(checksum)) !=
276
if (tl_prepare_icmp_packet(udp_globals.net_sess,
277
udp_globals.icmp_sess, packet, error) == EOK) {
278
/* Checksum error ICMP */
279
icmp_parameter_problem_msg(
280
udp_globals.icmp_sess, ICMP_PARAM_POINTER,
281
((size_t) ((void *) &header->checksum)) -
282
((size_t) ((void *) header)), packet);
288
/* Queue the received packet */
289
rc = dyn_fifo_push(&socket->received, packet_get_id(packet),
290
SOCKET_MAX_RECEIVED_SIZE);
292
return udp_release_and_return(packet, rc);
294
rc = tl_get_ip_packet_dimension(udp_globals.ip_sess,
295
&udp_globals.dimensions, device_id, &packet_dimension);
297
return udp_release_and_return(packet, rc);
299
/* Notify the destination socket */
300
fibril_rwlock_write_unlock(&udp_globals.lock);
302
async_exch_t *exch = async_exchange_begin(socket->sess);
303
async_msg_5(exch, NET_SOCKET_RECEIVED, (sysarg_t) socket->socket_id,
304
packet_dimension->content, 0, 0, (sysarg_t) fragments);
305
async_exchange_end(exch);
310
/** Processes the received UDP packet queue.
312
* Is used as an entry point from the underlying IP module.
313
* Locks the global lock and calls udp_process_packet() function.
315
* @param[in] device_id The receiving device identifier.
316
* @param[in,out] packet The received packet queue.
317
* @param receiver The target service. Ignored parameter.
318
* @param[in] error The packet error reporting service. Prefixes the
320
* @return EOK on success.
321
* @return Other error codes as defined for the
322
* udp_process_packet() function.
324
static int udp_received_msg(nic_device_id_t device_id, packet_t *packet,
325
services_t receiver, services_t error)
329
fibril_rwlock_write_lock(&udp_globals.lock);
330
result = udp_process_packet(device_id, packet, error);
332
fibril_rwlock_write_unlock(&udp_globals.lock);
337
/** Process IPC messages from the IP module
339
* @param[in] iid Message identifier.
340
* @param[in,out] icall Message parameters.
341
* @param[in] arg Local argument.
344
static void udp_receiver(ipc_callid_t iid, ipc_call_t *icall, void *arg)
350
switch (IPC_GET_IMETHOD(*icall)) {
351
case NET_TL_RECEIVED:
352
rc = packet_translate_remote(udp_globals.net_sess, &packet,
353
IPC_GET_PACKET(*icall));
355
rc = udp_received_msg(IPC_GET_DEVICE(*icall), packet,
356
SERVICE_UDP, IPC_GET_ERROR(*icall));
358
async_answer_0(iid, (sysarg_t) rc);
361
async_answer_0(iid, (sysarg_t) ENOTSUP);
364
iid = async_get_call(icall);
368
/** Initialize the UDP module.
370
* @param[in] sess Network module session.
372
* @return EOK on success.
373
* @return ENOMEM if there is not enough memory left.
376
int tl_initialize(async_sess_t *sess)
378
measured_string_t names[] = {
380
(uint8_t *) "UDP_CHECKSUM_COMPUTING",
384
(uint8_t *) "UDP_AUTOBINDING",
388
measured_string_t *configuration;
389
size_t count = sizeof(names) / sizeof(measured_string_t);
392
fibril_rwlock_initialize(&udp_globals.lock);
393
fibril_rwlock_write_lock(&udp_globals.lock);
395
udp_globals.net_sess = sess;
396
udp_globals.icmp_sess = icmp_connect_module();
398
udp_globals.ip_sess = ip_bind_service(SERVICE_IP, IPPROTO_UDP,
399
SERVICE_UDP, udp_receiver);
400
if (udp_globals.ip_sess == NULL) {
401
fibril_rwlock_write_unlock(&udp_globals.lock);
405
/* Read default packet dimensions */
406
int rc = ip_packet_size_req(udp_globals.ip_sess, -1,
407
&udp_globals.packet_dimension);
409
fibril_rwlock_write_unlock(&udp_globals.lock);
413
rc = socket_ports_initialize(&udp_globals.sockets);
415
fibril_rwlock_write_unlock(&udp_globals.lock);
419
rc = packet_dimensions_initialize(&udp_globals.dimensions);
421
socket_ports_destroy(&udp_globals.sockets, free);
422
fibril_rwlock_write_unlock(&udp_globals.lock);
426
udp_globals.packet_dimension.prefix += sizeof(udp_header_t);
427
udp_globals.packet_dimension.content -= sizeof(udp_header_t);
428
udp_globals.last_used_port = UDP_FREE_PORTS_START - 1;
430
udp_globals.checksum_computing = NET_DEFAULT_UDP_CHECKSUM_COMPUTING;
431
udp_globals.autobinding = NET_DEFAULT_UDP_AUTOBINDING;
433
/* Get configuration */
434
configuration = &names[0];
435
rc = net_get_conf_req(udp_globals.net_sess, &configuration, count,
438
socket_ports_destroy(&udp_globals.sockets, free);
439
fibril_rwlock_write_unlock(&udp_globals.lock);
444
if (configuration[0].value)
445
udp_globals.checksum_computing =
446
(configuration[0].value[0] == 'y');
448
if (configuration[1].value)
449
udp_globals.autobinding =
450
(configuration[1].value[0] == 'y');
452
net_free_settings(configuration, data);
455
fibril_rwlock_write_unlock(&udp_globals.lock);
459
/** Sends data from the socket to the remote address.
461
* Binds the socket to a free port if not already connected/bound.
462
* Handles the NET_SOCKET_SENDTO message.
463
* Supports AF_INET and AF_INET6 address families.
465
* @param[in,out] local_sockets The application local sockets.
466
* @param[in] socket_id Socket identifier.
467
* @param[in] addr The destination address.
468
* @param[in] addrlen The address length.
469
* @param[in] fragments The number of data fragments.
470
* @param[out] data_fragment_size The data fragment size in bytes.
471
* @param[in] flags Various send flags.
472
* @return EOK on success.
473
* @return EAFNOTSUPPORT if the address family is not supported.
474
* @return ENOTSOCK if the socket is not found.
475
* @return EINVAL if the address is invalid.
476
* @return ENOTCONN if the sending socket is not and cannot be
478
* @return ENOMEM if there is not enough memory left.
479
* @return Other error codes as defined for the
480
* socket_read_packet_data() function.
481
* @return Other error codes as defined for the
482
* ip_client_prepare_packet() function.
483
* @return Other error codes as defined for the ip_send_msg()
486
static int udp_sendto_message(socket_cores_t *local_sockets, int socket_id,
487
const struct sockaddr *addr, socklen_t addrlen, int fragments,
488
size_t *data_fragment_size, int flags)
490
socket_core_t *socket;
492
packet_t *next_packet;
493
udp_header_t *header;
501
nic_device_id_t device_id;
502
packet_dimension_t *packet_dimension;
506
/* In case of error, do not update the data fragment size. */
507
*data_fragment_size = 0;
509
rc = tl_get_address_port(addr, addrlen, &dest_port);
513
socket = socket_cores_find(local_sockets, socket_id);
517
if ((socket->port <= 0) && udp_globals.autobinding) {
518
/* Bind the socket to a random free port if not bound */
519
rc = socket_bind_free_port(&udp_globals.sockets, socket,
520
UDP_FREE_PORTS_START, UDP_FREE_PORTS_END,
521
udp_globals.last_used_port);
524
/* Set the next port as the search starting port number */
525
udp_globals.last_used_port = socket->port;
528
if (udp_globals.checksum_computing) {
529
rc = ip_get_route_req(udp_globals.ip_sess, IPPROTO_UDP, addr,
530
addrlen, &device_id, &ip_header, &headerlen);
533
/* Get the device packet dimension */
534
// rc = tl_get_ip_packet_dimension(udp_globals.ip_sess,
535
// &udp_globals.dimensions, device_id, &packet_dimension);
540
/* Do not ask all the time */
541
rc = ip_packet_size_req(udp_globals.ip_sess, -1,
542
&udp_globals.packet_dimension);
545
packet_dimension = &udp_globals.packet_dimension;
549
* Update the data fragment size based on what the lower layers can
550
* handle without fragmentation, but not more than the maximum allowed
553
size = MAX_UDP_FRAGMENT_SIZE;
554
if (packet_dimension->content < size)
555
size = packet_dimension->content;
556
*data_fragment_size = size;
558
/* Read the first packet fragment */
559
result = tl_socket_read_packet_data(udp_globals.net_sess, &packet,
560
UDP_HEADER_SIZE, packet_dimension, addr, addrlen);
564
total_length = (size_t) result;
565
if (udp_globals.checksum_computing)
566
checksum = compute_checksum(0, packet_get_data(packet),
567
packet_get_data_length(packet));
571
/* Prefix the UDP header */
572
header = PACKET_PREFIX(packet, udp_header_t);
574
return udp_release_and_return(packet, ENOMEM);
576
bzero(header, sizeof(*header));
578
/* Read the rest of the packet fragments */
579
for (index = 1; index < fragments; index++) {
580
result = tl_socket_read_packet_data(udp_globals.net_sess,
581
&next_packet, 0, packet_dimension, addr, addrlen);
583
return udp_release_and_return(packet, result);
585
rc = pq_add(&packet, next_packet, index, 0);
587
return udp_release_and_return(packet, rc);
589
total_length += (size_t) result;
590
if (udp_globals.checksum_computing) {
591
checksum = compute_checksum(checksum,
592
packet_get_data(next_packet),
593
packet_get_data_length(next_packet));
597
/* Set the UDP header */
598
header->source_port = htons((socket->port > 0) ? socket->port : 0);
599
header->destination_port = htons(dest_port);
600
header->total_length = htons(total_length + sizeof(*header));
601
header->checksum = 0;
603
if (udp_globals.checksum_computing) {
604
/* Update the pseudo header */
605
rc = ip_client_set_pseudo_header_data_length(ip_header,
606
headerlen, total_length + UDP_HEADER_SIZE);
609
return udp_release_and_return(packet, rc);
612
/* Finish the checksum computation */
613
checksum = compute_checksum(checksum, ip_header, headerlen);
614
checksum = compute_checksum(checksum, (uint8_t *) header,
617
htons(flip_checksum(compact_checksum(checksum)));
620
device_id = NIC_DEVICE_INVALID_ID;
622
/* Prepare the first packet fragment */
623
rc = ip_client_prepare_packet(packet, IPPROTO_UDP, 0, 0, 0, 0);
625
return udp_release_and_return(packet, rc);
627
/* Release the UDP global lock on success. */
628
fibril_rwlock_write_unlock(&udp_globals.lock);
630
/* Send the packet */
631
ip_send_msg(udp_globals.ip_sess, device_id, packet, SERVICE_UDP, 0);
636
/** Receives data to the socket.
638
* Handles the NET_SOCKET_RECVFROM message.
639
* Replies the source address as well.
641
* @param[in] local_sockets The application local sockets.
642
* @param[in] socket_id Socket identifier.
643
* @param[in] flags Various receive flags.
644
* @param[out] addrlen The source address length.
645
* @return The number of bytes received.
646
* @return ENOTSOCK if the socket is not found.
647
* @return NO_DATA if there are no received packets or data.
648
* @return ENOMEM if there is not enough memory left.
649
* @return EINVAL if the received address is not an IP address.
650
* @return Other error codes as defined for the packet_translate()
652
* @return Other error codes as defined for the data_reply()
655
static int udp_recvfrom_message(socket_cores_t *local_sockets, int socket_id,
656
int flags, size_t *addrlen)
658
socket_core_t *socket;
661
udp_header_t *header;
662
struct sockaddr *addr;
668
/* Find the socket */
669
socket = socket_cores_find(local_sockets, socket_id);
673
/* Get the next received packet */
674
packet_id = dyn_fifo_value(&socket->received);
678
rc = packet_translate_remote(udp_globals.net_sess, &packet, packet_id);
680
(void) dyn_fifo_pop(&socket->received);
685
data = packet_get_data(packet);
687
(void) dyn_fifo_pop(&socket->received);
688
return udp_release_and_return(packet, NO_DATA);
690
header = (udp_header_t *) data;
692
/* Set the source address port */
693
result = packet_get_addr(packet, (uint8_t **) &addr, NULL);
694
rc = tl_set_address_port(addr, result, ntohs(header->source_port));
696
(void) dyn_fifo_pop(&socket->received);
697
return udp_release_and_return(packet, rc);
699
*addrlen = (size_t) result;
701
/* Send the source address */
702
rc = data_reply(addr, *addrlen);
709
(void) dyn_fifo_pop(&socket->received);
710
return udp_release_and_return(packet, rc);
713
/* Trim the header */
714
rc = packet_trim(packet, UDP_HEADER_SIZE, 0);
716
(void) dyn_fifo_pop(&socket->received);
717
return udp_release_and_return(packet, rc);
720
/* Reply the packets */
721
rc = socket_reply_packets(packet, &length);
728
(void) dyn_fifo_pop(&socket->received);
729
return udp_release_and_return(packet, rc);
732
(void) dyn_fifo_pop(&socket->received);
734
/* Release the packet and return the total length */
735
return udp_release_and_return(packet, (int) length);
738
/** Process the socket client messages.
740
* Run until the client module disconnects.
744
* @param[in] sess Callback session.
745
* @param[in] callid Message identifier.
746
* @param[in] call Message parameters.
748
* @return EOK on success.
751
static int udp_process_client_messages(async_sess_t *sess, ipc_callid_t callid,
755
socket_cores_t local_sockets;
756
struct sockaddr *addr;
762
packet_dimension_t *packet_dimension;
765
* Accept the connection
766
* - Answer the first IPC_M_CONNECT_TO_ME call.
772
* The client connection is only in one fibril and therefore no
773
* additional locks are needed.
776
socket_cores_initialize(&local_sockets);
780
/* Answer the call */
781
answer_call(callid, res, &answer, answer_count);
784
refresh_answer(&answer, &answer_count);
786
/* Get the next call */
787
callid = async_get_call(&call);
789
/* Process the call */
790
if (!IPC_GET_IMETHOD(call)) {
795
switch (IPC_GET_IMETHOD(call)) {
797
socket_id = SOCKET_GET_SOCKET_ID(call);
798
res = socket_create(&local_sockets, sess, NULL,
800
SOCKET_SET_SOCKET_ID(answer, socket_id);
805
size = MAX_UDP_FRAGMENT_SIZE;
806
if (tl_get_ip_packet_dimension(udp_globals.ip_sess,
807
&udp_globals.dimensions, NIC_DEVICE_INVALID_ID,
808
&packet_dimension) == EOK) {
809
if (packet_dimension->content < size)
810
size = packet_dimension->content;
812
SOCKET_SET_DATA_FRAGMENT_SIZE(answer, size);
813
SOCKET_SET_HEADER_SIZE(answer, UDP_HEADER_SIZE);
817
case NET_SOCKET_BIND:
818
res = async_data_write_accept((void **) &addr, false,
822
fibril_rwlock_write_lock(&udp_globals.lock);
823
res = socket_bind(&local_sockets, &udp_globals.sockets,
824
SOCKET_GET_SOCKET_ID(call), addr, addrlen,
825
UDP_FREE_PORTS_START, UDP_FREE_PORTS_END,
826
udp_globals.last_used_port);
827
fibril_rwlock_write_unlock(&udp_globals.lock);
831
case NET_SOCKET_SENDTO:
832
res = async_data_write_accept((void **) &addr, false,
837
fibril_rwlock_write_lock(&udp_globals.lock);
838
res = udp_sendto_message(&local_sockets,
839
SOCKET_GET_SOCKET_ID(call), addr, addrlen,
840
SOCKET_GET_DATA_FRAGMENTS(call), &size,
841
SOCKET_GET_FLAGS(call));
842
SOCKET_SET_DATA_FRAGMENT_SIZE(answer, size);
845
fibril_rwlock_write_unlock(&udp_globals.lock);
852
case NET_SOCKET_RECVFROM:
853
fibril_rwlock_write_lock(&udp_globals.lock);
854
res = udp_recvfrom_message(&local_sockets,
855
SOCKET_GET_SOCKET_ID(call), SOCKET_GET_FLAGS(call),
857
fibril_rwlock_write_unlock(&udp_globals.lock);
862
SOCKET_SET_READ_DATA_LENGTH(answer, res);
863
SOCKET_SET_ADDRESS_LENGTH(answer, addrlen);
868
case NET_SOCKET_CLOSE:
869
fibril_rwlock_write_lock(&udp_globals.lock);
870
res = socket_destroy(udp_globals.net_sess,
871
SOCKET_GET_SOCKET_ID(call), &local_sockets,
872
&udp_globals.sockets, NULL);
873
fibril_rwlock_write_unlock(&udp_globals.lock);
876
case NET_SOCKET_GETSOCKOPT:
877
case NET_SOCKET_SETSOCKOPT:
884
/* Release the application session */
887
/* Release all local sockets */
888
socket_cores_release(udp_globals.net_sess, &local_sockets,
889
&udp_globals.sockets, NULL);
894
/** Per-connection initialization
897
void tl_connection(void)
901
/** Processes the UDP message.
903
* @param[in] callid The message identifier.
904
* @param[in] call The message parameters.
905
* @param[out] answer The message answer parameters.
906
* @param[out] answer_count The last parameter for the actual answer in the
908
* @return EOK on success.
909
* @return ENOTSUP if the message is not known.
911
* @see udp_interface.h
912
* @see IS_NET_UDP_MESSAGE()
914
int tl_message(ipc_callid_t callid, ipc_call_t *call,
915
ipc_call_t *answer, size_t *answer_count)
919
async_sess_t *callback =
920
async_callback_receive_start(EXCHANGE_SERIALIZE, call);
922
return udp_process_client_messages(callback, callid, *call);
927
int main(int argc, char *argv[])
929
/* Start the module */
930
return tl_module_start(SERVICE_UDP);