osmocom-bb/src/host/virt_phy/src/l1ctl_sock.c

199 lines
5.1 KiB
C

/* Socket based Layer1 <-> Layer23 communication over L1CTL primitives. */
/* (C) 2016 by Sebastian Stumpf <sebastian.stumpf87@googlemail.com>
* (C) 2017 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/select.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/socket.h>
#include <virtphy/l1ctl_sock.h>
#include <virtphy/logging.h>
#define L1CTL_SOCK_MSGB_SIZE 256
static void l1ctl_client_destroy(struct l1ctl_sock_client *lsc)
{
struct l1ctl_sock_inst *lsi = lsc->l1ctl_sock;
if (lsi->close_cb)
lsi->close_cb(lsc);
osmo_fd_close(&lsc->ofd);
llist_del(&lsc->list);
talloc_free(lsc);
}
/**
* @brief L1CTL socket file descriptor callback function.
*
* @param ofd The osmocom file descriptor.
* @param what Indicates if the fd has a read, write or exception request. See select.h.
*
* Will be called by osmo_select_main() if data on fd is pending.
*/
static int l1ctl_sock_data_cb(struct osmo_fd *ofd, unsigned int what)
{
struct l1ctl_sock_client *lsc = ofd->data;
struct l1ctl_hdr *l1h;
struct msgb *msg;
uint16_t len;
int rc;
/* Check if request is really read request */
if (!(what & OSMO_FD_READ))
return 0;
msg = msgb_alloc(L1CTL_SOCK_MSGB_SIZE, "L1CTL sock rx");
/* read length of the message first and convert to host byte order */
rc = read(ofd->fd, &len, sizeof(len));
if (rc < sizeof(len))
goto err_close;
/* convert to host byte order */
len = ntohs(len);
if (len <= 0 || len > L1CTL_SOCK_MSGB_SIZE)
goto err_close;
rc = read(ofd->fd, msgb_data(msg), len);
if (rc == len) {
msgb_put(msg, rc);
l1h = (void *) msgb_data(msg);
msg->l1h = (void *) l1h;
lsc->l1ctl_sock->recv_cb(lsc, msg);
return 0;
}
err_close:
LOGP(DL1C, LOGL_ERROR, "Failed to receive msg from l2. Connection will be closed.\n");
l1ctl_client_destroy(lsc);
return 0;
}
/* called for the master (listening) socket of the instance, allocates a new client */
static int l1ctl_sock_accept_cb(struct osmo_fd *ofd, unsigned int what)
{
struct l1ctl_sock_inst *lsi = ofd->data;
struct l1ctl_sock_client *lsc;
int fd, rc;
fd = accept(ofd->fd, NULL, NULL);
if (fd < 0) {
LOGP(DL1C, LOGL_ERROR, "Failed to accept connection to l2.\n");
return -1;
}
lsc = talloc_zero(lsi, struct l1ctl_sock_client);
if (!lsc) {
close(fd);
LOGP(DL1C, LOGL_ERROR, "Failed to allocate L1CTL client\n");
return -1;
}
lsc->l1ctl_sock = lsi;
osmo_fd_setup(&lsc->ofd, fd, OSMO_FD_READ, l1ctl_sock_data_cb, lsc, 0);
if (lsi->accept_cb) {
rc = lsi->accept_cb(lsc);
if (rc < 0) {
talloc_free(lsc);
close(fd);
return rc;
}
}
LOGP(DL1C, LOGL_INFO, "Accepted client (fd=%u) from server (fd=%u)\n", fd, ofd->fd);
if (osmo_fd_register(&lsc->ofd) != 0) {
LOGP(DL1C, LOGL_ERROR, "Failed to register the l2 connection fd.\n");
talloc_free(lsc);
return -1;
}
llist_add_tail(&lsc->list, &lsi->clients);
return 0;
}
struct l1ctl_sock_inst *l1ctl_sock_init(
void *ctx,
void (*recv_cb)(struct l1ctl_sock_client *lsc, struct msgb *msg),
int (*accept_cb)(struct l1ctl_sock_client *lsc),
void (*close_cb)(struct l1ctl_sock_client *lsc),
char *path)
{
struct l1ctl_sock_inst *lsi;
int rc;
if (!path)
path = L1CTL_SOCK_PATH;
lsi = talloc_zero(ctx, struct l1ctl_sock_inst);
lsi->priv = NULL;
osmo_fd_setup(&lsi->ofd, -1, OSMO_FD_READ, l1ctl_sock_accept_cb, lsi, 0);
rc = osmo_sock_unix_init_ofd(&lsi->ofd, SOCK_STREAM, 0, path, OSMO_SOCK_F_BIND);
if (rc < 0) {
LOGP(DL1C, LOGL_ERROR, "Error creating L1CTL listening socket\n");
talloc_free(lsi);
return NULL;
}
lsi->recv_cb = recv_cb;
lsi->accept_cb = accept_cb;
lsi->close_cb = close_cb;
lsi->l1ctl_sock_path = path;
INIT_LLIST_HEAD(&lsi->clients);
return lsi;
}
void l1ctl_sock_destroy(struct l1ctl_sock_inst *lsi)
{
struct l1ctl_sock_client *lsc, *lsc2;
llist_for_each_entry_safe(lsc, lsc2, &lsi->clients, list)
l1ctl_client_destroy(lsc);
osmo_fd_close(&lsi->ofd);
talloc_free(lsi);
}
int l1ctl_sock_write_msg(struct l1ctl_sock_client *lsc, struct msgb *msg)
{
int rc;
rc = write(lsc->ofd.fd, msgb_data(msg), msgb_length(msg));
msgb_free(msg);
return rc;
}