source: mainline/uspace/srv/net/inetsrv/inetsrv.c@ 26de91a

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

Remove include where not needed anymore.

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