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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c6533a7 was c6533a7, checked in by Jiri Svoboda <jiri@…>, 13 years ago

Remove most of net-related code from libnet and libc, except parts which are
presumably needed to implement sockets. Socket implementation will be replaced
later. NIC drivers should not include/link libnet.

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