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
Line 
1/*
2 * Copyright (c) 2025 Jiri Svoboda
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>
39#include <stdint.h>
40#include <stdlib.h>
41#include <loc.h>
42#include <inet/addr.h>
43#include <inet/eth_addr.h>
44#include <inet/iplink_srv.h>
45#include <io/chardev.h>
46#include <io/log.h>
47#include <errno.h>
48#include <str_error.h>
49#include <task.h>
50
51#define NAME "slip"
52#define CAT_IPLINK "iplink"
53
54#define SLIP_MTU 1006 /* as per RFC 1055 */
55
56#define SLIP_END 0300
57#define SLIP_ESC 0333
58#define SLIP_ESC_END 0334
59#define SLIP_ESC_ESC 0335
60
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 *);
66static errno_t slip_get_mac48(iplink_srv_t *, eth_addr_t *);
67static errno_t slip_addr_add(iplink_srv_t *, inet_addr_t *);
68static errno_t slip_addr_remove(iplink_srv_t *, inet_addr_t *);
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,
76 .send6 = slip_send6,
77 .get_mtu = slip_get_mtu,
78 .get_mac48 = slip_get_mac48,
79 .addr_add = slip_addr_add,
80 .addr_remove = slip_addr_remove
81};
82
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
90errno_t slip_open(iplink_srv_t *srv)
91{
92 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_open()");
93 return EOK;
94}
95
96errno_t slip_close(iplink_srv_t *srv)
97{
98 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_close()");
99 return EOK;
100}
101
102static void write_flush(chardev_t *chardev)
103{
104 size_t written = 0;
105
106 while (slip_send_pending > 0) {
107 errno_t rc;
108 size_t nwr;
109
110 rc = chardev_write(chardev, &slip_send_buf[written],
111 slip_send_pending, &nwr);
112 if (rc != EOK) {
113 log_msg(LOG_DEFAULT, LVL_ERROR,
114 "chardev_write() -> %s", str_error_name(rc));
115 slip_send_pending = 0;
116 break;
117 }
118 written += nwr;
119 slip_send_pending -= nwr;
120 }
121}
122
123static void write_buffered(chardev_t *chardev, uint8_t ch)
124{
125 if (slip_send_pending == sizeof(slip_send_buf))
126 write_flush(chardev);
127 slip_send_buf[slip_send_pending++] = ch;
128}
129
130errno_t slip_send(iplink_srv_t *srv, iplink_sdu_t *sdu)
131{
132 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_send()");
133
134 chardev_t *chardev = (chardev_t *) srv->arg;
135 uint8_t *data = sdu->data;
136
137 /*
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 */
142 write_buffered(chardev, SLIP_END);
143
144 for (size_t i = 0; i < sdu->size; i++) {
145 switch (data[i]) {
146 case SLIP_END:
147 write_buffered(chardev, SLIP_ESC);
148 write_buffered(chardev, SLIP_ESC_END);
149 break;
150 case SLIP_ESC:
151 write_buffered(chardev, SLIP_ESC);
152 write_buffered(chardev, SLIP_ESC_ESC);
153 break;
154 default:
155 write_buffered(chardev, data[i]);
156 break;
157 }
158 }
159
160 write_buffered(chardev, SLIP_END);
161 write_flush(chardev);
162
163 return EOK;
164}
165
166errno_t slip_send6(iplink_srv_t *srv, iplink_sdu6_t *sdu)
167{
168 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_send6()");
169
170 return ENOTSUP;
171}
172
173errno_t slip_get_mtu(iplink_srv_t *srv, size_t *mtu)
174{
175 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_get_mtu()");
176 *mtu = SLIP_MTU;
177 return EOK;
178}
179
180errno_t slip_get_mac48(iplink_srv_t *src, eth_addr_t *mac)
181{
182 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_get_mac48()");
183 return ENOTSUP;
184}
185
186errno_t slip_addr_add(iplink_srv_t *srv, inet_addr_t *addr)
187{
188 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_addr_add()");
189 return EOK;
190}
191
192errno_t slip_addr_remove(iplink_srv_t *srv, inet_addr_t *addr)
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
203static void slip_client_conn(ipc_call_t *icall, void *arg)
204{
205 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_client_conn()");
206 iplink_conn(icall, &slip_iplink);
207}
208
209static uint8_t read_buffered(chardev_t *chardev)
210{
211 while (slip_recv_pending == 0) {
212 errno_t rc;
213 size_t nread;
214
215 rc = chardev_read(chardev, slip_recv_buf,
216 sizeof(slip_recv_buf), &nread, chardev_f_none);
217 if (rc != EOK) {
218 log_msg(LOG_DEFAULT, LVL_ERROR,
219 "char_dev_read() -> %s", str_error_name(rc));
220 }
221
222 if (nread == 0)
223 return SLIP_END;
224
225 slip_recv_pending = nread;
226 slip_recv_read = 0;
227 }
228 slip_recv_pending--;
229 return slip_recv_buf[slip_recv_read++];
230}
231
232static errno_t slip_recv_fibril(void *arg)
233{
234 chardev_t *chardev = (chardev_t *) arg;
235 static uint8_t recv_final[SLIP_MTU];
236 iplink_recv_sdu_t sdu;
237 uint8_t ch;
238 errno_t rc;
239
240 sdu.data = recv_final;
241
242 while (true) {
243 sdu.size = 0;
244 while (sdu.size < sizeof(recv_final)) {
245 ch = read_buffered(chardev);
246 switch (ch) {
247 case SLIP_END:
248 if (sdu.size == 0) {
249 /*
250 * Discard the empty SLIP datagram.
251 */
252 break;
253 }
254 goto pass;
255
256 case SLIP_ESC:
257 ch = read_buffered(chardev);
258 if (ch == SLIP_ESC_END) {
259 recv_final[sdu.size++] = SLIP_END;
260 break;
261 } else if (ch == SLIP_ESC_ESC) {
262 recv_final[sdu.size++] = SLIP_ESC;
263 break;
264 }
265
266 /*
267 * The RFC suggests to simply insert the wrongly
268 * escaped character into the packet so we fall
269 * through.
270 */
271 /* Fallthrough */
272
273 default:
274 recv_final[sdu.size++] = ch;
275 break;
276 }
277
278 }
279
280 /*
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 */
287
288 pass:
289 rc = iplink_ev_recv(&slip_iplink, &sdu, ip_v4);
290 if (rc != EOK) {
291 log_msg(LOG_DEFAULT, LVL_ERROR,
292 "iplink_ev_recv() -> %s", str_error_name(rc));
293 }
294 }
295
296 return 0;
297}
298
299static errno_t slip_init(const char *svcstr, const char *linkstr)
300{
301 service_id_t svcid;
302 service_id_t linksid;
303 category_id_t iplinkcid;
304 async_sess_t *sess_in = NULL;
305 async_sess_t *sess_out = NULL;
306 chardev_t *chardev_in = NULL;
307 chardev_t *chardev_out = NULL;
308 fid_t fid;
309 loc_srv_t *srv;
310 errno_t rc;
311
312 iplink_srv_init(&slip_iplink);
313 slip_iplink.ops = &slip_iplink_ops;
314
315 async_set_fallback_port_handler(slip_client_conn, NULL);
316
317 rc = loc_server_register(NAME, &srv);
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) {
326 loc_server_unregister(srv);
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) {
334 loc_server_unregister(srv);
335 log_msg(LOG_DEFAULT, LVL_ERROR,
336 "Failed to get category ID for %s",
337 CAT_IPLINK);
338 return rc;
339 }
340
341 /*
342 * Create two sessions to allow to both read and write from the
343 * char_dev at the same time.
344 */
345 sess_out = loc_service_connect(svcid, INTERFACE_DDF, 0);
346 if (!sess_out) {
347 loc_server_unregister(srv);
348 log_msg(LOG_DEFAULT, LVL_ERROR,
349 "Failed to connect to service %s (ID=%d)",
350 svcstr, (int) svcid);
351 return ENOENT;
352 }
353
354 rc = chardev_open(sess_out, &chardev_out);
355 if (rc != EOK) {
356 loc_server_unregister(srv);
357 log_msg(LOG_DEFAULT, LVL_ERROR,
358 "Failed opening character device.");
359 return ENOENT;
360 }
361
362 slip_iplink.arg = chardev_out;
363
364 sess_in = loc_service_connect(svcid, INTERFACE_DDF, 0);
365 if (!sess_in) {
366 log_msg(LOG_DEFAULT, LVL_ERROR,
367 "Failed to connect to service %s (ID=%d)",
368 svcstr, (int) svcid);
369 rc = ENOENT;
370 goto fail;
371 }
372
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
380 rc = loc_service_register(srv, linkstr, fallback_port_id, &linksid);
381 if (rc != EOK) {
382 log_msg(LOG_DEFAULT, LVL_ERROR,
383 "Failed to register service %s",
384 linkstr);
385 goto fail;
386 }
387
388 rc = loc_service_add_to_cat(srv, linksid, iplinkcid);
389 if (rc != EOK) {
390 loc_service_unregister(srv, linksid);
391 log_msg(LOG_DEFAULT, LVL_ERROR,
392 "Failed to add service %d (%s) to category %d (%s).",
393 (int) linksid, linkstr, (int) iplinkcid, CAT_IPLINK);
394 goto fail;
395 }
396
397 fid = fibril_create(slip_recv_fibril, chardev_in);
398 if (!fid) {
399 log_msg(LOG_DEFAULT, LVL_ERROR,
400 "Failed to create receive fibril.");
401 rc = ENOENT;
402 goto fail;
403 }
404 fibril_add_ready(fid);
405
406 return EOK;
407
408fail:
409 loc_server_unregister(srv);
410 chardev_close(chardev_out);
411 if (sess_out)
412 async_hangup(sess_out);
413 chardev_close(chardev_in);
414 if (sess_in)
415 async_hangup(sess_in);
416
417 /*
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 */
421
422 return rc;
423}
424
425int main(int argc, char *argv[])
426{
427 errno_t rc;
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.