source: mainline/uspace/srv/net/inetsrv/inetsrv.c@ 59ff52d

Last change on this file since 59ff52d was 59ff52d, checked in by Jakub Jermar <jakub@…>, 7 years ago

Add async_accept_0() for accepting connections

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