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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c898236 was 695b6ff, checked in by Jiri Svoboda <jiri@…>, 12 years ago

Crude DHCP client prototype.

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