Merge commit '33cb71ac91fb870702dbb71595dba4a554001e3c'
authorHarald Welte <laforge@gnumonks.org>
Sun, 22 May 2011 10:07:37 +0000 (12:07 +0200)
committerHarald Welte <laforge@gnumonks.org>
Sun, 22 May 2011 10:07:37 +0000 (12:07 +0200)
src/shared/libosmocore/include/osmocom/core/Makefile.am
src/shared/libosmocore/include/osmocom/core/gsmtap_util.h
src/shared/libosmocore/include/osmocom/core/socket.h [new file with mode: 0644]
src/shared/libosmocore/src/Makefile.am
src/shared/libosmocore/src/gsmtap_util.c
src/shared/libosmocore/src/logging.c
src/shared/libosmocore/src/socket.c [new file with mode: 0644]

index 3698873..3c30362 100644 (file)
@@ -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 \
index 785f5e5..f553c17 100644 (file)
@@ -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/src/shared/libosmocore/include/osmocom/core/socket.h b/src/shared/libosmocore/include/osmocom/core/socket.h
new file mode 100644 (file)
index 0000000..3ede524
--- /dev/null
@@ -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 */
index e58bc28..1ae3cff 100644 (file)
@@ -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
index e0bc848..1542635 100644 (file)
@@ -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
  *
 
 #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>
 #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 */
index 0911010..3c9dc03 100644 (file)
@@ -198,6 +198,7 @@ static void _logp(unsigned int subsys, int level, char *file, int line,
        llist_for_each_entry(tar, &osmo_log_target_list, entry) {
                struct log_category *category;
                int output = 0;
+               va_list bp;
 
                category = &tar->categories[subsys];
                /* subsystem is not supposed to be logged */
@@ -224,7 +225,12 @@ static void _logp(unsigned int subsys, int level, char *file, int line,
                if (!output)
                        continue;
 
+               /* According to the manpage, vsnprintf leaves the value of ap
+                * in undefined state. Since _output uses vsnprintf and it may
+                * be called several times, we have to pass a copy of ap. */
+               va_copy(bp, ap);
                _output(tar, subsys, level, file, line, cont, format, ap);
+               va_end(bp);
        }
 }
 
diff --git a/src/shared/libosmocore/src/socket.c b/src/shared/libosmocore/src/socket.c
new file mode 100644 (file)
index 0000000..bd4914f
--- /dev/null
@@ -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;
+}