source: mainline/uspace/lib/net/tl/socket_core.c@ 6b82009

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6b82009 was 6b82009, checked in by Martin Decky <martin@…>, 14 years ago

networking stack: convert to the new async framework

  • Property mode set to 100644
File size: 18.7 KB
Line 
1/*
2 * Copyright (c) 2009 Lukas Mejdrech
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 libnet
30 * @{
31 */
32
33/** @file
34 * Socket common core implementation.
35 */
36
37#include <socket_core.h>
38#include <packet_client.h>
39#include <packet_remote.h>
40#include <net/socket_codes.h>
41#include <net/in.h>
42#include <net/inet.h>
43#include <net/packet.h>
44#include <net/modules.h>
45#include <stdint.h>
46#include <stdlib.h>
47#include <errno.h>
48#include <adt/dynamic_fifo.h>
49#include <adt/int_map.h>
50
51/**
52 * Maximum number of random attempts to find a new socket identifier before
53 * switching to the sequence.
54 */
55#define SOCKET_ID_TRIES 100
56
57/** Bound port sockets.*/
58struct socket_port {
59 /** The bound sockets map. */
60 socket_port_map_t map;
61 /** The bound sockets count. */
62 int count;
63};
64
65INT_MAP_IMPLEMENT(socket_cores, socket_core_t);
66
67GENERIC_CHAR_MAP_IMPLEMENT(socket_port_map, socket_core_t *);
68
69INT_MAP_IMPLEMENT(socket_ports, socket_port_t);
70
71/** Destroy the socket.
72 *
73 * If the socket is bound, the port is released.
74 * Release all buffered packets, call the release function and remove the
75 * socket from the local sockets.
76 *
77 * @param[in] sess Packet server session.
78 * @param[in] socket Socket to be destroyed.
79 * @param[in,out] local_sockets Local sockets to be updated.
80 * @param[in,out] global_sockets Global sockets to be updated.
81 * @param[in] socket_release Client release callback function.
82 *
83 */
84static void socket_destroy_core(async_sess_t *sess, socket_core_t *socket,
85 socket_cores_t *local_sockets, socket_ports_t *global_sockets,
86 void (* socket_release)(socket_core_t *socket))
87{
88 /* If bound */
89 if (socket->port) {
90 /* Release the port */
91 socket_port_release(global_sockets, socket);
92 }
93
94 /* Release all received packets */
95 int packet_id;
96 while ((packet_id = dyn_fifo_pop(&socket->received)) >= 0)
97 pq_release_remote(sess, packet_id);
98
99 dyn_fifo_destroy(&socket->received);
100 dyn_fifo_destroy(&socket->accepted);
101
102 if (socket_release)
103 socket_release(socket);
104
105 socket_cores_exclude(local_sockets, socket->socket_id, free);
106}
107
108/** Destroy local sockets.
109 *
110 * Release all buffered packets and call the release function for each of the
111 * sockets.
112 *
113 * @param[in] sess Packet server session.
114 * @param[in] local_sockets Local sockets to be destroyed.
115 * @param[in,out] global_sockets Global sockets to be updated.
116 * @param[in] socket_release Client release callback function.
117 *
118 */
119void socket_cores_release(async_sess_t *sess, socket_cores_t *local_sockets,
120 socket_ports_t *global_sockets,
121 void (* socket_release)(socket_core_t *socket))
122{
123 if (!socket_cores_is_valid(local_sockets))
124 return;
125
126 local_sockets->magic = 0;
127
128 int index;
129 for (index = 0; index < local_sockets->next; ++index) {
130 if (socket_cores_item_is_valid(&local_sockets->items[index])) {
131 local_sockets->items[index].magic = 0;
132
133 if (local_sockets->items[index].value) {
134 socket_destroy_core(sess,
135 local_sockets->items[index].value,
136 local_sockets, global_sockets,
137 socket_release);
138 free(local_sockets->items[index].value);
139 local_sockets->items[index].value = NULL;
140 }
141 }
142 }
143
144 free(local_sockets->items);
145}
146
147/** Adds the socket to a socket port.
148 *
149 * @param[in,out] socket_port The socket port structure.
150 * @param[in] socket The socket to be added.
151 * @param[in] key The socket key identifier.
152 * @param[in] key_length The socket key length.
153 * @return EOK on success.
154 * @return ENOMEM if there is not enough memory left.
155 */
156static int
157socket_port_add_core(socket_port_t *socket_port, socket_core_t *socket,
158 const uint8_t *key, size_t key_length)
159{
160 socket_core_t **socket_ref;
161 int rc;
162
163 /* Create a wrapper */
164 socket_ref = malloc(sizeof(*socket_ref));
165 if (!socket_ref)
166 return ENOMEM;
167
168 *socket_ref = socket;
169 /* Add the wrapper */
170 rc = socket_port_map_add(&socket_port->map, key, key_length,
171 socket_ref);
172 if (rc != EOK) {
173 free(socket_ref);
174 return rc;
175 }
176
177 ++socket_port->count;
178 socket->key = key;
179 socket->key_length = key_length;
180
181 return EOK;
182}
183
184/** Binds the socket to the port.
185 *
186 * The SOCKET_MAP_KEY_LISTENING key identifier is used.
187 *
188 * @param[in] global_sockets The global sockets to be updated.
189 * @param[in] socket The socket to be added.
190 * @param[in] port The port number to be bound to.
191 * @return EOK on success.
192 * @return ENOMEM if there is not enough memory left.
193 * @return Other error codes as defined for the
194 * socket_ports_add() function.
195 */
196static int
197socket_bind_insert(socket_ports_t *global_sockets, socket_core_t *socket,
198 int port)
199{
200 socket_port_t *socket_port;
201 int rc;
202
203 /* Create a wrapper */
204 socket_port = malloc(sizeof(*socket_port));
205 if (!socket_port)
206 return ENOMEM;
207
208 socket_port->count = 0;
209 rc = socket_port_map_initialize(&socket_port->map);
210 if (rc != EOK)
211 goto fail;
212
213 rc = socket_port_add_core(socket_port, socket,
214 (const uint8_t *) SOCKET_MAP_KEY_LISTENING, 0);
215 if (rc != EOK)
216 goto fail;
217
218 /* Register the incoming port */
219 rc = socket_ports_add(global_sockets, port, socket_port);
220 if (rc < 0)
221 goto fail;
222
223 socket->port = port;
224 return EOK;
225
226fail:
227 socket_port_map_destroy(&socket_port->map, free);
228 free(socket_port);
229 return rc;
230
231}
232
233/** Binds the socket to the port.
234 *
235 * The address port is used if set, a free port is used if not.
236 *
237 * @param[in] local_sockets The local sockets to be searched.
238 * @param[in,out] global_sockets The global sockets to be updated.
239 * @param[in] socket_id The new socket identifier.
240 * @param[in] addr The address to be bound to.
241 * @param[in] addrlen The address length.
242 * @param[in] free_ports_start The minimum free port.
243 * @param[in] free_ports_end The maximum free port.
244 * @param[in] last_used_port The last used free port.
245 * @return EOK on success.
246 * @return ENOTSOCK if the socket was not found.
247 * @return EAFNOSUPPORT if the address family is not supported.
248 * @return EADDRINUSE if the port is already in use.
249 * @return Other error codes as defined for the
250 * socket_bind_free_port() function.
251 * @return Other error codes as defined for the
252 * socket_bind_insert() function.
253 */
254int
255socket_bind(socket_cores_t *local_sockets, socket_ports_t *global_sockets,
256 int socket_id, void *addr, size_t addrlen, int free_ports_start,
257 int free_ports_end, int last_used_port)
258{
259 socket_core_t *socket;
260 socket_port_t *socket_port;
261 struct sockaddr *address;
262 struct sockaddr_in *address_in;
263
264 if (addrlen < sizeof(struct sockaddr))
265 return EINVAL;
266
267 address = (struct sockaddr *) addr;
268 switch (address->sa_family) {
269 case AF_INET:
270 if (addrlen != sizeof(struct sockaddr_in))
271 return EINVAL;
272
273 address_in = (struct sockaddr_in *) addr;
274 /* Find the socket */
275 socket = socket_cores_find(local_sockets, socket_id);
276 if (!socket)
277 return ENOTSOCK;
278
279 /* Bind a free port? */
280 if (address_in->sin_port <= 0)
281 return socket_bind_free_port(global_sockets, socket,
282 free_ports_start, free_ports_end, last_used_port);
283
284 /* Try to find the port */
285 socket_port = socket_ports_find(global_sockets,
286 ntohs(address_in->sin_port));
287 if (socket_port) {
288 /* Already used */
289 return EADDRINUSE;
290 }
291
292 /* If bound */
293 if (socket->port) {
294 /* Release the port */
295 socket_port_release(global_sockets, socket);
296 }
297 socket->port = -1;
298
299 return socket_bind_insert(global_sockets, socket,
300 ntohs(address_in->sin_port));
301
302 case AF_INET6:
303 // TODO IPv6
304 break;
305 }
306
307 return EAFNOSUPPORT;
308}
309
310/** Binds the socket to a free port.
311 *
312 * The first free port is used.
313 *
314 * @param[in,out] global_sockets The global sockets to be updated.
315 * @param[in,out] socket The socket to be bound.
316 * @param[in] free_ports_start The minimum free port.
317 * @param[in] free_ports_end The maximum free port.
318 * @param[in] last_used_port The last used free port.
319 * @return EOK on success.
320 * @return ENOTCONN if no free port was found.
321 * @return Other error codes as defined for the
322 * socket_bind_insert() function.
323 */
324int
325socket_bind_free_port(socket_ports_t *global_sockets, socket_core_t *socket,
326 int free_ports_start, int free_ports_end, int last_used_port)
327{
328 int index;
329
330 /* From the last used one */
331 index = last_used_port;
332
333 do {
334 ++index;
335
336 /* Till the range end */
337 if (index >= free_ports_end) {
338 /* Start from the range beginning */
339 index = free_ports_start - 1;
340 do {
341 ++index;
342 /* Till the last used one */
343 if (index >= last_used_port) {
344 /* None found */
345 return ENOTCONN;
346 }
347 } while (socket_ports_find(global_sockets, index));
348
349 /* Found, break immediately */
350 break;
351 }
352
353 } while (socket_ports_find(global_sockets, index));
354
355 return socket_bind_insert(global_sockets, socket, index);
356}
357
358/** Tries to find a new free socket identifier.
359 *
360 * @param[in] local_sockets The local sockets to be searched.
361 * @param[in] positive A value indicating whether a positive identifier is
362 * requested. A negative identifier is requested if set to
363 * false.
364 * @return The new socket identifier.
365 * @return ELIMIT if there is no socket identifier available.
366 */
367static int socket_generate_new_id(socket_cores_t *local_sockets, int positive)
368{
369 int socket_id;
370 int count;
371
372 count = 0;
373#if 0
374 socket_id = socket_globals.last_id;
375#endif
376 do {
377 if (count < SOCKET_ID_TRIES) {
378 socket_id = rand() % INT_MAX;
379 ++count;
380 } else if (count == SOCKET_ID_TRIES) {
381 socket_id = 1;
382 ++count;
383 /* Only this branch for last_id */
384 } else {
385 if (socket_id < INT_MAX) {
386 ++ socket_id;
387#if 0
388 } else if(socket_globals.last_id) {
389 socket_globals.last_id = 0;
390 socket_id = 1;
391#endif
392 } else {
393 return ELIMIT;
394 }
395 }
396 } while (socket_cores_find(local_sockets,
397 ((positive ? 1 : -1) * socket_id)));
398
399// last_id = socket_id
400 return socket_id;
401}
402
403/** Create a new socket.
404 *
405 * @param[in,out] local_sockets Local sockets to be updated.
406 * @param[in] sess Application session.
407 * @param[in] specific_data Socket specific data.
408 * @param[in,out] socket_id New socket identifier. A new identifier
409 * is chosen if set to zero or negative.
410 * A negative identifier is chosen if set
411 * to negative.
412 *
413 * @return EOK on success.
414 * @return EINVAL if the socket_id parameter is NULL.
415 * @return ENOMEM if there is not enough memory left.
416 *
417 */
418int socket_create(socket_cores_t *local_sockets, async_sess_t* sess,
419 void *specific_data, int *socket_id)
420{
421 socket_core_t *socket;
422 int positive;
423 int rc;
424
425 if (!socket_id)
426 return EINVAL;
427
428 /* Store the socket */
429 if (*socket_id <= 0) {
430 positive = (*socket_id == 0);
431 *socket_id = socket_generate_new_id(local_sockets, positive);
432 if (*socket_id <= 0)
433 return *socket_id;
434 if (!positive)
435 *socket_id *= -1;
436 } else if(socket_cores_find(local_sockets, *socket_id)) {
437 return EEXIST;
438 }
439
440 socket = (socket_core_t *) malloc(sizeof(*socket));
441 if (!socket)
442 return ENOMEM;
443
444 /* Initialize */
445 socket->sess = sess;
446 socket->port = -1;
447 socket->key = NULL;
448 socket->key_length = 0;
449 socket->specific_data = specific_data;
450 rc = dyn_fifo_initialize(&socket->received, SOCKET_INITIAL_RECEIVED_SIZE);
451 if (rc != EOK) {
452 free(socket);
453 return rc;
454 }
455
456 rc = dyn_fifo_initialize(&socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE);
457 if (rc != EOK) {
458 dyn_fifo_destroy(&socket->received);
459 free(socket);
460 return rc;
461 }
462 socket->socket_id = *socket_id;
463 rc = socket_cores_add(local_sockets, socket->socket_id, socket);
464 if (rc < 0) {
465 dyn_fifo_destroy(&socket->received);
466 dyn_fifo_destroy(&socket->accepted);
467 free(socket);
468 return rc;
469 }
470
471 return EOK;
472}
473
474/** Destroy the socket.
475 *
476 * If the socket is bound, the port is released.
477 * Release all buffered packets, call the release function and remove the
478 * socket from the local sockets.
479 *
480 * @param[in] sess Packet server session.
481 * @param[in] socket_id Socket identifier.
482 * @param[in,out] local_sockets Local sockets to be updated.
483 * @param[in,out] global_sockets Global sockets to be updated.
484 * @param[in] socket_release Client release callback function.
485 *
486 * @return EOK on success.
487 * @return ENOTSOCK if the socket is not found.
488 *
489 */
490int
491socket_destroy(async_sess_t *sess, int socket_id, socket_cores_t *local_sockets,
492 socket_ports_t *global_sockets,
493 void (*socket_release)(socket_core_t *socket))
494{
495 /* Find the socket */
496 socket_core_t *socket = socket_cores_find(local_sockets, socket_id);
497 if (!socket)
498 return ENOTSOCK;
499
500 /* Destroy all accepted sockets */
501 int accepted_id;
502 while ((accepted_id = dyn_fifo_pop(&socket->accepted)) >= 0)
503 socket_destroy(sess, accepted_id, local_sockets,
504 global_sockets, socket_release);
505
506 socket_destroy_core(sess, socket, local_sockets, global_sockets,
507 socket_release);
508
509 return EOK;
510}
511
512/** Replies the packet or the packet queue data to the application via the
513 * socket.
514 *
515 * Uses the current message processing fibril.
516 *
517 * @param[in] packet The packet to be transfered.
518 * @param[out] length The total data length.
519 * @return EOK on success.
520 * @return EBADMEM if the length parameter is NULL.
521 * @return ENOMEM if there is not enough memory left.
522 * @return Other error codes as defined for the data_reply()
523 * function.
524 */
525int socket_reply_packets(packet_t *packet, size_t *length)
526{
527 packet_t *next_packet;
528 size_t fragments;
529 size_t *lengths;
530 size_t index;
531 int rc;
532
533 if (!length)
534 return EBADMEM;
535
536 next_packet = pq_next(packet);
537 if (!next_packet) {
538 /* Write all if only one fragment */
539 rc = data_reply(packet_get_data(packet),
540 packet_get_data_length(packet));
541 if (rc != EOK)
542 return rc;
543 /* Store the total length */
544 *length = packet_get_data_length(packet);
545 } else {
546 /* Count the packet fragments */
547 fragments = 1;
548 next_packet = pq_next(packet);
549 while ((next_packet = pq_next(next_packet)))
550 ++fragments;
551
552 /* Compute and store the fragment lengths */
553 lengths = (size_t *) malloc(sizeof(size_t) * fragments +
554 sizeof(size_t));
555 if (!lengths)
556 return ENOMEM;
557
558 lengths[0] = packet_get_data_length(packet);
559 lengths[fragments] = lengths[0];
560 next_packet = pq_next(packet);
561
562 for (index = 1; index < fragments; ++index) {
563 lengths[index] = packet_get_data_length(next_packet);
564 lengths[fragments] += lengths[index];
565 next_packet = pq_next(packet);
566 }
567
568 /* Write the fragment lengths */
569 rc = data_reply(lengths, sizeof(int) * (fragments + 1));
570 if (rc != EOK) {
571 free(lengths);
572 return rc;
573 }
574 next_packet = packet;
575
576 /* Write the fragments */
577 for (index = 0; index < fragments; ++index) {
578 rc = data_reply(packet_get_data(next_packet),
579 lengths[index]);
580 if (rc != EOK) {
581 free(lengths);
582 return rc;
583 }
584 next_packet = pq_next(next_packet);
585 }
586
587 /* Store the total length */
588 *length = lengths[fragments];
589 free(lengths);
590 }
591
592 return EOK;
593}
594
595/** Finds the bound port socket.
596 *
597 * @param[in] global_sockets The global sockets to be searched.
598 * @param[in] port The port number.
599 * @param[in] key The socket key identifier.
600 * @param[in] key_length The socket key length.
601 * @return The found socket.
602 * @return NULL if no socket was found.
603 */
604socket_core_t *
605socket_port_find(socket_ports_t *global_sockets, int port, const uint8_t *key,
606 size_t key_length)
607{
608 socket_port_t *socket_port;
609 socket_core_t **socket_ref;
610
611 socket_port = socket_ports_find(global_sockets, port);
612 if (socket_port && (socket_port->count > 0)) {
613 socket_ref = socket_port_map_find(&socket_port->map, key,
614 key_length);
615 if (socket_ref)
616 return *socket_ref;
617 }
618
619 return NULL;
620}
621
622/** Releases the socket port.
623 *
624 * If the socket is bound the port entry is released.
625 * If there are no more port entries the port is release.
626 *
627 * @param[in] global_sockets The global sockets to be updated.
628 * @param[in] socket The socket to be unbound.
629 */
630void
631socket_port_release(socket_ports_t *global_sockets, socket_core_t *socket)
632{
633 socket_port_t *socket_port;
634 socket_core_t **socket_ref;
635
636 if (!socket->port)
637 return;
638
639 /* Find ports */
640 socket_port = socket_ports_find(global_sockets, socket->port);
641 if (socket_port) {
642 /* Find the socket */
643 socket_ref = socket_port_map_find(&socket_port->map,
644 socket->key, socket->key_length);
645
646 if (socket_ref) {
647 --socket_port->count;
648
649 /* Release if empty */
650 if (socket_port->count <= 0) {
651 /* Destroy the map */
652 socket_port_map_destroy(&socket_port->map, free);
653 /* Release the port */
654 socket_ports_exclude(global_sockets,
655 socket->port, free);
656 } else {
657 /* Remove */
658 socket_port_map_exclude(&socket_port->map,
659 socket->key, socket->key_length, free);
660 }
661 }
662 }
663
664 socket->port = 0;
665 socket->key = NULL;
666 socket->key_length = 0;
667}
668
669/** Adds the socket to an already bound port.
670 *
671 * @param[in] global_sockets The global sockets to be updated.
672 * @param[in] port The port number to be bound to.
673 * @param[in] socket The socket to be added.
674 * @param[in] key The socket key identifier.
675 * @param[in] key_length The socket key length.
676 * @return EOK on success.
677 * @return ENOENT if the port is not already used.
678 * @return Other error codes as defined for the
679 * socket_port_add_core() function.
680 */
681int
682socket_port_add(socket_ports_t *global_sockets, int port,
683 socket_core_t *socket, const uint8_t *key, size_t key_length)
684{
685 socket_port_t *socket_port;
686 int rc;
687
688 /* Find ports */
689 socket_port = socket_ports_find(global_sockets, port);
690 if (!socket_port)
691 return ENOENT;
692
693 /* Add the socket */
694 rc = socket_port_add_core(socket_port, socket, key, key_length);
695 if (rc != EOK)
696 return rc;
697
698 socket->port = port;
699 return EOK;
700}
701
702/** @}
703 */
Note: See TracBrowser for help on using the repository browser.