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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b688fd8 was b688fd8, checked in by Martin Decky <martin@…>, 10 years ago

gradually introduce async ports, initial phase

The initial phase is to reimplement the traditional async client connections as an untyped fallback port. This creates the possibility to introduce ports typed by interface type gradually in later changesets.

  • 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_fallback_port_handler(inet_client_conn, NULL);
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.