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

Last change on this file since 3951046 was 1bbc6dc, checked in by Jiri Svoboda <jiri@…>, 9 months ago

Network configuration persistence.

nconfsrv is folded into inetsrv
DHCP is disabled when a static address is configured on a link

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