diff options
author | Harald Welte <laforge@gnumonks.org> | 2011-05-21 18:54:32 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2011-05-22 11:43:23 +0200 |
commit | 33cb71ac91fb870702dbb71595dba4a554001e3c (patch) | |
tree | ba275acb6376eef01b8dc54a574fea6c2fe47443 | |
parent | 825607672215b7a12ea6e201a89cd5209f6d657f (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.am | 2 | ||||
-rw-r--r-- | include/osmocom/core/gsmtap_util.h | 46 | ||||
-rw-r--r-- | include/osmocom/core/socket.h | 16 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/gsmtap_util.c | 192 | ||||
-rw-r--r-- | src/socket.c | 143 |
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(>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 */ 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; +} |