Hi openbsc,
the following patch series adds a GTP hub program (maturity level: proof-of-concept). Its aim is related to the recent addition of OAP: it will allow routing multiple SGSNs through one GTP server hub (proxy).
The program so far forwards UDP packets to and from hardcoded ports, and logs validity of GTP headers passing through. I think this a good time to do a review cycle and merge to master, before adding the interesting stuff.
What will follow: decode GTP body, map/separate PDP contexts across clients and servers, replace TEIs. To do that without reinventing the GTP wheel, I may need to separate data parsing from send/recv in openggsn.
There are also some bits that should probably be moved out (to where I copied it from) and the API there changed (so that I can use it here without copying). Those are clearly marked with todo comments.
Thanks for your review!
~Neels
First steps towards a new GTP hub. The aim is to mux GTP connections, so that multiple SGSN <--> GGSN links can pass through a single point. Background: allow having more than one SGSN, possibly in various remote locations.
The recent addition of OAP to GSUP is related to the same background idea.
Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/debug.h | 1 + openbsc/src/gprs/gtphub_main.c | 296 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 297 insertions(+) create mode 100644 openbsc/src/gprs/gtphub_main.c
diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h index 19d8fc2..189ca47 100644 --- a/openbsc/include/openbsc/debug.h +++ b/openbsc/include/openbsc/debug.h @@ -33,6 +33,7 @@ enum { DCTRL, DSMPP, DFILTER, + DGTPHUB, Debug_LastEntry, };
diff --git a/openbsc/src/gprs/gtphub_main.c b/openbsc/src/gprs/gtphub_main.c new file mode 100644 index 0000000..aa35952 --- /dev/null +++ b/openbsc/src/gprs/gtphub_main.c @@ -0,0 +1,296 @@ +/* GTP Hub main program */ + +/* (C) 2015 by sysmocom s.f.m.c. GmbH info@sysmocom.de + * All Rights Reserved + * + * Author: Neels Hofmeyr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <sys/socket.h> +#include <netinet/in.h> + +#define _GNU_SOURCE +#include <getopt.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/select.h> +#include <osmocom/core/logging.h> + +#include <openbsc/debug.h> + +#include <gtp.h> + +#include <unistd.h> + +#define LOGERR(fmt, args...) \ + LOGP(DGTPHUB, LOGL_ERROR, fmt, ##args) + +#define LOG(fmt, args...) \ + LOGP(DGTPHUB, LOGL_NOTICE, fmt, ##args) + +extern void *osmo_gtphub_ctx; + +/* TODO move to osmocom/core/socket.c ? */ +#include <netdb.h> +/* The caller is required to call freeaddrinfo(*result), iff zero is returned. */ +/* use this in osmo_sock_init() to remove dup. */ +static int _osmo_getaddrinfo(struct addrinfo **result, + uint16_t family, uint16_t type, uint8_t proto, + const char *host, uint16_t port) +{ + struct addrinfo hints; + char portbuf[16]; + + sprintf(portbuf, "%u", port); + memset(&hints, '\0', sizeof(struct addrinfo)); + hints.ai_family = family; + if (type == SOCK_RAW) { + /* Workaround for glibc, that returns EAI_SERVICE (-8) if + * SOCK_RAW and IPPROTO_GRE is used. + */ + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + } else { + hints.ai_socktype = type; + hints.ai_protocol = proto; + } + + return getaddrinfo(host, portbuf, &hints, result); +} + +/* TODO move to osmocom/core/socket.c ? */ +/*! \brief Initialize a sockaddr \param[out] addr valid sockaddr pointer to + * write result to \param[out] addr_len valid pointer to write addr length to + * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC + * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM \param[in] proto + * Protocol like IPPROTO_TCP, IPPROTO_UDP \param[in] host remote host name or + * IP address in string form \param[in] port remote port number in host byte + * order \returns 0 on success, otherwise an error code (from getaddrinfo()). + * + * Copy the first result from a getaddrinfo() call with the given parameters to + * *addr and *addr_len. On error, do not change *addr and return nonzero. + */ +int osmo_sockaddr_init(struct sockaddr_storage *addr, socklen_t *addr_len, + uint16_t family, uint16_t type, uint8_t proto, + const char *host, uint16_t port) +{ + struct addrinfo *res; + int rc; + rc = _osmo_getaddrinfo(&res, family, type, proto, host, port); + + if (rc != 0) { + LOGERR("getaddrinfo returned error %d\n", (int)rc); + return -EINVAL; + } + + OSMO_ASSERT(res->ai_addrlen <= sizeof(*addr)); + memcpy(addr, res->ai_addr, res->ai_addrlen); + *addr_len = res->ai_addrlen; + freeaddrinfo(res); + + return 0; +} + + +void *tall_bsc_ctx; + +const char *gtphub_copyright = + "Copyright (C) 2015 sysmocom s.m.f.c GmbH info@sysmocom.de\r\n" + "License AGPLv3+: GNU AGPL version 2 or later http://gnu.org/licenses/agpl-3.0.html\r\n" + "This is free software: you are free to change and redistribute it.\r\n" + "There is NO WARRANTY, to the extent permitted by law.\r\n"; + +static struct log_info_cat gtphub_categories[] = { + [DGTPHUB] = { + .name = "DGTPHUB", + .description = "GTP Hub", + .color = "\033[1;33m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, +}; + +int gtphub_log_filter_fn(const struct log_context *ctx, + struct log_target *tar) +{ + return 0; +} + +static const struct log_info gtphub_log_info = { + .filter_fn = gtphub_log_filter_fn, + .cat = gtphub_categories, + .num_cat = ARRAY_SIZE(gtphub_categories), +}; + +/* Recv datagram from from->fd, optionally write sender's address to *from_addr + * and *from_addr_len, parse datagram as GTP, and forward on to to->fd using + * *to_addr. to_addr may be NULL, if an address is set on to->fd. */ +int gtp_relay(struct osmo_fd *from, struct sockaddr_storage *from_addr, socklen_t *from_addr_len, + struct osmo_fd *to, struct sockaddr_storage *to_addr, socklen_t to_addr_len) +{ + static uint8_t buf[4096]; + + errno = 0; + ssize_t received = recvfrom(from->fd, buf, sizeof(buf), 0, + (struct sockaddr*)from_addr, from_addr_len); + + if (received <= 0) { + if (errno != EAGAIN) + LOGERR("error: %s\n", strerror(errno)); + return -errno; + } + + if (from_addr) { + LOG("from %s\n", osmo_hexdump((uint8_t*)from_addr, *from_addr_len)); + } + + if (received <= 0) { + LOGERR("error: %s\n", strerror(errno)); + return -EINVAL; + } + + /* insert magic here */ + + errno = 0; + ssize_t sent = sendto(to->fd, buf, received, 0, + (struct sockaddr*)to_addr, to_addr_len); + + if (to_addr) { + LOG("to %s\n", osmo_hexdump((uint8_t*)to_addr, to_addr_len)); + } + + if (sent == -1) { + LOGERR("error: %s\n", strerror(errno)); + return -EINVAL; + } + + if (sent != received) + LOGERR("sent(%d) != received(%d)\n", (int)sent, (int)received); + else + LOG("%d b ok\n", (int)sent); + + return 0; +} + +struct sockaddr_storage last_client_addr; +socklen_t last_client_addr_len; +struct sockaddr_storage server_addr; +socklen_t server_addr_len; + +int clients_read_cb(struct osmo_fd *clients_ofd, unsigned int what) +{ + LOG("reading from clients socket\n"); + struct osmo_fd *server_ofd = clients_ofd->data; + + if (!(what & BSC_FD_READ)) + return 0; + + last_client_addr_len = sizeof(last_client_addr); + return gtp_relay(clients_ofd, &last_client_addr, &last_client_addr_len, + server_ofd, &server_addr, server_addr_len); +} + +int server_read_cb(struct osmo_fd *server_ofd, unsigned int what) +{ + LOG("reading from server socket\n"); + struct osmo_fd *clients_ofd = server_ofd->data; + + if (!(what & BSC_FD_READ)) + return 0; + + return gtp_relay(server_ofd, NULL, NULL, + clients_ofd, &last_client_addr, last_client_addr_len); +} + +int main(int argc, char **argv) +{ + osmo_init_logging(>phub_log_info); + + int rc; + + const char* clients_addr_str = "localhost"; + uint16_t clients_port = 3386; + + const char* server_addr_str = "localhost"; + uint16_t server_port = 1234; + + /* Which local interface to use to listen for the GTP server's + * responses */ + const char* server_rx_addr_str = "localhost"; + uint16_t server_rx_port = 4321; + + rc = osmo_sockaddr_init(&server_addr, &server_addr_len, + AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, server_addr_str, server_port); + if (rc != 0) { + LOGERR("Cannot resolve '%s port %d'\n", server_addr_str, server_port); + exit(-1); + } + + struct osmo_fd clients_ofd; + struct osmo_fd server_ofd; + + memset(&clients_ofd, 0, sizeof(clients_ofd)); + clients_ofd.when = BSC_FD_READ; + clients_ofd.cb = clients_read_cb; + clients_ofd.data = &server_ofd; + + rc = osmo_sock_init_ofd(&clients_ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, clients_addr_str, clients_port, OSMO_SOCK_F_BIND); + if (rc < 1) { + LOGERR("Cannot bind to %s port %d\n", clients_addr_str, clients_port); + exit(-1); + } + + memset(&server_ofd, 0, sizeof(server_ofd)); + server_ofd.when = BSC_FD_READ; + server_ofd.cb = server_read_cb; + server_ofd.data = &clients_ofd; + + rc = osmo_sock_init_ofd(&server_ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, server_rx_addr_str, server_rx_port, OSMO_SOCK_F_BIND); + if (rc < 1) { + LOGERR("Cannot bind to %s port %d\n", server_rx_addr_str, server_rx_port); + exit(-1); + } + + LOG("GTP server connection: %s port %d <--> %s port %d\n", + server_rx_addr_str, (int)server_rx_port, + server_addr_str, (int)server_port); + LOG("Listening for clients on %s port %d.\n", clients_addr_str, clients_port); + + int daemonize = 0; + + if (daemonize) { + rc = osmo_daemonize(); + if (rc < 0) { + LOGERR("Error during daemonize"); + exit(1); + } + } + + while (1) { + rc = osmo_select_main(0); + if (rc < 0) + exit(3); + } + + /* not reached */ + exit(0); +}
On 07 Oct 2015, at 16:18, Neels Hofmeyr nhofmeyr@sysmocom.de wrote:
+/* TODO move to osmocom/core/socket.c ? */
where did you take this from?
+/* TODO move to osmocom/core/socket.c ? */
+int osmo_sockaddr_init(struct sockaddr_storage *addr, socklen_t *addr_len,
uint16_t family, uint16_t type, uint8_t proto,const char *host, uint16_t port)+{
ah yeah. the by hostname is a good idea. But this should only be used at the beginning of an application as we don't want to block on a DNS query.
+static struct log_info_cat gtphub_categories[] = {
+static const struct log_info gtphub_log_info = {
- .filter_fn = gtphub_log_filter_fn,
- .cat = gtphub_categories,
- .num_cat = ARRAY_SIZE(gtphub_categories),
+};
nothing else will be used as debug log? Depending on the answer it might be better to just always initialize the GTPHUB area..
+int gtp_relay(struct osmo_fd *from, struct sockaddr_storage *from_addr, socklen_t *from_addr_len,
struct osmo_fd *to, struct sockaddr_storage *to_addr, socklen_t to_addr_len)+{
- static uint8_t buf[4096];
- errno = 0;
- ssize_t received = recvfrom(from->fd, buf, sizeof(buf), 0,
(struct sockaddr*)from_addr, from_addr_len);
When the first gbproxy was built our buffer was smaller and we truncated messages. Now 4k is pretty big and bigger than our expectation for a UDP message. If you use recvmsg then the msg_flags will have MSG_TRUNC set or not. Not sure if you want to use it.
- rc = osmo_sock_init_ofd(&clients_ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, clients_addr_str, clients_port, OSMO_SOCK_F_BIND);
- rc = osmo_sock_init_ofd(&server_ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, server_rx_addr_str, server_rx_port, OSMO_SOCK_F_BIND);
Who is the server? who is the client? Maybe give it names to what we expect on the side? One is the SGSN, the other is the GGSN?
On Thu, Oct 08, 2015 at 10:48:07AM +0200, Holger Freyther wrote:
On 07 Oct 2015, at 16:18, Neels Hofmeyr nhofmeyr@sysmocom.de wrote:
+/* TODO move to osmocom/core/socket.c ? */
where did you take this from?
basically the beginning of osmo_sock_init()
+/* TODO move to osmocom/core/socket.c ? */
+int osmo_sockaddr_init(struct sockaddr_storage *addr, socklen_t *addr_len,
uint16_t family, uint16_t type, uint8_t proto,const char *host, uint16_t port)+{
ah yeah. the by hostname is a good idea. But this should only be used at the beginning of an application as we don't want to block on a DNS query.
In fact, I use this function to hardcode the GTP server. We will in future fetch the GTP server by DNS, and I'm not entirely clear on whether getaddrinfo() can resolve that. If we need a full res_query() instead, the gtphub will no longer need this function, and its use is reduced to a courtesy to future API users...
+static struct log_info_cat gtphub_categories[] = {
+static const struct log_info gtphub_log_info = {
- .filter_fn = gtphub_log_filter_fn,
- .cat = gtphub_categories,
- .num_cat = ARRAY_SIZE(gtphub_categories),
+};
nothing else will be used as debug log? Depending on the answer it might be better to just always initialize the GTPHUB area..
I don't fully see thru the debug setup yet. Other binaries around seem to re-create very similar category lists, apparently just listing all that is defined. I tried to find the minimum first. Others may be added as needed.
What do you mean by
better to just always initialize the GTPHUB area..
? IIUC that's what the patch is doing right now?
If you use recvmsg then the msg_flags will have MSG_TRUNC set or not. Not sure if you want to use it.
I'll put a TODO comment there. The 4kb buf should suffice for now. Thanks for the hint! Wasn't aware.
- rc = osmo_sock_init_ofd(&clients_ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, clients_addr_str, clients_port, OSMO_SOCK_F_BIND);
- rc = osmo_sock_init_ofd(&server_ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, server_rx_addr_str, server_rx_port, OSMO_SOCK_F_BIND);
Who is the server? who is the client? Maybe give it names to what we expect on the side? One is the SGSN, the other is the GGSN?
I thought of a GTP server == GGSN and GTP client == SGSN. If that's ambiguous at all I'll be happy to change it, e.g. to to_sgsns, to_ggsns.
[SGSN] <---> [to_sgsns GTPHUB to_ggsns] <---> [GGSN]
With the "to_" prefix to avoid this ambiguity: if the SGSN contacts the gtphub, the gtphub looks like a GGSN, so a confused reader could think of the "to_sgsns" port as a GGSN. Or rather a "from_" prefix? :P
~Neels
Hi Neels,
On Wed, Oct 07, 2015 at 04:18:19PM +0200, Neels Hofmeyr wrote:
+#define LOGERR(fmt, args...) \
- LOGP(DGTPHUB, LOGL_ERROR, fmt, ##args)
+#define LOG(fmt, args...) \
- LOGP(DGTPHUB, LOGL_NOTICE, fmt, ##args)
we typically don't do this in the Osmo* codebase,bt use the full statement. However, it is your choice.
The problem I see with this is that you reduce/remove the flexibility of using multiplelog subsstems and levels. You more or less assume that there will always only be two log levels used, and all messages will use the same subsystem. Is that really the case?
When the full statement is used everytime, it is also easier to later change / tune the logging levels of certain statements, which is typically what happens after you run it in production with thousands of messages flowing through and you see what kind of granularity makes sense.
Also, I think in patch 10/10 you state 'add debug messages' but then use the LOG() macro. So in fact you are adding NOTICE messages, not DEBUG messages. So either of the two statements is wrong.
On Thu, Oct 08, 2015 at 05:00:08PM +0200, Harald Welte wrote:
Hi Neels,
On Wed, Oct 07, 2015 at 04:18:19PM +0200, Neels Hofmeyr wrote:
+#define LOGERR(fmt, args...) \
- LOGP(DGTPHUB, LOGL_ERROR, fmt, ##args)
+#define LOG(fmt, args...) \
- LOGP(DGTPHUB, LOGL_NOTICE, fmt, ##args)
we typically don't do this in the Osmo* codebase,bt use the full statement. However, it is your choice.
The problem I see with this is that you reduce/remove the flexibility of using multiplelog subsstems and levels. You more or less assume that there will always only be two log levels used, and all messages will use the same subsystem. Is that really the case?
It's intended as a per-C-file local definition just to reduce repetition. If a file needs another log level (more than once), I intend to create another LOGxxx #define; but never in an h. LOGP() is also available.
When the full statement is used everytime, it is also easier to later change / tune the logging levels of certain statements, which is typically what happens after you run it in production with thousands of messages flowing through and you see what kind of granularity makes sense.
ok, I see. I'll drop them as I go, then.
Also, I think in patch 10/10 you state 'add debug messages' but then use the LOG() macro. So in fact you are adding NOTICE messages, not DEBUG messages. So either of the two statements is wrong.
That's right. Most current LOG()s are actually not intended to live past the commit that concludes GTP transcoding, fairly soon. That's what I meant by debug, really.
Thanks!
~Neels
Add program osmo-gtphub from gtphub_main.c to Makefile.am. Add osmo-gtphub binary to gitignore.
Sponsored-by: On-Waves ehf --- openbsc/.gitignore | 1 + openbsc/src/gprs/Makefile.am | 4 ++++ 2 files changed, 5 insertions(+)
diff --git a/openbsc/.gitignore b/openbsc/.gitignore index 2210c47..f024d76 100644 --- a/openbsc/.gitignore +++ b/openbsc/.gitignore @@ -53,6 +53,7 @@ src/utils/isdnsync src/nat/bsc_nat src/gprs/osmo-sgsn src/gprs/osmo-gbproxy +src/gprs/osmo-gtphub src/osmo-bsc_nat/osmo-bsc_nat
#tests diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index f46a402..753a073 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -11,6 +11,7 @@ noinst_HEADERS = gprs_sndcp.h bin_PROGRAMS = osmo-gbproxy
if HAVE_LIBGTP +bin_PROGRAMS += osmo-gtphub if HAVE_LIBCARES bin_PROGRAMS += osmo-sgsn endif @@ -31,3 +32,6 @@ osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c gprs_sndcp.c gprs_sndcp_vty.c \ osmo_sgsn_LDADD = \ $(top_builddir)/src/libcommon/libcommon.a \ -lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) $(LIBCARES_LIBS) -lrt + +osmo_gtphub_SOURCES = gtphub_main.c +osmo_gtphub_LDADD = $(LIBOSMOCORE_LIBS) -lrt
Tweak the hardcoded default ports/interfaces for the test.
Sponsored-by: On-Waves ehf --- openbsc/configure.ac | 1 + openbsc/src/gprs/gtphub_main.c | 14 ++++---- openbsc/tests/Makefile.am | 2 +- openbsc/tests/gtphub/Makefile.am | 3 ++ openbsc/tests/gtphub/gtphub_nc_test.ok | 7 ++++ openbsc/tests/gtphub/gtphub_nc_test.sh | 58 ++++++++++++++++++++++++++++++++++ openbsc/tests/testsuite.at | 7 ++++ 7 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 openbsc/tests/gtphub/Makefile.am create mode 100644 openbsc/tests/gtphub/gtphub_nc_test.ok create mode 100755 openbsc/tests/gtphub/gtphub_nc_test.sh
diff --git a/openbsc/configure.ac b/openbsc/configure.ac index 78302dd..7809feb 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -209,6 +209,7 @@ AC_OUTPUT( tests/trau/Makefile tests/sgsn/Makefile tests/subscr/Makefile + tests/gtphub/Makefile doc/Makefile doc/examples/Makefile Makefile) diff --git a/openbsc/src/gprs/gtphub_main.c b/openbsc/src/gprs/gtphub_main.c index aa35952..f8bcd59 100644 --- a/openbsc/src/gprs/gtphub_main.c +++ b/openbsc/src/gprs/gtphub_main.c @@ -227,16 +227,18 @@ int main(int argc, char **argv)
int rc;
- const char* clients_addr_str = "localhost"; - uint16_t clients_port = 3386; + /* Which local interface to use to listen for GTP clients */ + const char* clients_addr_str = "127.0.0.3"; + uint16_t clients_port = 2123;
- const char* server_addr_str = "localhost"; - uint16_t server_port = 1234; + /* Where the GTP server sits that we're relaying for */ + const char* server_addr_str = "127.0.0.2"; + uint16_t server_port = 2123;
/* Which local interface to use to listen for the GTP server's * responses */ - const char* server_rx_addr_str = "localhost"; - uint16_t server_rx_port = 4321; + const char* server_rx_addr_str = "127.0.0.4"; + uint16_t server_rx_port = 2123;
rc = osmo_sockaddr_init(&server_addr, &server_addr_len, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, server_addr_str, server_port); diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am index 773830b..7dda124 100644 --- a/openbsc/tests/Makefile.am +++ b/openbsc/tests/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = gsm0408 db channel mgcp gprs abis gbproxy trau subscr +SUBDIRS = gsm0408 db channel mgcp gprs abis gbproxy trau subscr gtphub
if BUILD_NAT SUBDIRS += bsc-nat bsc-nat-trie diff --git a/openbsc/tests/gtphub/Makefile.am b/openbsc/tests/gtphub/Makefile.am new file mode 100644 index 0000000..d53c19d --- /dev/null +++ b/openbsc/tests/gtphub/Makefile.am @@ -0,0 +1,3 @@ +EXTRA_DIST = \ + gtphub_nc_test.sh \ + gtphub_nc_test.ok diff --git a/openbsc/tests/gtphub/gtphub_nc_test.ok b/openbsc/tests/gtphub/gtphub_nc_test.ok new file mode 100644 index 0000000..e4f641b --- /dev/null +++ b/openbsc/tests/gtphub/gtphub_nc_test.ok @@ -0,0 +1,7 @@ +--- recv_server: +[msg 1: client to server] +OK +--- recv_client: +[msg 2: server to client] +OK +done diff --git a/openbsc/tests/gtphub/gtphub_nc_test.sh b/openbsc/tests/gtphub/gtphub_nc_test.sh new file mode 100755 index 0000000..179bad7 --- /dev/null +++ b/openbsc/tests/gtphub/gtphub_nc_test.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# gtphub_nc_test.sh + +# TODO does this work with all relevant netcat implementations? +# TODO skip if netcat not found? + +gtphub_bin="$1" +if [ ! -x "$gtphub_bin" ]; then + echo "executable not found: $gtphub_bin" + exit 1; +fi + +# client osmo-gtphub gtp server +# 127.0.0.1:9876 <--> 127.0.0.3:2123 | 127.0.0.4:2123 <--> 127.0.0.2 2123 +# (netcat) ($gtphub_bin) (netcat) + +# start gtphub relay +"$gtphub_bin" & +sleep 0.1 + +# log what reaches client and server +nc --recv-only -u -l -p 9876 -s 127.0.0.1 > recv_client & +nc --recv-only -u -l -p 2123 -s 127.0.0.2 > recv_server & +sleep .1 + +# send test messages, both ways +msg1="[msg 1: client to server]" +echo "$msg1" | nc --send-only -u -s 127.0.0.1 -p 9876 127.0.0.3 2123 + +msg2="[msg 2: server to client]" +echo "$msg2" | nc --send-only -u -s 127.0.0.2 -p 2123 127.0.0.4 2123 + +sleep .1 +kill %1 %2 %3 + +# log what has reached the server and client ends, matched against +# gtphub_nc_test.ok +retval=0 +echo "--- recv_server:" +cat recv_server +if [ "$(cat recv_server)" == "$msg1" ]; then + echo "OK" +else + echo "*** FAILURE" + retval=1 +fi + +echo "--- recv_client:" +cat recv_client +if [ "$(cat recv_client)" == "$msg2" ]; then + echo "OK" +else + echo "*** FAILURE" + retval=2 +fi + +echo "done" +exit "$retval" diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at index 74aaef0..14d1f72 100644 --- a/openbsc/tests/testsuite.at +++ b/openbsc/tests/testsuite.at @@ -103,3 +103,10 @@ AT_CHECK([test "$enable_sgsn_test" != no || exit 77]) cat $abs_srcdir/sgsn/sgsn_test.ok > expout AT_CHECK([$abs_top_builddir/tests/sgsn/sgsn_test], [], [expout], [ignore]) AT_CLEANUP + +AT_SETUP([gtphub]) +AT_KEYWORDS([gtphub]) +AT_CHECK([test "$enable_gtphub_test" != no || exit 77]) +cat $abs_srcdir/gtphub/gtphub_nc_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/gtphub/gtphub_nc_test.sh $abs_top_builddir/src/gprs/osmo-gtphub], [], [expout], [ignore]) +AT_CLEANUP
On 07 Oct 2015, at 16:18, Neels Hofmeyr nhofmeyr@sysmocom.de wrote:
- const char* clients_addr_str = "localhost";
- uint16_t clients_port = 3386;
- /* Which local interface to use to listen for GTP clients */
- const char* clients_addr_str = "127.0.0.3";
- uint16_t clients_port = 2123;
- const char* server_addr_str = "localhost";
- uint16_t server_port = 1234;
/* Where the GTP server sits that we're relaying for */
const char* server_addr_str = "127.0.0.2";
uint16_t server_port = 2123;
/* Which local interface to use to listen for the GTP server's
- responses */
- const char* server_rx_addr_str = "localhost";
- uint16_t server_rx_port = 4321;
- const char* server_rx_addr_str = "127.0.0.4";
- uint16_t server_rx_port = 2123;
this won't work on FreeBSD (unless you add the ip addresses by hand). Could you use different ports?
holger
On Thu, Oct 08, 2015 at 10:50:48AM +0200, Holger Freyther wrote:
this won't work on FreeBSD (unless you add the ip addresses by hand). Could you use different ports?
The reason why I picked these: I want to put the osmo-gtphub binary between an openggsn and an sgsnemu, without changing their configuration. I did that and it works :)
To keep this working, let's change the ports in the test once the config interface (vty and/or cmdline args) is in place. Then the test can configure various ports on 127.0.0.1.
~Neels
Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/Makefile.am | 3 +- openbsc/include/openbsc/gtphub.h | 98 +++++++++++++++++++++++++++++++++++++ openbsc/src/gprs/Makefile.am | 2 +- openbsc/src/gprs/gtphub.c | 45 +++++++++++++++++ 4 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 openbsc/include/openbsc/gtphub.h create mode 100644 openbsc/src/gprs/gtphub.c
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index 254f43d..c5dd6af 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -16,7 +16,8 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \ arfcn_range_encode.h nat_rewrite_trie.h bsc_nat_callstats.h \ osmux.h mgcp_transcode.h gprs_utils.h \ gprs_gb_parse.h smpp.h meas_feed.h gprs_gsup_messages.h \ - gprs_gsup_client.h bsc_msg_filter.h + gprs_gsup_client.h bsc_msg_filter.h \ + gtphub.h
openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h openbscdir = $(includedir)/openbsc diff --git a/openbsc/include/openbsc/gtphub.h b/openbsc/include/openbsc/gtphub.h new file mode 100644 index 0000000..cd4c215 --- /dev/null +++ b/openbsc/include/openbsc/gtphub.h @@ -0,0 +1,98 @@ +/* GTP Hub Implementation */ + +/* (C) 2015 by sysmocom s.f.m.c. GmbH info@sysmocom.de + * All Rights Reserved + * + * Author: Neels Hofmeyr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#pragma once + +#include <stdint.h> +#include <sys/socket.h> + +#include <osmocom/core/select.h> + + +/* general */ + +enum gtphub_port_idx { + GTPH_PORT_CONTROL = 0, + GTPH_PORT_USER = 1, + GTPH_PORT_N +}; + +extern const char* const gtphub_port_idx_names[GTPH_PORT_N]; + + +/* config */ + +struct gtphub_cfg_addr { + const char *addr_str; + uint16_t port; +}; + +struct gtphub_cfg_bind { + struct gtphub_cfg_addr bind; +}; + +struct gtphub_cfg { + struct gtphub_cfg_bind to_clients[GTPH_PORT_N]; + struct gtphub_cfg_bind to_servers[GTPH_PORT_N]; +}; + + +/* state */ + +struct gtphub; + +struct gtphub_addr { + struct sockaddr_storage a; + socklen_t l; +}; + +struct gtphub_peer { + struct llist_head entry; + + struct gtphub_addr addr; +}; + +struct gtphub_bind { + struct osmo_fd ofd; + + /* list of struct gtphub_peer */ + struct llist_head peers; +}; + +struct gtphub { + struct gtphub_bind to_clients[GTPH_PORT_N]; + struct gtphub_bind to_servers[GTPH_PORT_N]; +}; + + +/* api */ + +void gtphub_zero(struct gtphub *hub); +int gtphub_init(struct gtphub *hub, struct gtphub_cfg *cfg); + +/* Create a new gtphub_peer instance added to peers_list. + * Initialize to all-zero. Return a pointer to the new instance, or NULL on + * error. */ +struct gtphub_peer *gtphub_peer_new(struct gtphub_bind *bind); + +/* Remove a gtphub_peer from its list and free it. */ +void gtphub_peer_del(struct gtphub_peer *peer); + diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index 753a073..9053766 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -33,5 +33,5 @@ osmo_sgsn_LDADD = \ $(top_builddir)/src/libcommon/libcommon.a \ -lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) $(LIBCARES_LIBS) -lrt
-osmo_gtphub_SOURCES = gtphub_main.c +osmo_gtphub_SOURCES = gtphub_main.c gtphub.c osmo_gtphub_LDADD = $(LIBOSMOCORE_LIBS) -lrt diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c new file mode 100644 index 0000000..ffe0d4a --- /dev/null +++ b/openbsc/src/gprs/gtphub.c @@ -0,0 +1,45 @@ +/* GTP Hub Implementation */ + +/* (C) 2015 by sysmocom s.f.m.c. GmbH info@sysmocom.de + * All Rights Reserved + * + * Author: Neels Hofmeyr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include <string.h> + +#include <openbsc/gtphub.h> +#include <openbsc/debug.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> + +void *osmo_gtphub_ctx; + +#define LOGERR(fmt, args...) \ + LOGP(DGTPHUB, LOGL_ERROR, fmt, ##args) + +void gtphub_zero(struct gtphub *hub) +{ + memset(hub, '\0', sizeof(*hub)); +} + +int gtphub_init(struct gtphub *hub, struct gtphub_cfg *cfg) +{ + LOGERR("%s not implemented\n", __func__); + return -1; +} +
Sponsored-by: On-Waves ehi --- openbsc/src/gprs/gtphub.c | 209 ++++++++++++++++++++++++++++++++++++++++- openbsc/src/gprs/gtphub_main.c | 183 +++++++++++------------------------- 2 files changed, 261 insertions(+), 131 deletions(-)
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index ffe0d4a..a8bd276 100644 --- a/openbsc/src/gprs/gtphub.c +++ b/openbsc/src/gprs/gtphub.c @@ -20,26 +20,231 @@ */
#include <string.h> +#include <errno.h> +#include <netinet/in.h>
#include <openbsc/gtphub.h> #include <openbsc/debug.h>
#include <osmocom/core/utils.h> #include <osmocom/core/logging.h> +#include <osmocom/core/socket.h>
void *osmo_gtphub_ctx;
#define LOGERR(fmt, args...) \ LOGP(DGTPHUB, LOGL_ERROR, fmt, ##args)
+#define LOG(fmt, args...) \ + LOGP(DGTPHUB, LOGL_NOTICE, fmt, ##args) + +/* TODO move this to osmocom/core/select.h ? */ +typedef int (*osmo_fd_cb_t)(struct osmo_fd *fd, unsigned int what); + +/* TODO move this to osmocom/core/linuxlist.h ? */ +#define __llist_first(head) (((head)->next == (head)) ? NULL : (head)->next) +#define llist_first(head, type, entry) llist_entry(__llist_first(head), type, entry) + + +/* general */ + +const char* const gtphub_port_idx_names[GTPH_PORT_N] = { + "CTRL", + "USER", +}; + + +/* gtphub */ + void gtphub_zero(struct gtphub *hub) { memset(hub, '\0', sizeof(*hub)); }
+static int gtphub_sock_init(struct osmo_fd *ofd, + const struct gtphub_cfg_addr *addr, + osmo_fd_cb_t cb, + void *data, + int ofd_id) +{ + ofd->when = BSC_FD_READ; + ofd->cb = cb; + ofd->data = data; + ofd->priv_nr = ofd_id; + + int rc; + rc = osmo_sock_init_ofd(ofd, + AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, + addr->addr_str, addr->port, + OSMO_SOCK_F_BIND); + if (rc < 1) { + LOGERR("Cannot bind to %s port %d (rc %d)\n", + addr->addr_str, (int)addr->port, rc); + return -1; + } + + return 0; +} + +static int gtphub_gtp_bind_init(struct gtphub_bind *b, + const struct gtphub_cfg_bind *cfg, + osmo_fd_cb_t cb, void *cb_data, + unsigned int ofd_id) +{ + memset(b, '\0', sizeof(*b)); + + INIT_LLIST_HEAD(&b->peers); + + if (gtphub_sock_init(&b->ofd, &cfg->bind, cb, cb_data, ofd_id) != 0) + return -1; + return 0; +} + +/* Recv datagram from from->fd, optionally write sender's address to *from_addr + * and *from_addr_len, parse datagram as GTP, and forward on to to->fd using + * *to_addr. to_addr may be NULL, if an address is set on to->fd. */ +static int gtp_relay(struct osmo_fd *from, + struct sockaddr_storage *from_addr, + socklen_t *from_addr_len, + struct osmo_fd *to, + struct sockaddr_storage *to_addr, + socklen_t to_addr_len) +{ + static uint8_t buf[4096]; + + /* recvfrom requires the available length to be set in *from_addr_len. */ + if (from_addr_len && from_addr) + *from_addr_len = sizeof(*from_addr); + + errno = 0; + ssize_t received = recvfrom(from->fd, buf, sizeof(buf), 0, + (struct sockaddr*)from_addr, from_addr_len); + + if (received <= 0) { + if (errno != EAGAIN) + LOGERR("error: %s\n", strerror(errno)); + return -errno; + } + + if (from_addr) { + LOG("from %s\n", osmo_hexdump((uint8_t*)from_addr, *from_addr_len)); + } + + if (received <= 0) { + LOGERR("error: %s\n", strerror(errno)); + return -EINVAL; + } + + /* insert magic here */ + + errno = 0; + ssize_t sent = sendto(to->fd, buf, received, 0, + (struct sockaddr*)to_addr, to_addr_len); + + if (to_addr) { + LOG("to %s\n", osmo_hexdump((uint8_t*)to_addr, to_addr_len)); + } + + if (sent == -1) { + LOGERR("error: %s\n", strerror(errno)); + return -EINVAL; + } + + if (sent != received) + LOGERR("sent(%d) != received(%d)\n", (int)sent, (int)received); + else + LOG("%d b ok\n", (int)sent); + + return 0; +} + +int servers_read_cb(struct osmo_fd *servers_ofd, unsigned int what) +{ + unsigned int port_idx = servers_ofd->priv_nr; + OSMO_ASSERT(port_idx < GTPH_PORT_N); + LOG("reading from servers socket (%s)\n", gtphub_port_idx_names[port_idx]); + if (!(what & BSC_FD_READ)) + return 0; + + struct gtphub *hub = servers_ofd->data; + + /* TODO this will not be hardcoded. */ + struct gtphub_peer *client = llist_first(&hub->to_clients[port_idx].peers, + struct gtphub_peer, entry); + if (!client) { + LOGERR("no client"); + return 0; + } + + return gtp_relay(servers_ofd, NULL, NULL, + &hub->to_clients[port_idx].ofd, + &client->addr.a, client->addr.l); +} + +int clients_read_cb(struct osmo_fd *clients_ofd, unsigned int what) +{ + unsigned int port_idx = clients_ofd->priv_nr; + OSMO_ASSERT(port_idx < GTPH_PORT_N); + LOG("reading from clients socket (%s)\n", gtphub_port_idx_names[port_idx]); + + if (!(what & BSC_FD_READ)) + return 0; + + struct gtphub *hub = clients_ofd->data; + + /* TODO this will not be hardcoded. */ + /* so far just remembering the last client */ + struct gtphub_peer *server = llist_first(&hub->to_servers[port_idx].peers, + struct gtphub_peer, entry); + if (!server) { + LOGERR("no server to send to\n"); + return 0; + } + + struct gtphub_peer *client = llist_first(&hub->to_clients[port_idx].peers, + struct gtphub_peer, entry); + if (!client) + client = gtphub_peer_new(&hub->to_clients[port_idx]); + + return gtp_relay(clients_ofd, &client->addr.a, &client->addr.l, + &hub->to_servers[port_idx].ofd, + &server->addr.a, server->addr.l); +} + int gtphub_init(struct gtphub *hub, struct gtphub_cfg *cfg) { - LOGERR("%s not implemented\n", __func__); - return -1; + gtphub_zero(hub); + + int port_id; + for (port_id = 0; port_id < GTPH_PORT_N; port_id++) { + int rc; + rc = gtphub_gtp_bind_init(&hub->to_servers[port_id], + &cfg->to_servers[port_id], + servers_read_cb, hub, port_id); + if (rc < 0) + return rc; + + rc = gtphub_gtp_bind_init(&hub->to_clients[port_id], + &cfg->to_clients[port_id], + clients_read_cb, hub, port_id); + if (rc < 0) + return rc; + + /* ... */ + } + return 0; +} + +struct gtphub_peer *gtphub_peer_new(struct gtphub_bind *bind) +{ + struct gtphub_peer *n = talloc_zero(osmo_gtphub_ctx, struct gtphub_peer); + llist_add(&n->entry, &bind->peers); + return n; +} + +void gtphub_peer_del(struct gtphub_peer *peer) +{ + llist_del(&peer->entry); + talloc_free(peer); }
diff --git a/openbsc/src/gprs/gtphub_main.c b/openbsc/src/gprs/gtphub_main.c index f8bcd59..104cbfd 100644 --- a/openbsc/src/gprs/gtphub_main.c +++ b/openbsc/src/gprs/gtphub_main.c @@ -20,27 +20,17 @@ */
#include <string.h> -#include <stdio.h> -#include <stdlib.h> #include <errno.h>
-#include <sys/socket.h> -#include <netinet/in.h> - #define _GNU_SOURCE #include <getopt.h>
#include <osmocom/core/application.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/socket.h> -#include <osmocom/core/select.h> #include <osmocom/core/logging.h> +#include <osmocom/core/utils.h>
#include <openbsc/debug.h> - -#include <gtp.h> - -#include <unistd.h> +#include <openbsc/gtphub.h>
#define LOGERR(fmt, args...) \ LOGP(DGTPHUB, LOGL_ERROR, fmt, ##args) @@ -112,7 +102,6 @@ int osmo_sockaddr_init(struct sockaddr_storage *addr, socklen_t *addr_len, }
-void *tall_bsc_ctx;
const char *gtphub_copyright = "Copyright (C) 2015 sysmocom s.m.f.c GmbH info@sysmocom.de\r\n" @@ -141,141 +130,77 @@ static const struct log_info gtphub_log_info = { .num_cat = ARRAY_SIZE(gtphub_categories), };
-/* Recv datagram from from->fd, optionally write sender's address to *from_addr - * and *from_addr_len, parse datagram as GTP, and forward on to to->fd using - * *to_addr. to_addr may be NULL, if an address is set on to->fd. */ -int gtp_relay(struct osmo_fd *from, struct sockaddr_storage *from_addr, socklen_t *from_addr_len, - struct osmo_fd *to, struct sockaddr_storage *to_addr, socklen_t to_addr_len) +void log_cfg(struct gtphub_cfg *cfg) { - static uint8_t buf[4096]; - - errno = 0; - ssize_t received = recvfrom(from->fd, buf, sizeof(buf), 0, - (struct sockaddr*)from_addr, from_addr_len); - - if (received <= 0) { - if (errno != EAGAIN) - LOGERR("error: %s\n", strerror(errno)); - return -errno; - } - - if (from_addr) { - LOG("from %s\n", osmo_hexdump((uint8_t*)from_addr, *from_addr_len)); - } - - if (received <= 0) { - LOGERR("error: %s\n", strerror(errno)); - return -EINVAL; - } - - /* insert magic here */ - - errno = 0; - ssize_t sent = sendto(to->fd, buf, received, 0, - (struct sockaddr*)to_addr, to_addr_len); - - if (to_addr) { - LOG("to %s\n", osmo_hexdump((uint8_t*)to_addr, to_addr_len)); - } - - if (sent == -1) { - LOGERR("error: %s\n", strerror(errno)); - return -EINVAL; - } - - if (sent != received) - LOGERR("sent(%d) != received(%d)\n", (int)sent, (int)received); - else - LOG("%d b ok\n", (int)sent); - - return 0; + struct gtphub_cfg_addr *a; + a = &cfg->to_clients[GTPH_PORT_CONTROL].bind; + LOG("Clients bind, Control: %s port %d\n", + a->addr_str, a->port); + a = &cfg->to_clients[GTPH_PORT_USER].bind; + LOG("Clients bind, User: %s port %d\n", + a->addr_str, a->port); + a = &cfg->to_servers[GTPH_PORT_CONTROL].bind; + LOG("GTP server bind, Control: %s port %d\n", + a->addr_str, a->port); + a = &cfg->to_servers[GTPH_PORT_USER].bind; + LOG("GTP server bind, User: %s port %d\n", + a->addr_str, a->port); }
-struct sockaddr_storage last_client_addr; -socklen_t last_client_addr_len; -struct sockaddr_storage server_addr; -socklen_t server_addr_len; - -int clients_read_cb(struct osmo_fd *clients_ofd, unsigned int what) +int main(int argc, char **argv) { - LOG("reading from clients socket\n"); - struct osmo_fd *server_ofd = clients_ofd->data; + osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub");
- if (!(what & BSC_FD_READ)) - return 0; - - last_client_addr_len = sizeof(last_client_addr); - return gtp_relay(clients_ofd, &last_client_addr, &last_client_addr_len, - server_ofd, &server_addr, server_addr_len); -} - -int server_read_cb(struct osmo_fd *server_ofd, unsigned int what) -{ - LOG("reading from server socket\n"); - struct osmo_fd *clients_ofd = server_ofd->data; + osmo_init_logging(>phub_log_info);
- if (!(what & BSC_FD_READ)) - return 0; + int rc;
- return gtp_relay(server_ofd, NULL, NULL, - clients_ofd, &last_client_addr, last_client_addr_len); -} + struct gtphub_cfg _cfg = { + .to_clients = { + { .bind = { + .addr_str = "127.0.0.3", + .port = 2123, + } }, + { .bind = { + .addr_str = "127.0.0.3", + .port = 2152, + } }, + }, + .to_servers = { + { .bind = { + .addr_str = "127.0.0.4", + .port = 2123, + } }, + { .bind = { + .addr_str = "127.0.0.4", + .port = 2152, + } }, + }, + };
-int main(int argc, char **argv) -{ - osmo_init_logging(>phub_log_info); + struct gtphub_cfg *cfg = &_cfg;
- int rc; + struct gtphub _hub; + struct gtphub *hub = &_hub;
- /* Which local interface to use to listen for GTP clients */ - const char* clients_addr_str = "127.0.0.3"; - uint16_t clients_port = 2123; + if (gtphub_init(hub, cfg) != 0) + return -1;
+ /* TODO this will not be configured, gtphub will have to find the + * servers from incoming GTP PDUs. */ /* Where the GTP server sits that we're relaying for */ const char* server_addr_str = "127.0.0.2"; uint16_t server_port = 2123; - - /* Which local interface to use to listen for the GTP server's - * responses */ - const char* server_rx_addr_str = "127.0.0.4"; - uint16_t server_rx_port = 2123; - - rc = osmo_sockaddr_init(&server_addr, &server_addr_len, - AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, server_addr_str, server_port); + struct gtphub_peer *test_server = gtphub_peer_new(&hub->to_servers[GTPH_PORT_CONTROL]); + rc = osmo_sockaddr_init(&test_server->addr.a, &test_server->addr.l, + AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, + server_addr_str, server_port); if (rc != 0) { LOGERR("Cannot resolve '%s port %d'\n", server_addr_str, server_port); exit(-1); }
- struct osmo_fd clients_ofd; - struct osmo_fd server_ofd; - - memset(&clients_ofd, 0, sizeof(clients_ofd)); - clients_ofd.when = BSC_FD_READ; - clients_ofd.cb = clients_read_cb; - clients_ofd.data = &server_ofd; - - rc = osmo_sock_init_ofd(&clients_ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, clients_addr_str, clients_port, OSMO_SOCK_F_BIND); - if (rc < 1) { - LOGERR("Cannot bind to %s port %d\n", clients_addr_str, clients_port); - exit(-1); - } - - memset(&server_ofd, 0, sizeof(server_ofd)); - server_ofd.when = BSC_FD_READ; - server_ofd.cb = server_read_cb; - server_ofd.data = &clients_ofd; - - rc = osmo_sock_init_ofd(&server_ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, server_rx_addr_str, server_rx_port, OSMO_SOCK_F_BIND); - if (rc < 1) { - LOGERR("Cannot bind to %s port %d\n", server_rx_addr_str, server_rx_port); - exit(-1); - } - - LOG("GTP server connection: %s port %d <--> %s port %d\n", - server_rx_addr_str, (int)server_rx_port, - server_addr_str, (int)server_port); - LOG("Listening for clients on %s port %d.\n", clients_addr_str, clients_port); + log_cfg(cfg);
int daemonize = 0;
On 07 Oct 2015, at 16:18, Neels Hofmeyr nhofmeyr@sysmocom.de wrote:
+/* TODO move this to osmocom/core/linuxlist.h ? */ +#define __llist_first(head) (((head)->next == (head)) ? NULL : (head)->next) +#define llist_first(head, type, entry) llist_entry(__llist_first(head), type, entry)
Why the null check? I mean
struct *foo = llist_first(list); if (!foo) return
vs.
if (llist_empty(list)) return; struct *foo = llist_first(list);
has no difference?
+int servers_read_cb(struct osmo_fd *servers_ofd, unsigned int what) +{
- unsigned int port_idx = servers_ofd->priv_nr;
- OSMO_ASSERT(port_idx < GTPH_PORT_N);
- LOG("reading from servers socket (%s)\n", gtphub_port_idx_names[port_idx]);
- if (!(what & BSC_FD_READ))
return 0;- struct gtphub *hub = servers_ofd->data;
- /* TODO this will not be hardcoded. */
- struct gtphub_peer *client = llist_first(&hub->to_clients[port_idx].peers,
struct gtphub_peer, entry);
interesting that you are the first to use the llist in this way. As you will track multiple peers the need for a llist_first will vanish?
On Thu, Oct 08, 2015 at 11:05:21AM +0200, Holger Freyther wrote:
+#define __llist_first(head) (((head)->next == (head)) ? NULL : (head)->next) +#define llist_first(head, type, entry) llist_entry(__llist_first(head), type, entry)
Why the null check? I mean
struct *foo = llist_first(list); if (!foo) return
vs.
if (llist_empty(list)) return; struct *foo = llist_first(list);
has no difference?
Almost. All it does is make sure I don't forget the emptiness check, really... a NULL dereference will more reliably result in a segfault than dereferencing a list item struct from an llist_head. But anyway: ...
- /* TODO this will not be hardcoded. */
- struct gtphub_peer *client = llist_first(&hub->to_clients[port_idx].peers,
struct gtphub_peer, entry);interesting that you are the first to use the llist in this way. As you will track multiple peers the need for a llist_first will vanish?
... Indeed.
~Neels
Sponsored-by: On-Waves ehi --- openbsc/include/openbsc/gtphub.h | 42 +++++++++++++++++++++ openbsc/src/gprs/gtphub.c | 81 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+)
diff --git a/openbsc/include/openbsc/gtphub.h b/openbsc/include/openbsc/gtphub.h index cd4c215..f25c56b 100644 --- a/openbsc/include/openbsc/gtphub.h +++ b/openbsc/include/openbsc/gtphub.h @@ -38,6 +38,46 @@ enum gtphub_port_idx { extern const char* const gtphub_port_idx_names[GTPH_PORT_N];
+/* Generator for unused TEI IDs. So far this counts upwards from zero, but the + * implementation may change in the future. Treat this like an opaque struct. */ +struct tei_pool { + uint32_t last_tei; +}; + +void tei_pool_init(struct tei_pool *pool); + +/* Return the next unused TEI from the tei_pool. */ +uint32_t tei_pool_next(struct tei_pool *pool); + + +struct tei_mapping { + struct llist_head entry; + + uint32_t orig; + uint32_t repl; +}; + +struct tei_map { + struct tei_pool *pool; + struct llist_head mappings; +}; + +/* Initialize an (already allocated) tei_map, and set the map's TEI pool. + * Multiple tei_map instances may use the same tei_pool. */ +void tei_map_init(struct tei_map *map, struct tei_pool *pool); + +/* Return a replacement TEI for tei_orig. If tei_orig is unknown, create a new + * mapping using a so far unused TEI to map tei_orig to. Return 0 on error. */ +uint32_t tei_map_get(struct tei_map *map, uint32_t tei_orig); + +/* Return the original TEI for a replacement TEI. If no mapping exists to + * tei_repl, return 0. */ +uint32_t tei_map_get_rev(struct tei_map *map, uint32_t tei_repl); + +/* Remove the mapping for tei_orig, if it exists. */ +void tei_map_del(struct tei_map *map, uint32_t tei_orig); + + /* config */
struct gtphub_cfg_addr { @@ -68,10 +108,12 @@ struct gtphub_peer { struct llist_head entry;
struct gtphub_addr addr; + struct tei_map teim; };
struct gtphub_bind { struct osmo_fd ofd; + struct tei_pool teip;
/* list of struct gtphub_peer */ struct llist_head peers; diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index a8bd276..419b21f 100644 --- a/openbsc/src/gprs/gtphub.c +++ b/openbsc/src/gprs/gtphub.c @@ -21,6 +21,7 @@
#include <string.h> #include <errno.h> +#include <inttypes.h> #include <netinet/in.h>
#include <openbsc/gtphub.h> @@ -54,6 +55,82 @@ const char* const gtphub_port_idx_names[GTPH_PORT_N] = { };
+void tei_pool_init(struct tei_pool *pool) +{ + *pool = (struct tei_pool){}; +} + +uint32_t tei_pool_next(struct tei_pool *pool) +{ + pool->last_tei ++; + + OSMO_ASSERT(pool->last_tei > 0); + /* TODO: gracefully handle running out of TEIs. */ + /* TODO: random TEIs. */ + + return pool->last_tei; +} + +void tei_map_init(struct tei_map *map, struct tei_pool *pool) +{ + *map = (struct tei_map){}; + map->pool = pool; + INIT_LLIST_HEAD(&map->mappings); +} + +static uint32_t tei_map_new(struct tei_map *map, uint32_t tei_orig) +{ + struct tei_mapping *mapping; + mapping = talloc_zero(osmo_gtphub_ctx, struct tei_mapping); + OSMO_ASSERT(mapping); + mapping->orig = tei_orig; + mapping->repl = tei_pool_next(map->pool); + llist_add(&mapping->entry, &map->mappings); + return mapping->repl; +} + +uint32_t tei_map_get(struct tei_map *map, uint32_t tei_orig) +{ + OSMO_ASSERT(tei_orig != 0); + + struct tei_mapping *mapping; + llist_for_each_entry(mapping, &map->mappings, entry) { + if (mapping->orig == tei_orig) + return mapping->repl; + } + /* Not found. */ + + return tei_map_new(map, tei_orig); +} + +uint32_t tei_map_get_rev(struct tei_map *map, uint32_t tei_repl) +{ + OSMO_ASSERT(tei_repl != 0); + + struct tei_mapping *pos; + llist_for_each_entry(pos, &map->mappings, entry) { + if (pos->repl == tei_repl) { + OSMO_ASSERT(pos->orig); + return pos->orig; + } + } + return 0; +} + +void tei_map_del(struct tei_map *map, uint32_t tei_orig) +{ + struct tei_mapping *mapping; + llist_for_each_entry(mapping, &map->mappings, entry) { + if (mapping->orig == tei_orig) { + llist_del(&mapping->entry); + talloc_free(mapping); + return; + } + } + LOGERR("No mapping exists for TEI %" PRIu32 ".\n", tei_orig); +} + + /* gtphub */
void gtphub_zero(struct gtphub *hub) @@ -93,6 +170,7 @@ static int gtphub_gtp_bind_init(struct gtphub_bind *b, { memset(b, '\0', sizeof(*b));
+ tei_pool_init(&b->teip); INIT_LLIST_HEAD(&b->peers);
if (gtphub_sock_init(&b->ofd, &cfg->bind, cb, cb_data, ofd_id) != 0) @@ -238,6 +316,9 @@ int gtphub_init(struct gtphub *hub, struct gtphub_cfg *cfg) struct gtphub_peer *gtphub_peer_new(struct gtphub_bind *bind) { struct gtphub_peer *n = talloc_zero(osmo_gtphub_ctx, struct gtphub_peer); + + tei_map_init(&n->teim, &bind->teip); + llist_add(&n->entry, &bind->peers); return n; }
Sponsored-by: On-Waves ehi --- openbsc/.gitignore | 1 + openbsc/tests/gtphub/Makefile.am | 15 ++++++++- openbsc/tests/gtphub/gtphub_test.c | 63 +++++++++++++++++++++++++++++++++++++ openbsc/tests/gtphub/gtphub_test.ok | 1 + openbsc/tests/testsuite.at | 2 ++ 5 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 openbsc/tests/gtphub/gtphub_test.c create mode 100644 openbsc/tests/gtphub/gtphub_test.ok
diff --git a/openbsc/.gitignore b/openbsc/.gitignore index f024d76..bf05ac2 100644 --- a/openbsc/.gitignore +++ b/openbsc/.gitignore @@ -78,6 +78,7 @@ tests/trau/trau_test tests/mgcp/mgcp_transcoding_test tests/sgsn/sgsn_test tests/subscr/subscr_test +tests/gtphub/gtphub_test
tests/atconfig tests/atlocal diff --git a/openbsc/tests/gtphub/Makefile.am b/openbsc/tests/gtphub/Makefile.am index d53c19d..c4b3ec1 100644 --- a/openbsc/tests/gtphub/Makefile.am +++ b/openbsc/tests/gtphub/Makefile.am @@ -1,3 +1,16 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) + EXTRA_DIST = \ gtphub_nc_test.sh \ - gtphub_nc_test.ok + gtphub_nc_test.ok \ + gtphub_test.ok + +noinst_PROGRAMS = gtphub_test + +gtphub_test_SOURCES = gtphub_test.c + +gtphub_test_LDADD = \ + $(LIBOSMOCORE_LIBS) \ + -lgtp -lrt + diff --git a/openbsc/tests/gtphub/gtphub_test.c b/openbsc/tests/gtphub/gtphub_test.c new file mode 100644 index 0000000..95f82c3 --- /dev/null +++ b/openbsc/tests/gtphub/gtphub_test.c @@ -0,0 +1,63 @@ +/* Test the GTP hub */ + +/* (C) 2015 by sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Neels Hofmeyr nhofmeyr@sysmcom.de + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <stdio.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/application.h> + +#include <openbsc/debug.h> + +void *osmo_gtphub_ctx; + +static void test_gtphub_api(void) +{ + OSMO_ASSERT(1); +} + +static struct log_info_cat gtphub_categories[] = { + [DGTPHUB] = { + .name = "DGTPHUB", + .description = "GTP Hub", + .color = "\033[1;33m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, +}; + +static struct log_info info = { + .cat = gtphub_categories, + .num_cat = ARRAY_SIZE(gtphub_categories), +}; + +int main(int argc, char **argv) +{ + osmo_init_logging(&info); + osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub"); + + test_gtphub_api(); + printf("Done\n"); + + talloc_report_full(osmo_gtphub_ctx, stderr); + return 0; +} + diff --git a/openbsc/tests/gtphub/gtphub_test.ok b/openbsc/tests/gtphub/gtphub_test.ok new file mode 100644 index 0000000..a965a70 --- /dev/null +++ b/openbsc/tests/gtphub/gtphub_test.ok @@ -0,0 +1 @@ +Done diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at index 14d1f72..9a1f9dd 100644 --- a/openbsc/tests/testsuite.at +++ b/openbsc/tests/testsuite.at @@ -109,4 +109,6 @@ AT_KEYWORDS([gtphub]) AT_CHECK([test "$enable_gtphub_test" != no || exit 77]) cat $abs_srcdir/gtphub/gtphub_nc_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gtphub/gtphub_nc_test.sh $abs_top_builddir/src/gprs/osmo-gtphub], [], [expout], [ignore]) +cat $abs_srcdir/gtphub/gtphub_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/gtphub/gtphub_test], [], [expout], [ignore]) AT_CLEANUP
Sponsored-by: On-Waves ehi --- openbsc/tests/gtphub/Makefile.am | 1 + openbsc/tests/gtphub/gtphub_test.c | 60 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 3 deletions(-)
diff --git a/openbsc/tests/gtphub/Makefile.am b/openbsc/tests/gtphub/Makefile.am index c4b3ec1..4b852e4 100644 --- a/openbsc/tests/gtphub/Makefile.am +++ b/openbsc/tests/gtphub/Makefile.am @@ -11,6 +11,7 @@ noinst_PROGRAMS = gtphub_test gtphub_test_SOURCES = gtphub_test.c
gtphub_test_LDADD = \ + $(top_builddir)/src/gprs/gtphub.o \ $(LIBOSMOCORE_LIBS) \ -lgtp -lrt
diff --git a/openbsc/tests/gtphub/gtphub_test.c b/openbsc/tests/gtphub/gtphub_test.c index 95f82c3..25e51e7 100644 --- a/openbsc/tests/gtphub/gtphub_test.c +++ b/openbsc/tests/gtphub/gtphub_test.c @@ -28,11 +28,65 @@
#include <openbsc/debug.h>
+#include <openbsc/gtphub.h> + void *osmo_gtphub_ctx;
-static void test_gtphub_api(void) +/* TODO copied from libosmo-abis/src/subchan_demux.c, remove dup */ +static int llist_len(struct llist_head *head) +{ + struct llist_head *entry; + int i = 0; + + llist_for_each(entry, head) + i++; + + return i; +} + +static void test_tei_map(void) { - OSMO_ASSERT(1); + /* Basic */ + struct tei_pool _pool; + struct tei_pool *pool = &_pool; + struct tei_map _map; + struct tei_map *map = &_map; + + tei_pool_init(pool); + tei_map_init(map, pool); + + OSMO_ASSERT(llist_empty(&map->mappings)); + +#define TEST_N 100 +#define TEST_I 123 + uint32_t i, check_i; + uint32_t m[TEST_N]; + + /* create TEST_N mappings */ + for (i = 0; i < TEST_N; i++) { + m[i] = tei_map_get(map, TEST_I + i); + OSMO_ASSERT(m[i] != 0); + OSMO_ASSERT(llist_len(&map->mappings) == (i+1)); + for (check_i = 0; check_i < i; check_i++) + OSMO_ASSERT(m[check_i] != m[i]); + } + OSMO_ASSERT(llist_len(&map->mappings) == TEST_N); + + /* verify mappings */ + for (i = 0; i < TEST_N; i++) { + OSMO_ASSERT(tei_map_get(map, TEST_I + i) == m[i]); + OSMO_ASSERT(tei_map_get_rev(map, m[i]) == (TEST_I + i)); + } + OSMO_ASSERT(llist_len(&map->mappings) == TEST_N); + + /* remove all mappings */ + for (i = 0; i < TEST_N; i++) { + tei_map_del(map, TEST_I + i); + OSMO_ASSERT(llist_len(&map->mappings) == (TEST_N - (i+1))); + } + OSMO_ASSERT(llist_empty(&map->mappings)); +#undef TEST_N +#undef TEST_I }
static struct log_info_cat gtphub_categories[] = { @@ -54,7 +108,7 @@ int main(int argc, char **argv) osmo_init_logging(&info); osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub");
- test_gtphub_api(); + test_tei_map(); printf("Done\n");
talloc_report_full(osmo_gtphub_ctx, stderr);
Sponsored-by: On-Waves ehi --- openbsc/src/gprs/gtphub.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+)
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index 419b21f..c6ce4f6 100644 --- a/openbsc/src/gprs/gtphub.c +++ b/openbsc/src/gprs/gtphub.c @@ -24,6 +24,8 @@ #include <inttypes.h> #include <netinet/in.h>
+#include <gtp.h> + #include <openbsc/gtphub.h> #include <openbsc/debug.h>
@@ -46,6 +48,116 @@ typedef int (*osmo_fd_cb_t)(struct osmo_fd *fd, unsigned int what); #define __llist_first(head) (((head)->next == (head)) ? NULL : (head)->next) #define llist_first(head, type, entry) llist_entry(__llist_first(head), type, entry)
+/* TODO duplicated from openggsn/gtp/gtpie.h */ +#define ntoh16(x) ntohs(x) +#define ntoh32(x) ntohl(x) + +/* TODO move GTP header stuff to openggsn/gtp/ ? See gtp_decaps*() */ + +enum gtp_header_type { + GTPH_V0 = 0, + GTPH_V1 = 1, + + GTPH_TOOSHORT = -1, + GTPH_UNSUPPORTED_VERSION = -2, + GTPH_LOWER_VERSION = -3, // when gtp0 is received by gtp1 socket +}; + +int validate_gtp0_header(const uint8_t *data, int data_len) +{ + struct gtp0_header *pheader; + pheader = (struct gtp0_header *)(data); + + OSMO_ASSERT(data_len >= 1); + + int version = (*data) >> 5; + + /* Version should be gtp0 (or earlier) */ + /* 09.60 is somewhat unclear on this issue. On gsn->fd0 we expect only */ + /* GTP 0 messages. If other version message is received we reply that we */ + /* only support version 0, implying that this is the only version */ + /* supported on this port */ + if (version != 0) { + LOGERR("Expecting GTP version 0, got: %d\n", version); + return GTPH_UNSUPPORTED_VERSION; + } + + /* Check length of gtp0 packet */ + if (data_len < GTP0_HEADER_SIZE) { + LOGERR("GTP0 packet too short: %d\n", data_len); + return GTPH_TOOSHORT; + } + + /* Check packet length field versus length of packet */ + if (data_len != (ntoh16(pheader->length) + GTP0_HEADER_SIZE)) { + LOGERR("GTP packet length field (%d + %d) does not match" + " actual length (%d)\n", + GTP0_HEADER_SIZE, (int)ntoh16(pheader->length), + data_len); + return GTPH_TOOSHORT; + } + + return GTPH_V0; +} + +int validate_gtp1_header(const uint8_t *data, int data_len) +{ + struct gtp1_header_short *pheader; + pheader = (struct gtp1_header_short *)data; + + OSMO_ASSERT(data_len >= 1); + + int version = (*data) >> 5; + + if (version != 1) { + LOGERR("Expecting GTP version 1, got: %d\n", version); + return (version < 1? GTPH_LOWER_VERSION : GTPH_UNSUPPORTED_VERSION); + } + + /* Check length of packet */ + if (data_len < GTP1_HEADER_SIZE_LONG) { + LOGERR("GTP packet too short: %d\n", data_len); + return GTPH_TOOSHORT; + } + + /* Check packet length field versus length of packet */ + if (data_len != (ntoh16(pheader->length) + GTP1_HEADER_SIZE_SHORT)) { + LOGERR("GTP packet length field (%d + %d) does not match" + " actual length (%d)\n", + GTP1_HEADER_SIZE_SHORT, (int)ntoh16(pheader->length), + data_len); + return GTPH_TOOSHORT; + } + + return GTPH_V1; +} + +/* Examine whether given data of size data_len has a valid GTP header. + * Return the GTP version, or < 0 on error (see enum gtp_header_type). + */ +int validate_gtp_header(const uint8_t *data, int data_len) +{ + /* Need at least 1 byte in order to check version */ + if (data_len < (1)) { + LOGERR("Discarding packet - too small\n"); + return GTPH_TOOSHORT; + } + + int version = (*data) >> 5; + + switch (version) { + case 0: + return validate_gtp0_header(data, data_len); + case 1: + return validate_gtp1_header(data, data_len); + default: + break; + } + + LOGERR("Unsupported GTP version: %d\n", version); + return GTPH_UNSUPPORTED_VERSION; +} +
/* general */
@@ -55,6 +167,8 @@ const char* const gtphub_port_idx_names[GTPH_PORT_N] = { };
+/* tei_map, tei_pool */ + void tei_pool_init(struct tei_pool *pool) { *pool = (struct tei_pool){}; @@ -215,6 +329,16 @@ static int gtp_relay(struct osmo_fd *from,
/* insert magic here */
+ int gtph = validate_gtp_header(buf, received); + + if (gtph >= 0) + LOG("Valid GTP header (v%d)\n", gtph); +#if 0 + else + // error has been logged + return 0; +#endif + errno = 0; ssize_t sent = sendto(to->fd, buf, received, 0, (struct sockaddr*)to_addr, to_addr_len);
Sponsored-by: On-Waves ehi --- openbsc/src/gprs/gtphub.c | 19 +++++++++++++++++-- openbsc/src/gprs/gtphub_main.c | 1 + 2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index c6ce4f6..5d7b63d 100644 --- a/openbsc/src/gprs/gtphub.c +++ b/openbsc/src/gprs/gtphub.c @@ -97,13 +97,14 @@ int validate_gtp0_header(const uint8_t *data, int data_len) return GTPH_TOOSHORT; }
+ LOG("GTP v0 TID = %" PRIu64 "\n", pheader->tid); return GTPH_V0; }
int validate_gtp1_header(const uint8_t *data, int data_len) { - struct gtp1_header_short *pheader; - pheader = (struct gtp1_header_short *)data; + struct gtp1_header_long *pheader; + pheader = (struct gtp1_header_long *)data;
OSMO_ASSERT(data_len >= 1);
@@ -129,6 +130,18 @@ int validate_gtp1_header(const uint8_t *data, int data_len) return GTPH_TOOSHORT; }
+ LOG("GTP v1 type = %" PRIu8 " 0x%02" PRIx8 "\n", + pheader->type, pheader->type); + LOG("GTP v1 length = %" PRIu16 " 0x%04" PRIx16 "\n", + ntoh16(pheader->length), ntoh16(pheader->length)); + LOG("GTP v1 TEI = %" PRIu32 " 0x%08" PRIx32 "\n", + ntoh32(pheader->tei), ntoh32(pheader->tei)); + LOG("GTP v1 seq = %" PRIu16 " 0x%04" PRIx16 "\n", + ntoh16(pheader->seq), ntoh16(pheader->seq)); + LOG("GTP v1 npdu = %" PRIu8 " 0x%02" PRIx8 "\n", + pheader->npdu, pheader->npdu); + LOG("GTP v1 next = %" PRIu8 " 0x%02" PRIx8 "\n", + pheader->next, pheader->next); return GTPH_V1; }
@@ -329,6 +342,8 @@ static int gtp_relay(struct osmo_fd *from,
/* insert magic here */
+ LOG("DATA\n%s\n", osmo_hexdump(buf, received)); + int gtph = validate_gtp_header(buf, received);
if (gtph >= 0) diff --git a/openbsc/src/gprs/gtphub_main.c b/openbsc/src/gprs/gtphub_main.c index 104cbfd..b584617 100644 --- a/openbsc/src/gprs/gtphub_main.c +++ b/openbsc/src/gprs/gtphub_main.c @@ -199,6 +199,7 @@ int main(int argc, char **argv) LOGERR("Cannot resolve '%s port %d'\n", server_addr_str, server_port); exit(-1); } + LOG("DEBUG: using GTP server %s port %d\n", server_addr_str, server_port);
log_cfg(cfg);