source: mainline/uspace/srv/net/slip/slip.c@ c8ea6eca

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

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • Property mode set to 100644
File size: 10.3 KB
Line 
1/*
2 * Copyright (c) 2013 Jakub Jermar
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 slip
30 * @{
31 */
32/**
33 * @file
34 * @brief IP over serial line IP link provider.
35 */
36
37#include <stdio.h>
38#include <stdint.h>
39#include <stdlib.h>
40#include <loc.h>
41#include <inet/addr.h>
42#include <inet/iplink_srv.h>
43#include <io/chardev.h>
44#include <io/log.h>
45#include <errno.h>
46#include <str_error.h>
47#include <task.h>
48
49#define NAME "slip"
50#define CAT_IPLINK "iplink"
51
52#define SLIP_MTU 1006 /* as per RFC 1055 */
53
54#define SLIP_END 0300
55#define SLIP_ESC 0333
56#define SLIP_ESC_END 0334
57#define SLIP_ESC_ESC 0335
58
59static errno_t slip_open(iplink_srv_t *);
60static errno_t slip_close(iplink_srv_t *);
61static errno_t slip_send(iplink_srv_t *, iplink_sdu_t *);
62static errno_t slip_send6(iplink_srv_t *, iplink_sdu6_t *);
63static errno_t slip_get_mtu(iplink_srv_t *, size_t *);
64static errno_t slip_get_mac48(iplink_srv_t *, addr48_t *);
65static errno_t slip_addr_add(iplink_srv_t *, inet_addr_t *);
66static errno_t slip_addr_remove(iplink_srv_t *, inet_addr_t *);
67
68static iplink_srv_t slip_iplink;
69
70static iplink_ops_t slip_iplink_ops = {
71 .open = slip_open,
72 .close = slip_close,
73 .send = slip_send,
74 .send6 = slip_send6,
75 .get_mtu = slip_get_mtu,
76 .get_mac48 = slip_get_mac48,
77 .addr_add = slip_addr_add,
78 .addr_remove = slip_addr_remove
79};
80
81static uint8_t slip_send_buf[SLIP_MTU + 2];
82static size_t slip_send_pending;
83
84static uint8_t slip_recv_buf[SLIP_MTU + 2];
85static size_t slip_recv_pending;
86static size_t slip_recv_read;
87
88errno_t slip_open(iplink_srv_t *srv)
89{
90 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_open()");
91 return EOK;
92}
93
94errno_t slip_close(iplink_srv_t *srv)
95{
96 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_close()");
97 return EOK;
98}
99
100static void write_flush(chardev_t *chardev)
101{
102 size_t written = 0;
103
104 while (slip_send_pending > 0) {
105 errno_t rc;
106 size_t nwr;
107
108 rc = chardev_write(chardev, &slip_send_buf[written],
109 slip_send_pending, &nwr);
110 if (rc != EOK) {
111 log_msg(LOG_DEFAULT, LVL_ERROR,
112 "chardev_write() -> %s", str_error_name(rc));
113 slip_send_pending = 0;
114 break;
115 }
116 written += nwr;
117 slip_send_pending -= nwr;
118 }
119}
120
121static void write_buffered(chardev_t *chardev, uint8_t ch)
122{
123 if (slip_send_pending == sizeof(slip_send_buf))
124 write_flush(chardev);
125 slip_send_buf[slip_send_pending++] = ch;
126}
127
128errno_t slip_send(iplink_srv_t *srv, iplink_sdu_t *sdu)
129{
130 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_send()");
131
132 chardev_t *chardev = (chardev_t *) srv->arg;
133 uint8_t *data = sdu->data;
134
135 /*
136 * Strictly speaking, this is not prescribed by the RFC, but the RFC
137 * suggests to start with sending a SLIP_END byte as a synchronization
138 * measure for dealing with previous possible noise on the line.
139 */
140 write_buffered(chardev, SLIP_END);
141
142 for (size_t i = 0; i < sdu->size; i++) {
143 switch (data[i]) {
144 case SLIP_END:
145 write_buffered(chardev, SLIP_ESC);
146 write_buffered(chardev, SLIP_ESC_END);
147 break;
148 case SLIP_ESC:
149 write_buffered(chardev, SLIP_ESC);
150 write_buffered(chardev, SLIP_ESC_ESC);
151 break;
152 default:
153 write_buffered(chardev, data[i]);
154 break;
155 }
156 }
157
158 write_buffered(chardev, SLIP_END);
159 write_flush(chardev);
160
161 return EOK;
162}
163
164errno_t slip_send6(iplink_srv_t *srv, iplink_sdu6_t *sdu)
165{
166 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_send6()");
167
168 return ENOTSUP;
169}
170
171errno_t slip_get_mtu(iplink_srv_t *srv, size_t *mtu)
172{
173 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_get_mtu()");
174 *mtu = SLIP_MTU;
175 return EOK;
176}
177
178errno_t slip_get_mac48(iplink_srv_t *src, addr48_t *mac)
179{
180 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_get_mac48()");
181 return ENOTSUP;
182}
183
184errno_t slip_addr_add(iplink_srv_t *srv, inet_addr_t *addr)
185{
186 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_addr_add()");
187 return EOK;
188}
189
190errno_t slip_addr_remove(iplink_srv_t *srv, inet_addr_t *addr)
191{
192 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_addr_remove()");
193 return EOK;
194}
195
196static void usage(void)
197{
198 printf("Usage: " NAME " <service-name> <link-name>\n");
199}
200
201static void slip_client_conn(ipc_call_t *icall, void *arg)
202{
203 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_client_conn()");
204 iplink_conn(icall, &slip_iplink);
205}
206
207static uint8_t read_buffered(chardev_t *chardev)
208{
209 while (slip_recv_pending == 0) {
210 errno_t rc;
211 size_t nread;
212
213 rc = chardev_read(chardev, slip_recv_buf,
214 sizeof(slip_recv_buf), &nread);
215 if (rc != EOK) {
216 log_msg(LOG_DEFAULT, LVL_ERROR,
217 "char_dev_read() -> %s", str_error_name(rc));
218 }
219
220 if (nread == 0)
221 return SLIP_END;
222
223 slip_recv_pending = nread;
224 slip_recv_read = 0;
225 }
226 slip_recv_pending--;
227 return slip_recv_buf[slip_recv_read++];
228}
229
230static errno_t slip_recv_fibril(void *arg)
231{
232 chardev_t *chardev = (chardev_t *) arg;
233 static uint8_t recv_final[SLIP_MTU];
234 iplink_recv_sdu_t sdu;
235 uint8_t ch;
236 errno_t rc;
237
238 sdu.data = recv_final;
239
240 while (true) {
241 sdu.size = 0;
242 while (sdu.size < sizeof(recv_final)) {
243 ch = read_buffered(chardev);
244 switch (ch) {
245 case SLIP_END:
246 if (sdu.size == 0) {
247 /*
248 * Discard the empty SLIP datagram.
249 */
250 break;
251 }
252 goto pass;
253
254 case SLIP_ESC:
255 ch = read_buffered(chardev);
256 if (ch == SLIP_ESC_END) {
257 recv_final[sdu.size++] = SLIP_END;
258 break;
259 } else if (ch == SLIP_ESC_ESC) {
260 recv_final[sdu.size++] = SLIP_ESC;
261 break;
262 }
263
264 /*
265 * The RFC suggests to simply insert the wrongly
266 * escaped character into the packet so we fall
267 * through.
268 */
269 /* Fallthrough */
270
271 default:
272 recv_final[sdu.size++] = ch;
273 break;
274 }
275
276 }
277
278 /*
279 * We have reached the limit of our MTU. Regardless of whether
280 * the datagram is properly ended with SLIP_END, pass it along.
281 * If the next character is really SLIP_END, nothing
282 * catastrophic happens. The algorithm will just see an
283 * artificially empty SLIP datagram and life will go on.
284 */
285
286 pass:
287 rc = iplink_ev_recv(&slip_iplink, &sdu, ip_v4);
288 if (rc != EOK) {
289 log_msg(LOG_DEFAULT, LVL_ERROR,
290 "iplink_ev_recv() -> %s", str_error_name(rc));
291 }
292 }
293
294 return 0;
295}
296
297static errno_t slip_init(const char *svcstr, const char *linkstr)
298{
299 service_id_t svcid;
300 service_id_t linksid;
301 category_id_t iplinkcid;
302 async_sess_t *sess_in = NULL;
303 async_sess_t *sess_out = NULL;
304 chardev_t *chardev_in = NULL;
305 chardev_t *chardev_out = NULL;
306 fid_t fid;
307 errno_t rc;
308
309 iplink_srv_init(&slip_iplink);
310 slip_iplink.ops = &slip_iplink_ops;
311
312 async_set_fallback_port_handler(slip_client_conn, NULL);
313
314 rc = loc_server_register(NAME);
315 if (rc != EOK) {
316 log_msg(LOG_DEFAULT, LVL_ERROR,
317 "Failed registering server.");
318 return rc;
319 }
320
321 rc = loc_service_get_id(svcstr, &svcid, 0);
322 if (rc != EOK) {
323 log_msg(LOG_DEFAULT, LVL_ERROR,
324 "Failed getting ID for service %s", svcstr);
325 return rc;
326 }
327
328 rc = loc_category_get_id(CAT_IPLINK, &iplinkcid, 0);
329 if (rc != EOK) {
330 log_msg(LOG_DEFAULT, LVL_ERROR,
331 "Failed to get category ID for %s",
332 CAT_IPLINK);
333 return rc;
334 }
335
336 /*
337 * Create two sessions to allow to both read and write from the
338 * char_dev at the same time.
339 */
340 sess_out = loc_service_connect(svcid, INTERFACE_DDF, 0);
341 if (!sess_out) {
342 log_msg(LOG_DEFAULT, LVL_ERROR,
343 "Failed to connect to service %s (ID=%d)",
344 svcstr, (int) svcid);
345 return ENOENT;
346 }
347
348 rc = chardev_open(sess_out, &chardev_out);
349 if (rc != EOK) {
350 log_msg(LOG_DEFAULT, LVL_ERROR,
351 "Failed opening character device.");
352 return ENOENT;
353 }
354
355 slip_iplink.arg = chardev_out;
356
357 sess_in = loc_service_connect(svcid, INTERFACE_DDF, 0);
358 if (!sess_in) {
359 log_msg(LOG_DEFAULT, LVL_ERROR,
360 "Failed to connect to service %s (ID=%d)",
361 svcstr, (int) svcid);
362 rc = ENOENT;
363 goto fail;
364 }
365
366 rc = chardev_open(sess_in, &chardev_in);
367 if (rc != EOK) {
368 log_msg(LOG_DEFAULT, LVL_ERROR,
369 "Failed opening character device.");
370 return ENOENT;
371 }
372
373 rc = loc_service_register(linkstr, &linksid);
374 if (rc != EOK) {
375 log_msg(LOG_DEFAULT, LVL_ERROR,
376 "Failed to register service %s",
377 linkstr);
378 goto fail;
379 }
380
381 rc = loc_service_add_to_cat(linksid, iplinkcid);
382 if (rc != EOK) {
383 log_msg(LOG_DEFAULT, LVL_ERROR,
384 "Failed to add service %d (%s) to category %d (%s).",
385 (int) linksid, linkstr, (int) iplinkcid, CAT_IPLINK);
386 goto fail;
387 }
388
389 fid = fibril_create(slip_recv_fibril, chardev_in);
390 if (!fid) {
391 log_msg(LOG_DEFAULT, LVL_ERROR,
392 "Failed to create receive fibril.");
393 rc = ENOENT;
394 goto fail;
395 }
396 fibril_add_ready(fid);
397
398 return EOK;
399
400fail:
401 chardev_close(chardev_out);
402 if (sess_out)
403 async_hangup(sess_out);
404 chardev_close(chardev_in);
405 if (sess_in)
406 async_hangup(sess_in);
407
408 /*
409 * We assume that our registration at the location service will be
410 * cleaned up automatically as the service (i.e. this task) terminates.
411 */
412
413 return rc;
414}
415
416int main(int argc, char *argv[])
417{
418 errno_t rc;
419
420 printf(NAME ": IP over serial line service\n");
421
422 if (argc != 3) {
423 usage();
424 return EINVAL;
425 }
426
427 rc = log_init(NAME);
428 if (rc != EOK) {
429 printf(NAME ": failed to initialize log\n");
430 return rc;
431 }
432
433 rc = slip_init(argv[1], argv[2]);
434 if (rc != EOK)
435 return 1;
436
437 printf(NAME ": Accepting connections.\n");
438 task_retval(0);
439 async_manager();
440
441 /* not reached */
442 return 0;
443}
444
445/** @}
446 */
Note: See TracBrowser for help on using the repository browser.