source: mainline/uspace/srv/net/inetsrv/inetsrv.c@ 7c15d6f

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

Find the association to deliver the datagram using amap.

  • Property mode set to 100644
File size: 13.4 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
474 log_msg(LOG_DEFAULT, LVL_NOTE, "inet_ev_recv: iplink=%zu",
475 dgram->iplink);
476
477 aid_t req = async_send_2(exch, INET_EV_RECV, dgram->tos,
478 dgram->iplink, &answer);
479
480 int rc = async_data_write_start(exch, &dgram->src, sizeof(inet_addr_t));
481 if (rc != EOK) {
482 async_exchange_end(exch);
483 async_forget(req);
484 return rc;
485 }
486
487 rc = async_data_write_start(exch, &dgram->dest, sizeof(inet_addr_t));
488 if (rc != EOK) {
489 async_exchange_end(exch);
490 async_forget(req);
491 return rc;
492 }
493
494 rc = async_data_write_start(exch, dgram->data, dgram->size);
495
496 async_exchange_end(exch);
497
498 if (rc != EOK) {
499 async_forget(req);
500 return rc;
501 }
502
503 sysarg_t retval;
504 async_wait_for(req, &retval);
505
506 return (int) retval;
507}
508
509int inet_recv_dgram_local(inet_dgram_t *dgram, uint8_t proto)
510{
511 inet_client_t *client;
512
513 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_recv_dgram_local()");
514
515 /* ICMP and ICMPv6 messages are handled internally */
516 if (proto == IP_PROTO_ICMP)
517 return icmp_recv(dgram);
518
519 if (proto == IP_PROTO_ICMPV6)
520 return icmpv6_recv(dgram);
521
522 client = inet_client_find(proto);
523 if (client == NULL) {
524 log_msg(LOG_DEFAULT, LVL_DEBUG, "No client found for protocol 0x%" PRIx8,
525 proto);
526 return ENOENT;
527 }
528
529 return inet_ev_recv(client, dgram);
530}
531
532int inet_recv_packet(inet_packet_t *packet)
533{
534 inet_addrobj_t *addr;
535 inet_dgram_t dgram;
536
537 addr = inet_addrobj_find(&packet->dest, iaf_addr);
538 if ((addr != NULL) ||
539 (inet_naddr_compare_mask(&solicited_node_mask, &packet->dest)) ||
540 (inet_addr_compare(&multicast_all_nodes, &packet->dest)) ||
541 (inet_addr_compare(&broadcast4_all_hosts, &packet->dest))) {
542 /* Destined for one of the local addresses */
543
544 /* Check if packet is a complete datagram */
545 if (packet->offs == 0 && !packet->mf) {
546 /* It is complete deliver it immediately */
547 dgram.iplink = packet->link_id;
548 dgram.src = packet->src;
549 dgram.dest = packet->dest;
550 dgram.tos = packet->tos;
551 dgram.data = packet->data;
552 dgram.size = packet->size;
553
554 return inet_recv_dgram_local(&dgram, packet->proto);
555 } else {
556 /* It is a fragment, queue it for reassembly */
557 inet_reass_queue_packet(packet);
558 }
559 }
560
561 return ENOENT;
562}
563
564int main(int argc, char *argv[])
565{
566 int rc;
567
568 printf(NAME ": HelenOS Internet Protocol service\n");
569
570 if (log_init(NAME) != EOK) {
571 printf(NAME ": Failed to initialize logging.\n");
572 return 1;
573 }
574
575 rc = inet_init();
576 if (rc != EOK)
577 return 1;
578
579 printf(NAME ": Accepting connections.\n");
580 task_retval(0);
581 async_manager();
582
583 /* Not reached */
584 return 0;
585}
586
587/** @}
588 */
Note: See TracBrowser for help on using the repository browser.