blob: b738b3f59708bc839a2b198f305042dfb92eb6fe [file] [log] [blame]
Alexander Couzensbeb10ef2016-11-01 22:05:13 +01001/* OpenBSC Abis receive lapd over a unix socket */
2
Harald Welte323d39d2017-11-13 01:09:21 +09003/* (C) 2016 by sysmocom - s.f.m.c. GmbH
Alexander Couzensbeb10ef2016-11-01 22:05:13 +01004 * Author: Alexander Couzens <lynxis@fe80.eu>
5 * Based on other e1_input drivers.
6 *
7 * All Rights Reserved
8 *
Harald Welte323d39d2017-11-13 01:09:21 +09009 * SPDX-License-Identifier: GPL-2.0+
10 *
Alexander Couzensbeb10ef2016-11-01 22:05:13 +010011 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
Alexander Couzensbeb10ef2016-11-01 22:05:13 +010021 */
22
23#include <errno.h>
24#include <stdio.h>
25#include <unistd.h>
26#include <sys/socket.h>
Stefan Sperlingb24efa52018-08-28 14:03:41 +020027#include <sys/un.h>
Alexander Couzensbeb10ef2016-11-01 22:05:13 +010028#include <limits.h>
29#include <string.h>
30
31#include <osmocom/core/talloc.h>
32#include <osmocom/core/logging.h>
33#include <osmocom/core/socket.h>
34
35#include <osmocom/abis/e1_input.h>
36#include <osmocom/abis/lapd.h>
37#include <osmocom/abis/e1_input.h>
38
39#include <osmocom/abis/unixsocket_proto.h>
40#include "internal.h"
41
42void *tall_unixsocket_ctx;
43#define UNIXSOCKET_ALLOC_SIZE 1600
44#define UNIXSOCKET_SOCK_PATH_DEFAULT "/tmp/osmo_abis_line_"
45
46struct unixsocket_line {
47 struct osmo_fd fd;
48};
49
50static int unixsocket_line_update(struct e1inp_line *line);
51static int ts_want_write(struct e1inp_ts *e1i_ts);
52
53static int unixsocket_exception_cb(struct osmo_fd *bfd)
54{
55 struct e1inp_line *line = bfd->data;
56
Harald Welteb5af0992020-01-12 12:59:52 +010057 LOGPIL(line, DLINP, LOGL_ERROR, "Socket connection failure, reconnecting... (line=%p, fd=%d)\n",
58 line, bfd->fd);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +010059
60 /* Unregister faulty file descriptor from select loop */
61 if(osmo_fd_is_registered(bfd)) {
Harald Welteb5af0992020-01-12 12:59:52 +010062 LOGPIL(line, DLINP, LOGL_DEBUG, "removing inactive socket from select loop... (line=%p, fd=%d)\n",
63 line, bfd->fd);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +010064 osmo_fd_unregister(bfd);
65 }
66
67 /* Close faulty file descriptor */
68 close(bfd->fd);
69
70 unixsocket_line_update(line);
71
72 return 0;
73}
74
75static int unixsocket_read_cb(struct osmo_fd *bfd)
76{
77 struct e1inp_line *line = bfd->data;
78 struct msgb *msg = msgb_alloc(UNIXSOCKET_ALLOC_SIZE, "UNIXSOCKET TS");
79 uint8_t version;
80 uint8_t controldata;
81 int ret;
82
83 if (!msg)
84 return -ENOMEM;
85
86 ret = read(bfd->fd, msg->data, UNIXSOCKET_ALLOC_SIZE - 16);
87 if (ret == 0) {
88 unixsocket_exception_cb(bfd);
89 goto fail;
90 } else if (ret < 0) {
91 perror("read ");
92 goto fail;
93 } else if (ret < 2) {
94 /* packet must be at least 2 byte long to hold version + control/data header */
Harald Welteb5af0992020-01-12 12:59:52 +010095 LOGPIL(line, DLMI, LOGL_ERROR, "received to small packet: %d < 2", ret);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +010096 ret = -1;
97 goto fail;
98 }
99 msgb_put(msg, ret);
100
Harald Welteb5af0992020-01-12 12:59:52 +0100101 LOGPIL(line, DLMI, LOGL_DEBUG, "rx msg: %s (fd=%d)\n", osmo_hexdump_nospc(msg->data, msg->len), bfd->fd);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100102
103 /* check version header */
104 version = msgb_pull_u8(msg);
105 controldata = msgb_pull_u8(msg);
106
107 if (version != UNIXSOCKET_PROTO_VERSION) {
Harald Welteb5af0992020-01-12 12:59:52 +0100108 LOGPIL(line, DLMI, LOGL_ERROR, "received message with invalid version %d. valid: %d",
109 ret, UNIXSOCKET_PROTO_VERSION);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100110 ret = -1;
111 goto fail;
112 }
113
114 switch (controldata) {
115 case UNIXSOCKET_PROTO_DATA:
116 return e1inp_rx_ts_lapd(&line->ts[0], msg);
117 case UNIXSOCKET_PROTO_CONTROL:
Harald Welteb5af0992020-01-12 12:59:52 +0100118 LOGPIL(line, DLMI, LOGL_ERROR, "received (invalid) control message.");
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100119 ret = -1;
120 break;
121 default:
Harald Welteb5af0992020-01-12 12:59:52 +0100122 LOGPIL(line, DLMI, LOGL_ERROR, "received invalid message.");
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100123 ret = -1;
124 break;
125 }
126fail:
127 msgb_free(msg);
128 return ret;
129}
130
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100131static int unixsocket_write_cb(struct osmo_fd *bfd)
132{
133 struct e1inp_line *line = bfd->data;
134 struct e1inp_ts *e1i_ts = &line->ts[0];
135 struct msgb *msg;
136 struct e1inp_sign_link *sign_link;
137
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100138 /* get the next msg for this timeslot */
139 msg = e1inp_tx_ts(e1i_ts, &sign_link);
140 if (!msg) {
Harald Welte4b77d422022-05-06 22:43:54 +0200141 osmo_fd_write_disable(bfd);
Harald Welteb5af0992020-01-12 12:59:52 +0100142 LOGPITS(e1i_ts, DLINP, LOGL_INFO, "no message available (line=%p)\n", line);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100143 return 0;
144 }
145
Harald Welteb5af0992020-01-12 12:59:52 +0100146 LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "sending: %s (line=%p)\n", msgb_hexdump(msg), line);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100147 lapd_transmit(e1i_ts->lapd, sign_link->tei,
148 sign_link->sapi, msg);
149
150 return 0;
151}
152
153static int unixsocket_cb(struct osmo_fd *bfd, unsigned int what)
154{
155 int ret = 0;
156
Harald Weltede3959e2020-10-18 22:28:50 +0200157 if (what & OSMO_FD_READ)
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100158 ret = unixsocket_read_cb(bfd);
Harald Weltede3959e2020-10-18 22:28:50 +0200159 if (what & OSMO_FD_WRITE)
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100160 ret = unixsocket_write_cb(bfd);
161
162 return ret;
163}
164
165static int ts_want_write(struct e1inp_ts *e1i_ts)
166{
167 struct unixsocket_line *line = e1i_ts->line->driver_data;
168
Harald Welte949b8a22020-10-18 23:01:53 +0200169 osmo_fd_write_enable(&line->fd);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100170
171 return 0;
172}
173
Harald Welteb5af0992020-01-12 12:59:52 +0100174static void unixsocket_write_msg(struct msgb *msg, struct osmo_fd *bfd)
175{
176 struct e1inp_line *line = bfd->data;
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100177 int ret;
178
Harald Welteb5af0992020-01-12 12:59:52 +0100179 LOGPIL(line, DLMI, LOGL_DEBUG, "tx msg: %s (fd=%d)\n",
180 osmo_hexdump_nospc(msg->data, msg->len), bfd->fd);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100181
182 ret = write(bfd->fd, msg->data, msg->len);
183 msgb_free(msg);
184 if (ret == -1)
185 unixsocket_exception_cb(bfd);
186 else if (ret < 0)
Harald Welteb5af0992020-01-12 12:59:52 +0100187 LOGPIL(line, DLMI, LOGL_NOTICE, "%s write failed %d\n", __func__, ret);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100188}
189
190/*!
191 * \brief unixsocket_write_msg lapd callback for data to unixsocket
192 * \param msg
193 * \param cbdata
194 */
195static void unixsocket_write_msg_lapd_cb(struct msgb *msg, void *cbdata)
196{
197 struct osmo_fd *bfd = cbdata;
198
199 /* data|control */
200 msgb_push_u8(msg, UNIXSOCKET_PROTO_DATA);
201 /* add version header */
202 msgb_push_u8(msg, UNIXSOCKET_PROTO_VERSION);
203
204 unixsocket_write_msg(msg, bfd);
205}
206
207static int unixsocket_line_update(struct e1inp_line *line)
208{
209 struct unixsocket_line *config;
Stefan Sperling0d7d0b02018-09-20 17:34:31 +0200210 struct sockaddr_un un;
Stefan Sperlingb24efa52018-08-28 14:03:41 +0200211 const char *sock_path;
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100212 int ret = 0;
213 int i;
214
Harald Welteb5af0992020-01-12 12:59:52 +0100215 LOGPIL(line, DLINP, LOGL_NOTICE, "line update (line=%p)\n", line);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100216
217 if (!line->driver_data)
218 line->driver_data = talloc_zero(line, struct unixsocket_line);
219
220 if (!line->driver_data) {
Harald Welteb5af0992020-01-12 12:59:52 +0100221 LOGPIL(line, DLINP, LOGL_ERROR, "OOM in line update (line=%p)\n", line);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100222 return -ENOMEM;
223 }
224
225 config = line->driver_data;
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100226
227 /* Open unix domain socket */
Stefan Sperlingb24efa52018-08-28 14:03:41 +0200228 if (line->sock_path == NULL) {
Stefan Sperling0d7d0b02018-09-20 17:34:31 +0200229 ret = snprintf(un.sun_path, sizeof(un.sun_path), "%s%d",
230 UNIXSOCKET_SOCK_PATH_DEFAULT, line->num);
231 if (ret == -1) {
Harald Welteb5af0992020-01-12 12:59:52 +0100232 LOGPIL(line, DLINP, LOGL_ERROR, "Cannot create default socket path: %s\n", strerror(errno));
Stefan Sperling0d7d0b02018-09-20 17:34:31 +0200233 return -errno;
234 } else if (ret >= sizeof(un.sun_path)) {
Harald Welteb5af0992020-01-12 12:59:52 +0100235 LOGPIL(line, DLINP, LOGL_ERROR, "Default socket path exceeds %zd bytes: %s%d\n",
Stefan Sperling0d7d0b02018-09-20 17:34:31 +0200236 sizeof(un.sun_path), UNIXSOCKET_SOCK_PATH_DEFAULT, line->num);
237 return -ENOSPC;
238 }
239 sock_path = un.sun_path;
Stefan Sperlingb24efa52018-08-28 14:03:41 +0200240 } else
241 sock_path = line->sock_path;
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100242 ret = osmo_sock_unix_init(SOCK_SEQPACKET, 0, sock_path,
243 OSMO_SOCK_F_CONNECT);
244 if (ret < 0) {
245 /* Note: We will not free the allocated driver_data memory if
246 * opening the socket fails. The caller may want to call this
247 * function multiple times using config->fd.data as line
248 * parameter. Freeing now would destroy that reference. */
Harald Welteb5af0992020-01-12 12:59:52 +0100249 LOGPIL(line, DLINP, LOGL_ERROR, "unable to open socket: %s (line=%p, fd=%d)\n", sock_path,
250 line, config->fd.fd);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100251 return ret;
252 }
Harald Welteb5af0992020-01-12 12:59:52 +0100253 LOGPIL(line, DLINP, LOGL_DEBUG, "successfully opend (new) socket: %s (line=%p, fd=%d, ret=%d)\n",
254 sock_path, line, config->fd.fd, ret);
Harald Welte6e831b72020-10-18 22:59:58 +0200255 osmo_fd_setup(&config->fd, ret, OSMO_FD_READ, unixsocket_cb, line, 0);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100256
257 /* Register socket in select loop */
258 if (osmo_fd_register(&config->fd) < 0) {
Harald Welteb5af0992020-01-12 12:59:52 +0100259 LOGPIL(line, DLINP, LOGL_ERROR, "error registering new socket (line=%p, fd=%d)\n",
260 line, config->fd.fd);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100261 close(config->fd.fd);
262 return -EIO;
263 }
264
265 /* Set line parameter */
266 for (i = 0; i < ARRAY_SIZE(line->ts); i++) {
267 struct e1inp_ts *e1i_ts = &line->ts[i];
Harald Welteb9031882020-05-02 21:09:15 +0200268 char name[32];
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100269 if (!e1i_ts->lapd) {
Harald Welteb9031882020-05-02 21:09:15 +0200270 e1inp_ts_name(name, sizeof(name), e1i_ts);
271 e1i_ts->lapd = lapd_instance_alloc2(1,
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100272 unixsocket_write_msg_lapd_cb, &config->fd,
Harald Welteb9031882020-05-02 21:09:15 +0200273 e1inp_dlsap_up, e1i_ts, &lapd_profile_abis, name);
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100274 }
275 }
276
277 /* Ensure ericsson-superchannel is turned of when
278 * a new connection is made */
279 e1inp_ericsson_set_altc(line, 0);
280
281 return ret;
282}
283
284struct e1inp_driver unixsocket_driver = {
285 .name = "unixsocket",
286 .want_write = ts_want_write,
287 .line_update = unixsocket_line_update,
288 .default_delay = 0,
289};
290
291void e1inp_unixsocket_init(void)
292{
293 tall_unixsocket_ctx = talloc_named_const(libosmo_abis_ctx, 1, "unixsocket");
294 e1inp_driver_register(&unixsocket_driver);
295}
296
297void e1inp_ericsson_set_altc(struct e1inp_line *unixline, int superchannel)
298{
299 struct unixsocket_line *config;
300 struct msgb *msg;
301
302 if (!unixline)
303 return;
304
305 if (unixline->driver != &unixsocket_driver) {
Harald Welteb5af0992020-01-12 12:59:52 +0100306 LOGPIL(unixline, DLMI, LOGL_NOTICE, "altc is only supported by unixsocket\n");
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100307 return;
308 }
309
310 config = unixline->driver_data;
311 if (!config) {
Harald Welteb5af0992020-01-12 12:59:52 +0100312 LOGPIL(unixline, DLMI, LOGL_NOTICE, "e1inp driver not yet initialized.\n");
Alexander Couzensbeb10ef2016-11-01 22:05:13 +0100313 return;
314 }
315
316
317 msg = msgb_alloc_headroom(200, 100, "ALTC");
318
319 /* version header */
320 msgb_put_u8(msg, UNIXSOCKET_PROTO_VERSION);
321 /* data|control */
322 msgb_put_u8(msg, UNIXSOCKET_PROTO_CONTROL);
323
324 /* magic */
325 msgb_put_u32(msg, 0x23004200);
326 msgb_put_u8(msg, superchannel ? 1 : 0);
327
328 unixsocket_write_msg(msg, &config->fd);
329}
330