mirror of https://gerrit.osmocom.org/libosmocore
gsmtap: rework GSMTAP API to be more future-proof
* use write_queue where applicable * provide functions that work on raw FD and those with osmo_fd * add support for multiple gsmtap instances (no global variables)
This commit is contained in:
parent
8256076722
commit
33cb71ac91
|
@ -1,5 +1,5 @@
|
|||
osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h bits.h \
|
||||
bitvec.h statistics.h utils.h \
|
||||
bitvec.h statistics.h utils.h socket.h \
|
||||
gsmtap.h write_queue.h \
|
||||
logging.h rate_ctr.h gsmtap_util.h \
|
||||
plugin.h crc16.h panic.h process.h msgfile.h \
|
||||
|
|
|
@ -2,24 +2,52 @@
|
|||
#define _GSMTAP_UTIL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/select.h>
|
||||
|
||||
/* convert RSL channel number to GSMTAP channel type */
|
||||
uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t rsl_link_id);
|
||||
|
||||
/* receive a message from L1/L2 and put it in GSMTAP */
|
||||
/* generate msgb from data + metadata */
|
||||
struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type,
|
||||
uint8_t ss, uint32_t fn, int8_t signal_dbm,
|
||||
uint8_t snr, const uint8_t *data, unsigned int len);
|
||||
|
||||
/* receive a message from L1/L2 and put it in GSMTAP */
|
||||
int gsmtap_sendmsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss,
|
||||
uint32_t fn, int8_t signal_dbm, uint8_t snr,
|
||||
const uint8_t *data, unsigned int len);
|
||||
/* one gsmtap instance */
|
||||
struct gsmtap_inst {
|
||||
int ofd_wq_mode;
|
||||
struct osmo_wqueue wq;
|
||||
struct osmo_fd sink_ofd;
|
||||
};
|
||||
|
||||
int gsmtap_init(uint32_t dst_ip);
|
||||
static inline int gsmtap_inst_fd(struct gsmtap_inst *gti)
|
||||
{
|
||||
return gti->wq.bfd.fd;
|
||||
}
|
||||
|
||||
/* Create a local 'gsmtap sink' avoiding the UDP packets being rejected
|
||||
* with ICMP reject messages */
|
||||
int gsmtap_sink_init(uint32_t bind_ip);
|
||||
/* Open a GSMTAP source (sending) socket, conncet it to host/port and
|
||||
* return resulting fd */
|
||||
int gsmtap_source_init_fd(const char *host, uint16_t port);
|
||||
|
||||
/* Add a local sink to an existing GSMTAP source and return fd */
|
||||
int gsmtap_source_add_sink_fd(int gsmtap_fd);
|
||||
|
||||
/* Open GSMTAP source (sending) socket, connect it to host/port,
|
||||
* allocate 'struct gsmtap_inst' and optionally osmo_fd/osmo_wqueue
|
||||
* registration */
|
||||
struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port,
|
||||
int ofd_wq_mode);
|
||||
|
||||
/* Add a local sink to an existing GSMTAP source instance */
|
||||
int gsmtap_source_add_sink(struct gsmtap_inst *gti);
|
||||
|
||||
/* Send a msgb through a GSMTAP source */
|
||||
int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg);
|
||||
|
||||
/* generate a message and send it via GSMTAP */
|
||||
int gsmtap_send(struct gsmtap_inst *gti, uint16_t arfcn, uint8_t ts,
|
||||
uint8_t chan_type, uint8_t ss, uint32_t fn,
|
||||
int8_t signal_dbm, uint8_t snr, const uint8_t *data,
|
||||
unsigned int len);
|
||||
|
||||
#endif /* _GSMTAP_UTIL_H */
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef _OSMOCORE_SOCKET_H
|
||||
#define _OSMOCORE_SOCKET_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
|
||||
const char *host, uint16_t port, int connect0_bind1);
|
||||
|
||||
int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
|
||||
uint8_t proto, int connect0_bind1);
|
||||
|
||||
/* determine if the given address is a local address */
|
||||
int osmo_sockaddr_is_local(struct sockaddr *addr, socklen_t addrlen);
|
||||
|
||||
#endif /* _OSMOCORE_SOCKET_H */
|
|
@ -2,7 +2,7 @@ SUBDIRS=. vty codec gsm
|
|||
|
||||
# This is _NOT_ the library release version, it's an API version.
|
||||
# Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification
|
||||
LIBVERSION=1:0:1
|
||||
LIBVERSION=2:0:0
|
||||
|
||||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS = -fPIC -Wall
|
||||
|
@ -11,7 +11,7 @@ lib_LTLIBRARIES = libosmocore.la
|
|||
|
||||
libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c bits.c \
|
||||
bitvec.c statistics.c \
|
||||
write_queue.c utils.c \
|
||||
write_queue.c utils.c socket.c \
|
||||
logging.c logging_syslog.c rate_ctr.c \
|
||||
gsmtap_util.c crc16.c panic.c backtrace.c \
|
||||
process.c conv.c application.c
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* GSMTAP output for Osmocom Layer2 (will only work on the host PC) */
|
||||
/* GSMTAP support code in libmsomcore */
|
||||
/*
|
||||
* (C) 2010 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2010-2011 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
|
@ -22,18 +22,19 @@
|
|||
|
||||
#include "../config.h"
|
||||
|
||||
#ifdef HAVE_SYS_SELECT_H
|
||||
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
#include <osmocom/gsm/rsl.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
@ -42,10 +43,6 @@
|
|||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
static struct osmo_fd gsmtap_bfd = { .fd = -1 };
|
||||
static struct osmo_fd gsmtap_sink_bfd = { .fd = -1 };
|
||||
static LLIST_HEAD(gsmtap_txqueue);
|
||||
|
||||
uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id)
|
||||
{
|
||||
uint8_t ret = GSMTAP_CHANNEL_UNKNOWN;
|
||||
|
@ -114,44 +111,82 @@ struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type,
|
|||
return msg;
|
||||
}
|
||||
|
||||
/* Open a GSMTAP source (sending) socket, conncet it to host/port and
|
||||
* return resulting fd */
|
||||
int gsmtap_source_init_fd(const char *host, uint16_t port)
|
||||
{
|
||||
if (port == 0)
|
||||
port = GSMTAP_UDP_PORT;
|
||||
if (host == NULL)
|
||||
host = "localhost";
|
||||
|
||||
return osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, host, port, 0);
|
||||
}
|
||||
|
||||
int gsmtap_source_add_sink_fd(int gsmtap_fd)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t ss_len = sizeof(ss);
|
||||
int rc;
|
||||
|
||||
rc = getpeername(gsmtap_fd, (struct sockaddr *)&ss, &ss_len);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (osmo_sockaddr_is_local((struct sockaddr *)&ss, ss_len) == 1) {
|
||||
rc = osmo_sock_init_sa((struct sockaddr *)&ss, SOCK_DGRAM, IPPROTO_UDP, 1);
|
||||
if (rc >= 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYS_SELECT_H
|
||||
|
||||
int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg)
|
||||
{
|
||||
if (gti->ofd_wq_mode)
|
||||
return osmo_wqueue_enqueue(>i->wq, msg);
|
||||
else {
|
||||
/* try immediate send and return error if any */
|
||||
int rc;
|
||||
|
||||
rc = write(gsmtap_inst_fd(gti), msg->data, msg->len);
|
||||
if (rc <= 0) {
|
||||
return rc;
|
||||
} else if (rc >= msg->len) {
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
} else {
|
||||
/* short write */
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* receive a message from L1/L2 and put it in GSMTAP */
|
||||
int gsmtap_sendmsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss,
|
||||
uint32_t fn, int8_t signal_dbm, uint8_t snr,
|
||||
const uint8_t *data, unsigned int len)
|
||||
int gsmtap_send(struct gsmtap_inst *gti, uint16_t arfcn, uint8_t ts,
|
||||
uint8_t chan_type, uint8_t ss, uint32_t fn,
|
||||
int8_t signal_dbm, uint8_t snr, const uint8_t *data,
|
||||
unsigned int len)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
/* gsmtap was never initialized, so don't try to send anything */
|
||||
if (gsmtap_bfd.fd == -1)
|
||||
return 0;
|
||||
|
||||
msg = gsmtap_makemsg(arfcn, ts, chan_type, ss, fn, signal_dbm,
|
||||
snr, data, len);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
msgb_enqueue(&gsmtap_txqueue, msg);
|
||||
gsmtap_bfd.when |= BSC_FD_WRITE;
|
||||
|
||||
return 0;
|
||||
return gsmtap_sendmsg(gti, msg);
|
||||
}
|
||||
|
||||
/* Callback from select layer if we can write to the socket */
|
||||
static int gsmtap_fd_cb(struct osmo_fd *fd, unsigned int flags)
|
||||
static int gsmtap_wq_w_cb(struct osmo_fd *ofd, struct msgb *msg)
|
||||
{
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
if (!(flags & BSC_FD_WRITE))
|
||||
return 0;
|
||||
|
||||
msg = msgb_dequeue(&gsmtap_txqueue);
|
||||
if (!msg) {
|
||||
/* no more messages in the queue, disable READ cb */
|
||||
gsmtap_bfd.when = 0;
|
||||
return 0;
|
||||
}
|
||||
rc = write(gsmtap_bfd.fd, msg->data, msg->len);
|
||||
rc = write(ofd->fd, msg->data, msg->len);
|
||||
if (rc < 0) {
|
||||
perror("writing msgb to gsmtap fd");
|
||||
msgb_free(msg);
|
||||
|
@ -167,37 +202,6 @@ static int gsmtap_fd_cb(struct osmo_fd *fd, unsigned int flags)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int gsmtap_init(uint32_t dst_ip)
|
||||
{
|
||||
int rc;
|
||||
struct sockaddr_in sin;
|
||||
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(GSMTAP_UDP_PORT);
|
||||
sin.sin_addr.s_addr = htonl(dst_ip);
|
||||
|
||||
/* create socket */
|
||||
rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (rc < 0) {
|
||||
perror("creating UDP socket");
|
||||
return rc;
|
||||
}
|
||||
gsmtap_bfd.fd = rc;
|
||||
rc = connect(rc, (struct sockaddr *)&sin, sizeof(sin));
|
||||
if (rc < 0) {
|
||||
perror("connecting UDP socket");
|
||||
close(gsmtap_bfd.fd);
|
||||
gsmtap_bfd.fd = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
gsmtap_bfd.when = BSC_FD_WRITE;
|
||||
gsmtap_bfd.cb = gsmtap_fd_cb;
|
||||
gsmtap_bfd.data = NULL;
|
||||
|
||||
return osmo_fd_register(&gsmtap_bfd);
|
||||
}
|
||||
|
||||
/* Callback from select layer if we can read from the sink socket */
|
||||
static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags)
|
||||
{
|
||||
|
@ -217,37 +221,53 @@ static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Create a local 'gsmtap sink' avoiding the UDP packets being rejected
|
||||
* with ICMP reject messages */
|
||||
int gsmtap_sink_init(uint32_t bind_ip)
|
||||
/* Add a local sink to an existing GSMTAP source instance */
|
||||
int gsmtap_source_add_sink(struct gsmtap_inst *gti)
|
||||
{
|
||||
int rc;
|
||||
struct sockaddr_in sin;
|
||||
int fd;
|
||||
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(GSMTAP_UDP_PORT);
|
||||
sin.sin_addr.s_addr = htonl(bind_ip);
|
||||
fd = gsmtap_source_add_sink_fd(gsmtap_inst_fd(gti));
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (rc < 0) {
|
||||
perror("creating UDP socket");
|
||||
return rc;
|
||||
}
|
||||
gsmtap_sink_bfd.fd = rc;
|
||||
rc = bind(rc, (struct sockaddr *)&sin, sizeof(sin));
|
||||
if (rc < 0) {
|
||||
perror("binding UDP socket");
|
||||
close(gsmtap_sink_bfd.fd);
|
||||
gsmtap_sink_bfd.fd = -1;
|
||||
return rc;
|
||||
if (gti->ofd_wq_mode) {
|
||||
struct osmo_fd *sink_ofd;
|
||||
|
||||
sink_ofd = >i->sink_ofd;
|
||||
sink_ofd->fd = fd;
|
||||
sink_ofd->when = BSC_FD_READ;
|
||||
sink_ofd->cb = gsmtap_sink_fd_cb;
|
||||
|
||||
osmo_fd_register(sink_ofd);
|
||||
}
|
||||
|
||||
gsmtap_sink_bfd.when = BSC_FD_READ;
|
||||
gsmtap_sink_bfd.cb = gsmtap_sink_fd_cb;
|
||||
gsmtap_sink_bfd.data = NULL;
|
||||
return fd;
|
||||
}
|
||||
|
||||
return osmo_fd_register(&gsmtap_sink_bfd);
|
||||
/* like gsmtap_init2() but integrated with libosmocore select.c */
|
||||
struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port,
|
||||
int ofd_wq_mode)
|
||||
{
|
||||
struct gsmtap_inst *gti;
|
||||
int fd;
|
||||
|
||||
fd = gsmtap_source_init_fd(host, port);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
gti = talloc_zero(NULL, struct gsmtap_inst);
|
||||
gti->ofd_wq_mode = ofd_wq_mode;
|
||||
gti->wq.bfd.fd = fd;
|
||||
gti->sink_ofd.fd = -1;
|
||||
|
||||
if (ofd_wq_mode) {
|
||||
osmo_wqueue_init(>i->wq, 64);
|
||||
gti->wq.write_cb = &gsmtap_wq_w_cb;
|
||||
|
||||
osmo_fd_register(>i->wq.bfd);
|
||||
}
|
||||
|
||||
return gti;
|
||||
}
|
||||
|
||||
#endif /* HAVE_SYS_SELECT_H */
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
#include "../config.h"
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
|
||||
const char *host, uint16_t port, int connect0_bind1)
|
||||
{
|
||||
struct addrinfo hints, *result, *rp;
|
||||
int sfd, rc;
|
||||
char portbuf[16];
|
||||
|
||||
sprintf(portbuf, "%u", port);
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = family;
|
||||
hints.ai_socktype = type;
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_protocol = proto;
|
||||
|
||||
rc = getaddrinfo(host, portbuf, &hints, &result);
|
||||
if (rc != 0) {
|
||||
perror("getaddrinfo returned NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (sfd == -1)
|
||||
continue;
|
||||
if (connect0_bind1 == 0) {
|
||||
if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
|
||||
break;
|
||||
} else {
|
||||
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
|
||||
break;
|
||||
}
|
||||
close(sfd);
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
|
||||
if (rp == NULL) {
|
||||
perror("unable to connect/bind socket");
|
||||
return -ENODEV;
|
||||
}
|
||||
return sfd;
|
||||
}
|
||||
|
||||
int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
|
||||
uint8_t proto, int connect0_bind1)
|
||||
{
|
||||
char host[NI_MAXHOST];
|
||||
uint16_t port;
|
||||
struct sockaddr_in *sin;
|
||||
struct sockaddr_in6 *sin6;
|
||||
int s, sa_len;
|
||||
|
||||
/* determine port and host from ss */
|
||||
switch (ss->sa_family) {
|
||||
case AF_INET:
|
||||
sin = (struct sockaddr_in *) ss;
|
||||
sa_len = sizeof(struct sockaddr_in);
|
||||
port = ntohs(sin->sin_port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
sin6 = (struct sockaddr_in6 *) ss;
|
||||
sa_len = sizeof(struct sockaddr_in6);
|
||||
port = ntohs(sin6->sin6_port);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
fprintf(stderr, "==> PORT = %u\n", port);
|
||||
|
||||
s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
|
||||
NULL, 0, NI_NUMERICHOST);
|
||||
if (s != 0) {
|
||||
perror("getnameinfo failed");
|
||||
return s;
|
||||
}
|
||||
|
||||
return osmo_sock_init(ss->sa_family, type, proto, host,
|
||||
port, connect0_bind1);
|
||||
}
|
||||
|
||||
static int sockaddr_equal(const struct sockaddr *a,
|
||||
const struct sockaddr *b, socklen_t len)
|
||||
{
|
||||
struct sockaddr_in *sin_a, *sin_b;
|
||||
struct sockaddr_in6 *sin6_a, *sin6_b;
|
||||
|
||||
if (a->sa_family != b->sa_family)
|
||||
return 0;
|
||||
|
||||
switch (a->sa_family) {
|
||||
case AF_INET:
|
||||
sin_a = (struct sockaddr_in *)a;
|
||||
sin_b = (struct sockaddr_in *)b;
|
||||
if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
|
||||
sizeof(struct in_addr)))
|
||||
return 1;
|
||||
break;
|
||||
case AF_INET6:
|
||||
sin6_a = (struct sockaddr_in6 *)a;
|
||||
sin6_b = (struct sockaddr_in6 *)b;
|
||||
if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
|
||||
sizeof(struct in6_addr)))
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* determine if the given address is a local address */
|
||||
int osmo_sockaddr_is_local(struct sockaddr *addr, socklen_t addrlen)
|
||||
{
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
|
||||
if (getifaddrs(&ifaddr) == -1) {
|
||||
perror("getifaddrs");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue