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

Last change on this file since ca48672 was ca48672, checked in by Jiri Svoboda <jiri@…>, 8 days ago

loc_service_register() needs to take a port ID argument.

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