source: mainline/uspace/srv/net/inetsrv/inetsrv.c@ 3a4c6d9

Last change on this file since 3a4c6d9 was 3a4c6d9, checked in by Jiri Svoboda <jiri@…>, 2 months ago

Packet capture (thx Nataliia Korop)

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