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

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

Move IP link discovery to a separate network configuration server.

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