source: mainline/uspace/srv/inet/inet.c@ 6428115

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

Ping utility, ICMP echo send and receive.

  • Property mode set to 100644
File size: 9.3 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
49#include "addrobj.h"
50#include "icmp.h"
51#include "icmp_std.h"
52#include "inet.h"
53#include "inetcfg.h"
54#include "inetping.h"
55#include "inet_link.h"
56
57#define NAME "inet"
58
59static void inet_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg);
60
61static FIBRIL_MUTEX_INITIALIZE(client_list_lock);
62static LIST_INITIALIZE(client_list);
63
64static int inet_init(void)
65{
66 service_id_t sid;
67 int rc;
68
69 log_msg(LVL_DEBUG, "inet_init()");
70
71 async_set_client_connection(inet_client_conn);
72
73 rc = loc_server_register(NAME);
74 if (rc != EOK) {
75 log_msg(LVL_ERROR, "Failed registering server (%d).", rc);
76 return EEXIST;
77 }
78
79 rc = loc_service_register_with_iface(SERVICE_NAME_INET, &sid,
80 INET_PORT_DEFAULT);
81 if (rc != EOK) {
82 log_msg(LVL_ERROR, "Failed registering service (%d).", rc);
83 return EEXIST;
84 }
85
86 rc = loc_service_register_with_iface(SERVICE_NAME_INETCFG, &sid,
87 INET_PORT_CFG);
88 if (rc != EOK) {
89 log_msg(LVL_ERROR, "Failed registering service (%d).", rc);
90 return EEXIST;
91 }
92
93 rc = loc_service_register_with_iface(SERVICE_NAME_INETPING, &sid,
94 INET_PORT_PING);
95 if (rc != EOK) {
96 log_msg(LVL_ERROR, "Failed registering service (%d).", rc);
97 return EEXIST;
98 }
99
100 rc = inet_link_discovery_start();
101 if (rc != EOK)
102 return EEXIST;
103
104 return EOK;
105}
106
107static void inet_callback_create_srv(inet_client_t *client, ipc_callid_t callid,
108 ipc_call_t *call)
109{
110 log_msg(LVL_DEBUG, "inet_callback_create_srv()");
111
112 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
113 if (sess == NULL) {
114 async_answer_0(callid, ENOMEM);
115 return;
116 }
117
118 client->sess = sess;
119 async_answer_0(callid, EOK);
120}
121
122int inet_route_packet(inet_dgram_t *dgram, uint8_t proto, uint8_t ttl,
123 int df)
124{
125 inet_addrobj_t *addr;
126
127 addr = inet_addrobj_find(&dgram->dest, iaf_net);
128 if (addr != NULL) {
129 /* Destination is directly accessible */
130 return inet_addrobj_send_dgram(addr, dgram, proto, ttl, df);
131 }
132
133 /* TODO: Gateways */
134 log_msg(LVL_DEBUG, "inet_send: No route to destination.");
135 return ENOENT;
136}
137
138static int inet_send(inet_client_t *client, inet_dgram_t *dgram,
139 uint8_t proto, uint8_t ttl, int df)
140{
141 return inet_route_packet(dgram, proto, ttl, df);
142}
143
144int inet_get_srcaddr(inet_addr_t *remote, uint8_t tos, inet_addr_t *local)
145{
146 inet_addrobj_t *addr;
147
148 addr = inet_addrobj_find(remote, iaf_net);
149 if (addr != NULL) {
150 /* Destination is directly accessible */
151 local->ipv4 = addr->naddr.ipv4;
152 return EOK;
153 }
154
155 return ENOENT;
156}
157
158static void inet_get_srcaddr_srv(inet_client_t *client, ipc_callid_t callid,
159 ipc_call_t *call)
160{
161 inet_addr_t remote;
162 uint8_t tos;
163 inet_addr_t local;
164 int rc;
165
166 log_msg(LVL_DEBUG, "inet_get_srcaddr_srv()");
167
168 remote.ipv4 = IPC_GET_ARG1(*call);
169 tos = IPC_GET_ARG2(*call);
170 local.ipv4 = 0;
171
172 rc = inet_get_srcaddr(&remote, tos, &local);
173 async_answer_1(callid, rc, local.ipv4);
174}
175
176static void inet_send_srv(inet_client_t *client, ipc_callid_t callid,
177 ipc_call_t *call)
178{
179 inet_dgram_t dgram;
180 uint8_t ttl;
181 int df;
182 int rc;
183
184 log_msg(LVL_DEBUG, "inet_send_srv()");
185
186 dgram.src.ipv4 = IPC_GET_ARG1(*call);
187 dgram.dest.ipv4 = IPC_GET_ARG2(*call);
188 dgram.tos = IPC_GET_ARG3(*call);
189 ttl = IPC_GET_ARG4(*call);
190 df = IPC_GET_ARG5(*call);
191
192 rc = async_data_write_accept(&dgram.data, false, 0, 0, 0, &dgram.size);
193 if (rc != EOK) {
194 async_answer_0(callid, rc);
195 return;
196 }
197
198 rc = inet_send(client, &dgram, client->protocol, ttl, df);
199
200 free(dgram.data);
201 async_answer_0(callid, rc);
202}
203
204static void inet_set_proto_srv(inet_client_t *client, ipc_callid_t callid,
205 ipc_call_t *call)
206{
207 sysarg_t proto;
208
209 proto = IPC_GET_ARG1(*call);
210 log_msg(LVL_DEBUG, "inet_set_proto_srv(%lu)", (unsigned long) proto);
211
212 if (proto > UINT8_MAX) {
213 async_answer_0(callid, EINVAL);
214 return;
215 }
216
217 client->protocol = proto;
218 async_answer_0(callid, EOK);
219}
220
221static void inet_client_init(inet_client_t *client)
222{
223 client->sess = NULL;
224
225 fibril_mutex_lock(&client_list_lock);
226 list_append(&client->client_list, &client_list);
227 fibril_mutex_unlock(&client_list_lock);
228}
229
230static void inet_client_fini(inet_client_t *client)
231{
232 async_hangup(client->sess);
233 client->sess = NULL;
234
235 fibril_mutex_lock(&client_list_lock);
236 list_remove(&client->client_list);
237 fibril_mutex_unlock(&client_list_lock);
238}
239
240static void inet_default_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
241{
242 inet_client_t client;
243
244 log_msg(LVL_DEBUG, "inet_default_conn()");
245
246 /* Accept the connection */
247 async_answer_0(iid, EOK);
248
249 inet_client_init(&client);
250
251 while (true) {
252 ipc_call_t call;
253 ipc_callid_t callid = async_get_call(&call);
254 sysarg_t method = IPC_GET_IMETHOD(call);
255
256 if (!method) {
257 /* The other side has hung up */
258 async_answer_0(callid, EOK);
259 return;
260 }
261
262 switch (method) {
263 case INET_CALLBACK_CREATE:
264 inet_callback_create_srv(&client, callid, &call);
265 break;
266 case INET_GET_SRCADDR:
267 inet_get_srcaddr_srv(&client, callid, &call);
268 break;
269 case INET_SEND:
270 inet_send_srv(&client, callid, &call);
271 break;
272 case INET_SET_PROTO:
273 inet_set_proto_srv(&client, callid, &call);
274 break;
275 default:
276 async_answer_0(callid, EINVAL);
277 }
278 }
279
280 inet_client_fini(&client);
281}
282
283static void inet_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
284{
285 sysarg_t port;
286
287 port = IPC_GET_ARG1(*icall);
288
289 switch (port) {
290 case INET_PORT_DEFAULT:
291 inet_default_conn(iid, icall, arg);
292 break;
293 case INET_PORT_CFG:
294 inet_cfg_conn(iid, icall, arg);
295 break;
296 case INET_PORT_PING:
297 inetping_conn(iid, icall, arg);
298 break;
299 default:
300 async_answer_0(iid, ENOTSUP);
301 break;
302 }
303}
304
305static inet_client_t *inet_client_find(uint8_t proto)
306{
307 fibril_mutex_lock(&client_list_lock);
308
309 list_foreach(client_list, link) {
310 inet_client_t *client = list_get_instance(link, inet_client_t,
311 client_list);
312
313 if (client->protocol == proto) {
314 fibril_mutex_unlock(&client_list_lock);
315 return client;
316 }
317 }
318
319 fibril_mutex_unlock(&client_list_lock);
320 return NULL;
321}
322
323int inet_ev_recv(inet_client_t *client, inet_dgram_t *dgram)
324{
325 async_exch_t *exch = async_exchange_begin(client->sess);
326
327 ipc_call_t answer;
328 aid_t req = async_send_3(exch, INET_EV_RECV, dgram->src.ipv4,
329 dgram->dest.ipv4, dgram->tos, &answer);
330 int rc = async_data_write_start(exch, dgram->data, dgram->size);
331 async_exchange_end(exch);
332
333 if (rc != EOK) {
334 async_wait_for(req, NULL);
335 return rc;
336 }
337
338 sysarg_t retval;
339 async_wait_for(req, &retval);
340 if (retval != EOK)
341 return retval;
342
343 return EOK;
344}
345
346static int inet_recv_dgram_local(inet_dgram_t *dgram, uint8_t proto)
347{
348 inet_client_t *client;
349
350 log_msg(LVL_DEBUG, "inet_recv_dgram_local()");
351
352 /* ICMP messages are handled internally */
353 if (proto == IP_PROTO_ICMP)
354 return icmp_recv(dgram);
355
356 client = inet_client_find(proto);
357 if (client == NULL) {
358 log_msg(LVL_DEBUG, "No client found for protocol 0x%" PRIx8,
359 proto);
360 return ENOENT;
361 }
362
363 return inet_ev_recv(client, dgram);
364}
365
366int inet_recv_packet(inet_packet_t *packet)
367{
368 inet_addrobj_t *addr;
369 inet_dgram_t dgram;
370
371 addr = inet_addrobj_find(&packet->dest, iaf_addr);
372 if (addr != NULL) {
373 /* Destined for one of the local addresses */
374
375 /* XXX Reassemble packets */
376 dgram.src = packet->src;
377 dgram.dest = packet->dest;
378 dgram.tos = packet->tos;
379 dgram.data = packet->data;
380 dgram.size = packet->size;
381
382 return inet_recv_dgram_local(&dgram, packet->proto);
383 }
384
385 return ENOENT;
386}
387
388int main(int argc, char *argv[])
389{
390 int rc;
391
392 printf(NAME ": HelenOS Internet Protocol service\n");
393
394 if (log_init(NAME, LVL_DEBUG) != EOK) {
395 printf(NAME ": Failed to initialize logging.\n");
396 return 1;
397 }
398
399 rc = inet_init();
400 if (rc != EOK)
401 return 1;
402
403 printf(NAME ": Accepting connections.\n");
404 task_retval(0);
405 async_manager();
406
407 /* Not reached */
408 return 0;
409}
410
411/** @}
412 */
Note: See TracBrowser for help on using the repository browser.