source: mainline/uspace/srv/net/inetsrv/inetsrv.c@ f5c03a8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f5c03a8 was 1c635d6, checked in by Martin Sucha <sucha14@…>, 11 years ago

Do not hold a task's return value after it has disconnected.

Holding the task's return value meant that if nobody waited
for task's result, it polluted NS's memory. This was apparently
done because of a race between spawning a task and waiting for it.

We solve this problem in another way: ns discards the return value
as soon as the task disconnects from it. This typically happens
when the task finishes its execution. In order to avoid the race,
we send the wait request to ns while spawning the task (i.e. when
we talk to the loader), but before we allow the loaded program
to run.

Fixes #132

  • Property mode set to 100644
File size: 13.3 KB
Line 
1/*
2 * Copyright (c) 2012 Jiri Svoboda
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 inet
30 * @{
31 */
32/**
33 * @file
34 * @brief Internet Protocol service
35 */
36
37#include <adt/list.h>
38#include <async.h>
39#include <errno.h>
40#include <fibril_synch.h>
41#include <io/log.h>
42#include <ipc/inet.h>
43#include <ipc/services.h>
44#include <loc.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <sys/types.h>
48#include <task.h>
49#include "addrobj.h"
50#include "icmp.h"
51#include "icmp_std.h"
52#include "icmpv6.h"
53#include "icmpv6_std.h"
54#include "inetsrv.h"
55#include "inetcfg.h"
56#include "inetping.h"
57#include "inet_link.h"
58#include "reass.h"
59#include "sroute.h"
60
61#define NAME "inetsrv"
62
63static inet_naddr_t solicited_node_mask = {
64 .version = ip_v6,
65 .addr6 = {0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0xff, 0, 0, 0},
66 .prefix = 104
67};
68
69static inet_addr_t broadcast4_all_hosts = {
70 .version = ip_v4,
71 .addr = 0xffffffff
72};
73
74static inet_addr_t multicast_all_nodes = {
75 .version = ip_v6,
76 .addr6 = {0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}
77};
78
79static void inet_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg);
80
81static FIBRIL_MUTEX_INITIALIZE(client_list_lock);
82static LIST_INITIALIZE(client_list);
83
84static int inet_init(void)
85{
86 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_init()");
87
88 async_set_client_connection(inet_client_conn);
89
90 int rc = loc_server_register(NAME);
91 if (rc != EOK) {
92 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server (%d).", rc);
93 return EEXIST;
94 }
95
96 service_id_t sid;
97 rc = loc_service_register_with_iface(SERVICE_NAME_INET, &sid,
98 INET_PORT_DEFAULT);
99 if (rc != EOK) {
100 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service (%d).", rc);
101 return EEXIST;
102 }
103
104 rc = loc_service_register_with_iface(SERVICE_NAME_INETCFG, &sid,
105 INET_PORT_CFG);
106 if (rc != EOK) {
107 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service (%d).", rc);
108 return EEXIST;
109 }
110
111 rc = loc_service_register_with_iface(SERVICE_NAME_INETPING, &sid,
112 INET_PORT_PING);
113 if (rc != EOK) {
114 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service (%d).", rc);
115 return EEXIST;
116 }
117
118 return EOK;
119}
120
121static void inet_callback_create_srv(inet_client_t *client, ipc_callid_t callid,
122 ipc_call_t *call)
123{
124 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_callback_create_srv()");
125
126 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
127 if (sess == NULL) {
128 async_answer_0(callid, ENOMEM);
129 return;
130 }
131
132 client->sess = sess;
133 async_answer_0(callid, EOK);
134}
135
136static int inet_find_dir(inet_addr_t *src, inet_addr_t *dest, uint8_t tos,
137 inet_dir_t *dir)
138{
139 inet_sroute_t *sr;
140
141 /* XXX Handle case where source address is specified */
142 (void) src;
143
144 dir->aobj = inet_addrobj_find(dest, iaf_net);
145 if (dir->aobj != NULL) {
146 dir->ldest = *dest;
147 dir->dtype = dt_direct;
148 } else {
149 /* No direct path, try using a static route */
150 sr = inet_sroute_find(dest);
151 if (sr != NULL) {
152 dir->aobj = inet_addrobj_find(&sr->router, iaf_net);
153 dir->ldest = sr->router;
154 dir->dtype = dt_router;
155 }
156 }
157
158 if (dir->aobj == NULL) {
159 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_send: No route to destination.");
160 return ENOENT;
161 }
162
163 return EOK;
164}
165
166int inet_route_packet(inet_dgram_t *dgram, uint8_t proto, uint8_t ttl,
167 int df)
168{
169 inet_dir_t dir;
170 inet_link_t *ilink;
171 int rc;
172
173 if (dgram->iplink != 0) {
174 /* XXX TODO - IPv6 */
175 log_msg(LOG_DEFAULT, LVL_DEBUG, "dgram directly to iplink %zu",
176 dgram->iplink);
177 /* Send packet directly to the specified IP link */
178 ilink = inet_link_get_by_id(dgram->iplink);
179 if (ilink == 0)
180 return ENOENT;
181
182 if (dgram->src.version != ip_v4 ||
183 dgram->dest.version != ip_v4)
184 return EINVAL;
185
186 return inet_link_send_dgram(ilink, dgram->src.addr,
187 dgram->dest.addr, dgram, proto, ttl, df);
188 }
189
190 log_msg(LOG_DEFAULT, LVL_DEBUG, "dgram to be routed");
191
192 /* Route packet using source/destination addresses */
193
194 rc = inet_find_dir(&dgram->src, &dgram->dest, dgram->tos, &dir);
195 if (rc != EOK)
196 return rc;
197
198 return inet_addrobj_send_dgram(dir.aobj, &dir.ldest, dgram,
199 proto, ttl, df);
200}
201
202static int inet_send(inet_client_t *client, inet_dgram_t *dgram,
203 uint8_t proto, uint8_t ttl, int df)
204{
205 return inet_route_packet(dgram, proto, ttl, df);
206}
207
208int inet_get_srcaddr(inet_addr_t *remote, uint8_t tos, inet_addr_t *local)
209{
210 inet_dir_t dir;
211 int rc;
212
213 rc = inet_find_dir(NULL, remote, tos, &dir);
214 if (rc != EOK)
215 return rc;
216
217 /* XXX dt_local? */
218
219 /* Take source address from the address object */
220 if (remote->version == ip_v4 && remote->addr == 0xffffffff) {
221 /* XXX TODO - IPv6 */
222 local->version = ip_v4;
223 local->addr = 0;
224 return EOK;
225 }
226
227 inet_naddr_addr(&dir.aobj->naddr, local);
228 return EOK;
229}
230
231static void inet_get_srcaddr_srv(inet_client_t *client, ipc_callid_t iid,
232 ipc_call_t *icall)
233{
234 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_get_srcaddr_srv()");
235
236 uint8_t tos = IPC_GET_ARG1(*icall);
237
238 ipc_callid_t callid;
239 size_t size;
240 if (!async_data_write_receive(&callid, &size)) {
241 async_answer_0(callid, EREFUSED);
242 async_answer_0(iid, EREFUSED);
243 return;
244 }
245
246 if (size != sizeof(inet_addr_t)) {
247 async_answer_0(callid, EINVAL);
248 async_answer_0(iid, EINVAL);
249 return;
250 }
251
252 inet_addr_t remote;
253 int rc = async_data_write_finalize(callid, &remote, size);
254 if (rc != EOK) {
255 async_answer_0(callid, rc);
256 async_answer_0(iid, rc);
257 }
258
259 inet_addr_t local;
260 rc = inet_get_srcaddr(&remote, tos, &local);
261 if (rc != EOK) {
262 async_answer_0(iid, rc);
263 return;
264 }
265
266 if (!async_data_read_receive(&callid, &size)) {
267 async_answer_0(callid, EREFUSED);
268 async_answer_0(iid, EREFUSED);
269 return;
270 }
271
272 if (size != sizeof(inet_addr_t)) {
273 async_answer_0(callid, EINVAL);
274 async_answer_0(iid, EINVAL);
275 return;
276 }
277
278 rc = async_data_read_finalize(callid, &local, size);
279 if (rc != EOK) {
280 async_answer_0(callid, rc);
281 async_answer_0(iid, rc);
282 return;
283 }
284
285 async_answer_0(iid, rc);
286}
287
288static void inet_send_srv(inet_client_t *client, ipc_callid_t iid,
289 ipc_call_t *icall)
290{
291 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_send_srv()");
292
293 inet_dgram_t dgram;
294
295 dgram.iplink = IPC_GET_ARG1(*icall);
296 dgram.tos = IPC_GET_ARG2(*icall);
297
298 uint8_t ttl = IPC_GET_ARG3(*icall);
299 int df = IPC_GET_ARG4(*icall);
300
301 ipc_callid_t callid;
302 size_t size;
303 if (!async_data_write_receive(&callid, &size)) {
304 async_answer_0(callid, EREFUSED);
305 async_answer_0(iid, EREFUSED);
306 return;
307 }
308
309 if (size != sizeof(inet_addr_t)) {
310 async_answer_0(callid, EINVAL);
311 async_answer_0(iid, EINVAL);
312 return;
313 }
314
315 int rc = async_data_write_finalize(callid, &dgram.src, size);
316 if (rc != EOK) {
317 async_answer_0(callid, rc);
318 async_answer_0(iid, rc);
319 }
320
321 if (!async_data_write_receive(&callid, &size)) {
322 async_answer_0(callid, EREFUSED);
323 async_answer_0(iid, EREFUSED);
324 return;
325 }
326
327 if (size != sizeof(inet_addr_t)) {
328 async_answer_0(callid, EINVAL);
329 async_answer_0(iid, EINVAL);
330 return;
331 }
332
333 rc = async_data_write_finalize(callid, &dgram.dest, size);
334 if (rc != EOK) {
335 async_answer_0(callid, rc);
336 async_answer_0(iid, rc);
337 }
338
339 rc = async_data_write_accept(&dgram.data, false, 0, 0, 0,
340 &dgram.size);
341 if (rc != EOK) {
342 async_answer_0(iid, rc);
343 return;
344 }
345
346 rc = inet_send(client, &dgram, client->protocol, ttl, df);
347
348 free(dgram.data);
349 async_answer_0(iid, rc);
350}
351
352static void inet_set_proto_srv(inet_client_t *client, ipc_callid_t callid,
353 ipc_call_t *call)
354{
355 sysarg_t proto;
356
357 proto = IPC_GET_ARG1(*call);
358 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_set_proto_srv(%lu)", (unsigned long) proto);
359
360 if (proto > UINT8_MAX) {
361 async_answer_0(callid, EINVAL);
362 return;
363 }
364
365 client->protocol = proto;
366 async_answer_0(callid, EOK);
367}
368
369static void inet_client_init(inet_client_t *client)
370{
371 client->sess = NULL;
372
373 fibril_mutex_lock(&client_list_lock);
374 list_append(&client->client_list, &client_list);
375 fibril_mutex_unlock(&client_list_lock);
376}
377
378static void inet_client_fini(inet_client_t *client)
379{
380 async_hangup(client->sess);
381 client->sess = NULL;
382
383 fibril_mutex_lock(&client_list_lock);
384 list_remove(&client->client_list);
385 fibril_mutex_unlock(&client_list_lock);
386}
387
388static void inet_default_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
389{
390 inet_client_t client;
391
392 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_default_conn()");
393
394 /* Accept the connection */
395 async_answer_0(iid, EOK);
396
397 inet_client_init(&client);
398
399 while (true) {
400 ipc_call_t call;
401 ipc_callid_t callid = async_get_call(&call);
402 sysarg_t method = IPC_GET_IMETHOD(call);
403
404 if (!method) {
405 /* The other side has hung up */
406 async_answer_0(callid, EOK);
407 return;
408 }
409
410 switch (method) {
411 case INET_CALLBACK_CREATE:
412 inet_callback_create_srv(&client, callid, &call);
413 break;
414 case INET_GET_SRCADDR:
415 inet_get_srcaddr_srv(&client, callid, &call);
416 break;
417 case INET_SEND:
418 inet_send_srv(&client, callid, &call);
419 break;
420 case INET_SET_PROTO:
421 inet_set_proto_srv(&client, callid, &call);
422 break;
423 default:
424 async_answer_0(callid, EINVAL);
425 }
426 }
427
428 inet_client_fini(&client);
429}
430
431static void inet_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
432{
433 sysarg_t port;
434
435 port = IPC_GET_ARG1(*icall);
436
437 switch (port) {
438 case INET_PORT_DEFAULT:
439 inet_default_conn(iid, icall, arg);
440 break;
441 case INET_PORT_CFG:
442 inet_cfg_conn(iid, icall, arg);
443 break;
444 case INET_PORT_PING:
445 inetping_conn(iid, icall, arg);
446 break;
447 default:
448 async_answer_0(iid, ENOTSUP);
449 break;
450 }
451}
452
453static inet_client_t *inet_client_find(uint8_t proto)
454{
455 fibril_mutex_lock(&client_list_lock);
456
457 list_foreach(client_list, client_list, inet_client_t, client) {
458 if (client->protocol == proto) {
459 fibril_mutex_unlock(&client_list_lock);
460 return client;
461 }
462 }
463
464 fibril_mutex_unlock(&client_list_lock);
465 return NULL;
466}
467
468int inet_ev_recv(inet_client_t *client, inet_dgram_t *dgram)
469{
470 async_exch_t *exch = async_exchange_begin(client->sess);
471
472 ipc_call_t answer;
473 aid_t req = async_send_1(exch, INET_EV_RECV, dgram->tos, &answer);
474
475 int rc = async_data_write_start(exch, &dgram->src, sizeof(inet_addr_t));
476 if (rc != EOK) {
477 async_exchange_end(exch);
478 async_forget(req);
479 return rc;
480 }
481
482 rc = async_data_write_start(exch, &dgram->dest, sizeof(inet_addr_t));
483 if (rc != EOK) {
484 async_exchange_end(exch);
485 async_forget(req);
486 return rc;
487 }
488
489 rc = async_data_write_start(exch, dgram->data, dgram->size);
490
491 async_exchange_end(exch);
492
493 if (rc != EOK) {
494 async_forget(req);
495 return rc;
496 }
497
498 sysarg_t retval;
499 async_wait_for(req, &retval);
500
501 return (int) retval;
502}
503
504int inet_recv_dgram_local(inet_dgram_t *dgram, uint8_t proto)
505{
506 inet_client_t *client;
507
508 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_recv_dgram_local()");
509
510 /* ICMP and ICMPv6 messages are handled internally */
511 if (proto == IP_PROTO_ICMP)
512 return icmp_recv(dgram);
513
514 if (proto == IP_PROTO_ICMPV6)
515 return icmpv6_recv(dgram);
516
517 client = inet_client_find(proto);
518 if (client == NULL) {
519 log_msg(LOG_DEFAULT, LVL_DEBUG, "No client found for protocol 0x%" PRIx8,
520 proto);
521 return ENOENT;
522 }
523
524 return inet_ev_recv(client, dgram);
525}
526
527int inet_recv_packet(inet_packet_t *packet)
528{
529 inet_addrobj_t *addr;
530 inet_dgram_t dgram;
531
532 addr = inet_addrobj_find(&packet->dest, iaf_addr);
533 if ((addr != NULL) ||
534 (inet_naddr_compare_mask(&solicited_node_mask, &packet->dest)) ||
535 (inet_addr_compare(&multicast_all_nodes, &packet->dest)) ||
536 (inet_addr_compare(&broadcast4_all_hosts, &packet->dest))) {
537 /* Destined for one of the local addresses */
538
539 /* Check if packet is a complete datagram */
540 if (packet->offs == 0 && !packet->mf) {
541 /* It is complete deliver it immediately */
542 dgram.src = packet->src;
543 dgram.dest = packet->dest;
544 dgram.tos = packet->tos;
545 dgram.data = packet->data;
546 dgram.size = packet->size;
547
548 return inet_recv_dgram_local(&dgram, packet->proto);
549 } else {
550 /* It is a fragment, queue it for reassembly */
551 inet_reass_queue_packet(packet);
552 }
553 }
554
555 return ENOENT;
556}
557
558int main(int argc, char *argv[])
559{
560 int rc;
561
562 printf(NAME ": HelenOS Internet Protocol service\n");
563
564 if (log_init(NAME) != EOK) {
565 printf(NAME ": Failed to initialize logging.\n");
566 return 1;
567 }
568
569 rc = inet_init();
570 if (rc != EOK)
571 return 1;
572
573 printf(NAME ": Accepting connections.\n");
574 task_retval(0);
575 async_manager();
576
577 /* Not reached */
578 return 0;
579}
580
581/** @}
582 */
Note: See TracBrowser for help on using the repository browser.