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

Last change on this file was ca48672, checked in by Jiri Svoboda <jiri@…>, 8 days ago

loc_service_register() needs to take a port ID argument.

  • Property mode set to 100644
File size: 10.6 KB
RevLine 
[2093fbe]1/*
[ca48672]2 * Copyright (c) 2025 Jiri Svoboda
[2093fbe]3 * Copyright (c) 2013 Jakub Jermar
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup slip
31 * @{
32 */
33/**
34 * @file
35 * @brief IP over serial line IP link provider.
36 */
37
38#include <stdio.h>
[7b64cf0]39#include <stdint.h>
[c1694b6b]40#include <stdlib.h>
[2093fbe]41#include <loc.h>
[02a09ed]42#include <inet/addr.h>
[b4edc96]43#include <inet/eth_addr.h>
[2093fbe]44#include <inet/iplink_srv.h>
[74017ce]45#include <io/chardev.h>
[2093fbe]46#include <io/log.h>
47#include <errno.h>
[c1694b6b]48#include <str_error.h>
[1c635d6]49#include <task.h>
[2093fbe]50
51#define NAME "slip"
52#define CAT_IPLINK "iplink"
53
54#define SLIP_MTU 1006 /* as per RFC 1055 */
55
[7b64cf0]56#define SLIP_END 0300
57#define SLIP_ESC 0333
[02a09ed]58#define SLIP_ESC_END 0334
[7b64cf0]59#define SLIP_ESC_ESC 0335
60
[b7fd2a0]61static errno_t slip_open(iplink_srv_t *);
62static errno_t slip_close(iplink_srv_t *);
63static errno_t slip_send(iplink_srv_t *, iplink_sdu_t *);
64static errno_t slip_send6(iplink_srv_t *, iplink_sdu6_t *);
65static errno_t slip_get_mtu(iplink_srv_t *, size_t *);
[b4edc96]66static errno_t slip_get_mac48(iplink_srv_t *, eth_addr_t *);
[b7fd2a0]67static errno_t slip_addr_add(iplink_srv_t *, inet_addr_t *);
68static errno_t slip_addr_remove(iplink_srv_t *, inet_addr_t *);
[2093fbe]69
70static iplink_srv_t slip_iplink;
71
72static iplink_ops_t slip_iplink_ops = {
73 .open = slip_open,
74 .close = slip_close,
75 .send = slip_send,
[a17356fd]76 .send6 = slip_send6,
[2093fbe]77 .get_mtu = slip_get_mtu,
[a17356fd]78 .get_mac48 = slip_get_mac48,
[2093fbe]79 .addr_add = slip_addr_add,
80 .addr_remove = slip_addr_remove
81};
82
[7b64cf0]83static uint8_t slip_send_buf[SLIP_MTU + 2];
84static size_t slip_send_pending;
85
86static uint8_t slip_recv_buf[SLIP_MTU + 2];
87static size_t slip_recv_pending;
88static size_t slip_recv_read;
89
[b7fd2a0]90errno_t slip_open(iplink_srv_t *srv)
[2093fbe]91{
92 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_open()");
93 return EOK;
94}
95
[b7fd2a0]96errno_t slip_close(iplink_srv_t *srv)
[2093fbe]97{
98 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_close()");
99 return EOK;
100}
101
[74017ce]102static void write_flush(chardev_t *chardev)
[7b64cf0]103{
104 size_t written = 0;
105
106 while (slip_send_pending > 0) {
[b7fd2a0]107 errno_t rc;
[74017ce]108 size_t nwr;
[7b64cf0]109
[74017ce]110 rc = chardev_write(chardev, &slip_send_buf[written],
111 slip_send_pending, &nwr);
112 if (rc != EOK) {
[7b64cf0]113 log_msg(LOG_DEFAULT, LVL_ERROR,
[c1694b6b]114 "chardev_write() -> %s", str_error_name(rc));
[7b64cf0]115 slip_send_pending = 0;
116 break;
117 }
[74017ce]118 written += nwr;
119 slip_send_pending -= nwr;
[7b64cf0]120 }
121}
122
[74017ce]123static void write_buffered(chardev_t *chardev, uint8_t ch)
[7b64cf0]124{
125 if (slip_send_pending == sizeof(slip_send_buf))
[74017ce]126 write_flush(chardev);
[7b64cf0]127 slip_send_buf[slip_send_pending++] = ch;
128}
129
[b7fd2a0]130errno_t slip_send(iplink_srv_t *srv, iplink_sdu_t *sdu)
[2093fbe]131{
[a17356fd]132 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_send()");
[a35b458]133
[74017ce]134 chardev_t *chardev = (chardev_t *) srv->arg;
[7b64cf0]135 uint8_t *data = sdu->data;
[a35b458]136
[7b64cf0]137 /*
[a17356fd]138 * Strictly speaking, this is not prescribed by the RFC, but the RFC
139 * suggests to start with sending a SLIP_END byte as a synchronization
140 * measure for dealing with previous possible noise on the line.
141 */
[74017ce]142 write_buffered(chardev, SLIP_END);
[a35b458]143
[a17356fd]144 for (size_t i = 0; i < sdu->size; i++) {
[7b64cf0]145 switch (data[i]) {
146 case SLIP_END:
[74017ce]147 write_buffered(chardev, SLIP_ESC);
148 write_buffered(chardev, SLIP_ESC_END);
[7b64cf0]149 break;
150 case SLIP_ESC:
[74017ce]151 write_buffered(chardev, SLIP_ESC);
152 write_buffered(chardev, SLIP_ESC_ESC);
[7b64cf0]153 break;
154 default:
[74017ce]155 write_buffered(chardev, data[i]);
[7b64cf0]156 break;
157 }
158 }
[a35b458]159
[74017ce]160 write_buffered(chardev, SLIP_END);
161 write_flush(chardev);
[a35b458]162
[7b64cf0]163 return EOK;
[2093fbe]164}
165
[b7fd2a0]166errno_t slip_send6(iplink_srv_t *srv, iplink_sdu6_t *sdu)
[a17356fd]167{
168 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_send6()");
[a35b458]169
[a17356fd]170 return ENOTSUP;
171}
172
[b7fd2a0]173errno_t slip_get_mtu(iplink_srv_t *srv, size_t *mtu)
[2093fbe]174{
175 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_get_mtu()");
176 *mtu = SLIP_MTU;
177 return EOK;
178}
179
[b4edc96]180errno_t slip_get_mac48(iplink_srv_t *src, eth_addr_t *mac)
[a17356fd]181{
182 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_get_mac48()");
183 return ENOTSUP;
184}
185
[b7fd2a0]186errno_t slip_addr_add(iplink_srv_t *srv, inet_addr_t *addr)
[2093fbe]187{
188 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_addr_add()");
189 return EOK;
190}
191
[b7fd2a0]192errno_t slip_addr_remove(iplink_srv_t *srv, inet_addr_t *addr)
[2093fbe]193{
194 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_addr_remove()");
195 return EOK;
196}
197
198static void usage(void)
199{
200 printf("Usage: " NAME " <service-name> <link-name>\n");
201}
202
[984a9ba]203static void slip_client_conn(ipc_call_t *icall, void *arg)
[2093fbe]204{
205 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_client_conn()");
[984a9ba]206 iplink_conn(icall, &slip_iplink);
[2093fbe]207}
208
[74017ce]209static uint8_t read_buffered(chardev_t *chardev)
[7b64cf0]210{
211 while (slip_recv_pending == 0) {
[b7fd2a0]212 errno_t rc;
[74017ce]213 size_t nread;
[7b64cf0]214
[74017ce]215 rc = chardev_read(chardev, slip_recv_buf,
[f2d88f3]216 sizeof(slip_recv_buf), &nread, chardev_f_none);
[74017ce]217 if (rc != EOK) {
[7b64cf0]218 log_msg(LOG_DEFAULT, LVL_ERROR,
[c1694b6b]219 "char_dev_read() -> %s", str_error_name(rc));
[7b64cf0]220 }
[74017ce]221
222 if (nread == 0)
223 return SLIP_END;
224
225 slip_recv_pending = nread;
[7b64cf0]226 slip_recv_read = 0;
227 }
228 slip_recv_pending--;
229 return slip_recv_buf[slip_recv_read++];
230}
231
[b7fd2a0]232static errno_t slip_recv_fibril(void *arg)
[7b64cf0]233{
[74017ce]234 chardev_t *chardev = (chardev_t *) arg;
[7b64cf0]235 static uint8_t recv_final[SLIP_MTU];
[02a09ed]236 iplink_recv_sdu_t sdu;
[7b64cf0]237 uint8_t ch;
[b7fd2a0]238 errno_t rc;
[7b64cf0]239
240 sdu.data = recv_final;
241
242 while (true) {
[84239b1]243 sdu.size = 0;
244 while (sdu.size < sizeof(recv_final)) {
[74017ce]245 ch = read_buffered(chardev);
[7b64cf0]246 switch (ch) {
247 case SLIP_END:
248 if (sdu.size == 0) {
249 /*
[02a09ed]250 * Discard the empty SLIP datagram.
251 */
[7b64cf0]252 break;
253 }
254 goto pass;
255
256 case SLIP_ESC:
[74017ce]257 ch = read_buffered(chardev);
[7b64cf0]258 if (ch == SLIP_ESC_END) {
259 recv_final[sdu.size++] = SLIP_END;
260 break;
[74017ce]261 } else if (ch == SLIP_ESC_ESC) {
[7b64cf0]262 recv_final[sdu.size++] = SLIP_ESC;
263 break;
264 }
265
266 /*
[e141281]267 * The RFC suggests to simply insert the wrongly
268 * escaped character into the packet so we fall
269 * through.
270 */
[dc12262]271 /* Fallthrough */
[e141281]272
[7b64cf0]273 default:
274 recv_final[sdu.size++] = ch;
275 break;
276 }
[a35b458]277
[7b64cf0]278 }
279
280 /*
[ae7d03c]281 * We have reached the limit of our MTU. Regardless of whether
282 * the datagram is properly ended with SLIP_END, pass it along.
283 * If the next character is really SLIP_END, nothing
284 * catastrophic happens. The algorithm will just see an
285 * artificially empty SLIP datagram and life will go on.
286 */
[7b64cf0]287
[3bacee1]288 pass:
[417a2ba1]289 rc = iplink_ev_recv(&slip_iplink, &sdu, ip_v4);
[7b64cf0]290 if (rc != EOK) {
291 log_msg(LOG_DEFAULT, LVL_ERROR,
[c1694b6b]292 "iplink_ev_recv() -> %s", str_error_name(rc));
[7b64cf0]293 }
294 }
295
296 return 0;
297}
298
[b7fd2a0]299static errno_t slip_init(const char *svcstr, const char *linkstr)
[2093fbe]300{
301 service_id_t svcid;
302 service_id_t linksid;
303 category_id_t iplinkcid;
[3abf0760]304 async_sess_t *sess_in = NULL;
305 async_sess_t *sess_out = NULL;
[74017ce]306 chardev_t *chardev_in = NULL;
307 chardev_t *chardev_out = NULL;
[7b64cf0]308 fid_t fid;
[4c6fd56]309 loc_srv_t *srv;
[b7fd2a0]310 errno_t rc;
[2093fbe]311
312 iplink_srv_init(&slip_iplink);
313 slip_iplink.ops = &slip_iplink_ops;
314
[b688fd8]315 async_set_fallback_port_handler(slip_client_conn, NULL);
[2093fbe]316
[4c6fd56]317 rc = loc_server_register(NAME, &srv);
[2093fbe]318 if (rc != EOK) {
319 log_msg(LOG_DEFAULT, LVL_ERROR,
320 "Failed registering server.");
321 return rc;
322 }
323
324 rc = loc_service_get_id(svcstr, &svcid, 0);
325 if (rc != EOK) {
[4c6fd56]326 loc_server_unregister(srv);
[2093fbe]327 log_msg(LOG_DEFAULT, LVL_ERROR,
328 "Failed getting ID for service %s", svcstr);
329 return rc;
330 }
331
332 rc = loc_category_get_id(CAT_IPLINK, &iplinkcid, 0);
333 if (rc != EOK) {
[4c6fd56]334 loc_server_unregister(srv);
[2093fbe]335 log_msg(LOG_DEFAULT, LVL_ERROR,
336 "Failed to get category ID for %s",
337 CAT_IPLINK);
338 return rc;
339 }
340
[7b64cf0]341 /*
[3abf0760]342 * Create two sessions to allow to both read and write from the
343 * char_dev at the same time.
[7b64cf0]344 */
[f9b2cb4c]345 sess_out = loc_service_connect(svcid, INTERFACE_DDF, 0);
[3abf0760]346 if (!sess_out) {
[4c6fd56]347 loc_server_unregister(srv);
[2093fbe]348 log_msg(LOG_DEFAULT, LVL_ERROR,
349 "Failed to connect to service %s (ID=%d)",
350 svcstr, (int) svcid);
[b11f6fb]351 return ENOENT;
[2093fbe]352 }
[74017ce]353
354 rc = chardev_open(sess_out, &chardev_out);
355 if (rc != EOK) {
[4c6fd56]356 loc_server_unregister(srv);
[74017ce]357 log_msg(LOG_DEFAULT, LVL_ERROR,
358 "Failed opening character device.");
359 return ENOENT;
360 }
361
362 slip_iplink.arg = chardev_out;
[3abf0760]363
[f9b2cb4c]364 sess_in = loc_service_connect(svcid, INTERFACE_DDF, 0);
[3abf0760]365 if (!sess_in) {
366 log_msg(LOG_DEFAULT, LVL_ERROR,
367 "Failed to connect to service %s (ID=%d)",
368 svcstr, (int) svcid);
[b11f6fb]369 rc = ENOENT;
[3abf0760]370 goto fail;
371 }
[2093fbe]372
[74017ce]373 rc = chardev_open(sess_in, &chardev_in);
374 if (rc != EOK) {
375 log_msg(LOG_DEFAULT, LVL_ERROR,
376 "Failed opening character device.");
377 return ENOENT;
378 }
379
[ca48672]380 rc = loc_service_register(srv, linkstr, fallback_port_id, &linksid);
[2093fbe]381 if (rc != EOK) {
382 log_msg(LOG_DEFAULT, LVL_ERROR,
[3bacee1]383 "Failed to register service %s",
384 linkstr);
[2093fbe]385 goto fail;
386 }
387
[4c6fd56]388 rc = loc_service_add_to_cat(srv, linksid, iplinkcid);
[2093fbe]389 if (rc != EOK) {
[4c6fd56]390 loc_service_unregister(srv, linksid);
[2093fbe]391 log_msg(LOG_DEFAULT, LVL_ERROR,
[7b64cf0]392 "Failed to add service %d (%s) to category %d (%s).",
[2093fbe]393 (int) linksid, linkstr, (int) iplinkcid, CAT_IPLINK);
394 goto fail;
395 }
396
[74017ce]397 fid = fibril_create(slip_recv_fibril, chardev_in);
[7b64cf0]398 if (!fid) {
399 log_msg(LOG_DEFAULT, LVL_ERROR,
400 "Failed to create receive fibril.");
[b11f6fb]401 rc = ENOENT;
[7b64cf0]402 goto fail;
403 }
404 fibril_add_ready(fid);
405
[2093fbe]406 return EOK;
407
408fail:
[4c6fd56]409 loc_server_unregister(srv);
[74017ce]410 chardev_close(chardev_out);
[3abf0760]411 if (sess_out)
412 async_hangup(sess_out);
[74017ce]413 chardev_close(chardev_in);
[3abf0760]414 if (sess_in)
415 async_hangup(sess_in);
[2093fbe]416
417 /*
[ae7d03c]418 * We assume that our registration at the location service will be
419 * cleaned up automatically as the service (i.e. this task) terminates.
420 */
[2093fbe]421
422 return rc;
423}
424
425int main(int argc, char *argv[])
426{
[b7fd2a0]427 errno_t rc;
[2093fbe]428
429 printf(NAME ": IP over serial line service\n");
430
431 if (argc != 3) {
432 usage();
433 return EINVAL;
434 }
435
436 rc = log_init(NAME);
437 if (rc != EOK) {
438 printf(NAME ": failed to initialize log\n");
439 return rc;
440 }
441
442 rc = slip_init(argv[1], argv[2]);
443 if (rc != EOK)
444 return 1;
445
446 printf(NAME ": Accepting connections.\n");
447 task_retval(0);
448 async_manager();
449
450 /* not reached */
451 return 0;
452}
453
454/** @}
455 */
Note: See TracBrowser for help on using the repository browser.