summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2011-05-21 18:54:32 +0200
committerHarald Welte <laforge@gnumonks.org>2011-05-22 11:43:23 +0200
commit33cb71ac91fb870702dbb71595dba4a554001e3c (patch)
treeba275acb6376eef01b8dc54a574fea6c2fe47443
parent825607672215b7a12ea6e201a89cd5209f6d657f (diff)
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)
-rw-r--r--include/osmocom/core/Makefile.am2
-rw-r--r--include/osmocom/core/gsmtap_util.h46
-rw-r--r--include/osmocom/core/socket.h16
-rw-r--r--src/Makefile.am4
-rw-r--r--src/gsmtap_util.c192
-rw-r--r--src/socket.c143
6 files changed, 305 insertions, 98 deletions
diff --git a/include/osmocom/core/Makefile.am b/include/osmocom/core/Makefile.am
index 36988733..3c30362c 100644
--- a/include/osmocom/core/Makefile.am
+++ b/include/osmocom/core/Makefile.am
@@ -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 \
diff --git a/include/osmocom/core/gsmtap_util.h b/include/osmocom/core/gsmtap_util.h
index 785f5e58..f553c17a 100644
--- a/include/osmocom/core/gsmtap_util.h
+++ b/include/osmocom/core/gsmtap_util.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 */
diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h
new file mode 100644
index 00000000..3ede524b
--- /dev/null
+++ b/include/osmocom/core/socket.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 */
diff --git a/src/Makefile.am b/src/Makefile.am
index e58bc286..1ae3cff8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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
diff --git a/src/gsmtap_util.c b/src/gsmtap_util.c
index e0bc848c..15426358 100644
--- a/src/gsmtap_util.c
+++ b/src/gsmtap_util.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(&gti->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 = &gti->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(&gti->wq, 64);
+ gti->wq.write_cb = &gsmtap_wq_w_cb;
+
+ osmo_fd_register(&gti->wq.bfd);
+ }
+ return gti;
}
#endif /* HAVE_SYS_SELECT_H */
diff --git a/src/socket.c b/src/socket.c
new file mode 100644
index 00000000..bd4914fd
--- /dev/null
+++ b/src/socket.c
@@ -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;
+}