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

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

support arbitrary addresses in INET_SEND and INET_GET_SRCADDR
use addr32_t instead of uint32_t

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