Here is the part of the branch openbsc:neels/gtphub that I believe may be merged to master at this point. (Holger: please go ahead with the merge, or tell me to do so...)
The remaining bit is mostly a unit test netcat'ing to a gtphub instance. To do that properly, gtphub must be configurable. Hence I'm starting to add VTY config, which is not complete yet. Hence that part is still omitted here.
Neels Hofmeyr (15): Add GTP hub stub, as simplistic UDP forwarder. gtphub: add to build gtphub: add skeletal gtphub.[hc] gtphub: populate API impl from test prog gtphub: add a todo comment gtphub: add TEI map API. gtphub: add gtphub_test.c (empty) gtphub: add TEI map test gtphub: add GTP header validation gtphub: undup code: memset on a struct. gtphub: tweak logging gtphub: index IEs, decode and log a few. gtphub: split gtp_relay() in r/w funcs gtphub: map sequence numbers SGSNs<->GGSNs gtphub: add signal handler to gtphub_main
openbsc/.gitignore | 2 + openbsc/configure.ac | 1 + openbsc/include/openbsc/Makefile.am | 3 +- openbsc/include/openbsc/debug.h | 1 + openbsc/include/openbsc/gtphub.h | 151 ++++++++ openbsc/src/gprs/Makefile.am | 4 + openbsc/src/gprs/gtphub.c | 714 ++++++++++++++++++++++++++++++++++++ openbsc/src/gprs/gtphub_main.c | 256 +++++++++++++ openbsc/tests/Makefile.am | 2 +- openbsc/tests/gtphub/Makefile.am | 15 + openbsc/tests/gtphub/gtphub_test.c | 117 ++++++ openbsc/tests/gtphub/gtphub_test.ok | 1 + openbsc/tests/testsuite.at | 7 + 13 files changed, 1272 insertions(+), 2 deletions(-) create mode 100644 openbsc/include/openbsc/gtphub.h create mode 100644 openbsc/src/gprs/gtphub.c create mode 100644 openbsc/src/gprs/gtphub_main.c create mode 100644 openbsc/tests/gtphub/Makefile.am create mode 100644 openbsc/tests/gtphub/gtphub_test.c create mode 100644 openbsc/tests/gtphub/gtphub_test.ok
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
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..f225efc --- /dev/null +++ b/openbsc/src/gprs/gtphub_main.c @@ -0,0 +1,297 @@ +/* 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 ? + * -- will actually disappear when the GGSNs are resolved by DNS. */ +/*! \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); +}
Add program osmo-gtphub from gtphub_main.c to Makefile.am. Add osmo-gtphub binary to gitignore.
Sponsored-by: On-Waves ehf
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 f012003..66d0625 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 @@ -32,3 +33,6 @@ osmo_sgsn_LDADD = \ $(top_builddir)/src/libcommon/libcommon.a \ -lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) $(LIBCARES_LIBS) \ $(LIBCRYPTO_LIBS) -lrt + +osmo_gtphub_SOURCES = gtphub_main.c +osmo_gtphub_LDADD = $(LIBOSMOCORE_LIBS) -lrt
Sponsored-by: On-Waves ehf
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..7c89db1 --- /dev/null +++ b/openbsc/include/openbsc/gtphub.h @@ -0,0 +1,96 @@ +/* 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_sgsns[GTPH_PORT_N]; + struct gtphub_cfg_bind to_ggsns[GTPH_PORT_N]; +}; + + +/* state */ + +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_sgsns[GTPH_PORT_N]; + struct gtphub_bind to_ggsns[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 66d0625..f2f914d 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -34,5 +34,5 @@ osmo_sgsn_LDADD = \ -lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) $(LIBCARES_LIBS) \ $(LIBCRYPTO_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
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index ffe0d4a..97e52a6 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 from_ggsns_read_cb(struct osmo_fd *from_ggsns_ofd, unsigned int what) +{ + unsigned int port_idx = from_ggsns_ofd->priv_nr; + OSMO_ASSERT(port_idx < GTPH_PORT_N); + LOG("reading from GGSN (%s)\n", gtphub_port_idx_names[port_idx]); + if (!(what & BSC_FD_READ)) + return 0; + + struct gtphub *hub = from_ggsns_ofd->data; + + /* TODO this will not be hardcoded. */ + struct gtphub_peer *sgsn = llist_first(&hub->to_sgsns[port_idx].peers, + struct gtphub_peer, entry); + if (!sgsn) { + LOGERR("no sgsn"); + return 0; + } + + return gtp_relay(from_ggsns_ofd, NULL, NULL, + &hub->to_sgsns[port_idx].ofd, + &sgsn->addr.a, sgsn->addr.l); +} + +int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what) +{ + unsigned int port_idx = from_sgsns_ofd->priv_nr; + OSMO_ASSERT(port_idx < GTPH_PORT_N); + LOG("reading from SGSN (%s)\n", gtphub_port_idx_names[port_idx]); + + if (!(what & BSC_FD_READ)) + return 0; + + struct gtphub *hub = from_sgsns_ofd->data; + + /* TODO this will not be hardcoded. */ + struct gtphub_peer *ggsn = llist_first(&hub->to_ggsns[port_idx].peers, + struct gtphub_peer, entry); + if (!ggsn) { + LOGERR("no ggsn to send to\n"); + return 0; + } + + /* so far just remembering the last sgsn */ + struct gtphub_peer *sgsn = llist_first(&hub->to_sgsns[port_idx].peers, + struct gtphub_peer, entry); + if (!sgsn) + sgsn = gtphub_peer_new(&hub->to_sgsns[port_idx]); + + return gtp_relay(from_sgsns_ofd, &sgsn->addr.a, &sgsn->addr.l, + &hub->to_ggsns[port_idx].ofd, + &ggsn->addr.a, ggsn->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_ggsns[port_id], + &cfg->to_ggsns[port_id], + from_ggsns_read_cb, hub, port_id); + if (rc < 0) + return rc; + + rc = gtphub_gtp_bind_init(&hub->to_sgsns[port_id], + &cfg->to_sgsns[port_id], + from_sgsns_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 f225efc..b43e28d 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) @@ -113,7 +103,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" @@ -142,139 +131,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 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); + struct gtphub_cfg_addr *a; + a = &cfg->to_sgsns[GTPH_PORT_CONTROL].bind; + LOG("to-SGSNs bind, Control: %s port %d\n", + a->addr_str, a->port); + a = &cfg->to_sgsns[GTPH_PORT_USER].bind; + LOG("to-SGSNs bind, User: %s port %d\n", + a->addr_str, a->port); + a = &cfg->to_ggsns[GTPH_PORT_CONTROL].bind; + LOG("to-GGSNs bind, Control: %s port %d\n", + a->addr_str, a->port); + a = &cfg->to_ggsns[GTPH_PORT_USER].bind; + LOG("to-GGSNs bind, User: %s port %d\n", + a->addr_str, a->port); }
int main(int argc, char **argv) { + osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub"); + 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; + struct gtphub_cfg _cfg = { + .to_sgsns = { + { .bind = { + .addr_str = "127.0.0.3", + .port = 2123, + } }, + { .bind = { + .addr_str = "127.0.0.3", + .port = 2152, + } }, + }, + .to_ggsns = { + { .bind = { + .addr_str = "127.0.0.4", + .port = 2123, + } }, + { .bind = { + .addr_str = "127.0.0.4", + .port = 2152, + } }, + }, + };
- memset(&clients_ofd, 0, sizeof(clients_ofd)); - clients_ofd.when = BSC_FD_READ; - clients_ofd.cb = clients_read_cb; - clients_ofd.data = &server_ofd; + struct gtphub_cfg *cfg = &_cfg;
- 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); - } + struct gtphub _hub; + struct gtphub *hub = &_hub;
- memset(&server_ofd, 0, sizeof(server_ofd)); - server_ofd.when = BSC_FD_READ; - server_ofd.cb = server_read_cb; - server_ofd.data = &clients_ofd; + if (gtphub_init(hub, cfg) != 0) + return -1;
- 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); + /* TODO this will not be configured, gtphub will have to find the + * ggsns from incoming GTP PDUs. */ + /* Where the GTP ggsn sits that we're relaying for */ + const char* ggsn_addr_str = "127.0.0.2"; + uint16_t ggsn_port = 2123; + struct gtphub_peer *test_ggsn = gtphub_peer_new(&hub->to_ggsns[GTPH_PORT_CONTROL]); + rc = osmo_sockaddr_init(&test_ggsn->addr.a, &test_ggsn->addr.l, + AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, + ggsn_addr_str, ggsn_port); + if (rc != 0) { + LOGERR("Cannot resolve '%s port %d'\n", ggsn_addr_str, ggsn_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;
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index 97e52a6..577a2d0 100644 --- a/openbsc/src/gprs/gtphub.c +++ b/openbsc/src/gprs/gtphub.c @@ -119,6 +119,8 @@ static int gtp_relay(struct osmo_fd *from, errno = 0; ssize_t received = recvfrom(from->fd, buf, sizeof(buf), 0, (struct sockaddr*)from_addr, from_addr_len); + /* TODO use recvmsg and get a MSG_TRUNC flag to make sure the message + * is not truncated. Then maybe reduce buf's size. */
if (received <= 0) { if (errno != EAGAIN)
Sponsored-by: On-Waves ehi
diff --git a/openbsc/include/openbsc/gtphub.h b/openbsc/include/openbsc/gtphub.h index 7c89db1..7354786 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 { @@ -66,10 +106,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 577a2d0..a80243c 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) @@ -240,6 +318,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
create mode 100644 openbsc/tests/gtphub/Makefile.am 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/configure.ac b/openbsc/configure.ac index fc30b5e..9393282 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/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..7cc3ac9 --- /dev/null +++ b/openbsc/tests/gtphub/Makefile.am @@ -0,0 +1,14 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) + +EXTRA_DIST = \ + 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 74aaef0..bfaa76e 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_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/gtphub/gtphub_test], [], [expout], [ignore]) +AT_CLEANUP
Sponsored-by: On-Waves ehi
diff --git a/openbsc/tests/gtphub/Makefile.am b/openbsc/tests/gtphub/Makefile.am index 7cc3ac9..5347811 100644 --- a/openbsc/tests/gtphub/Makefile.am +++ b/openbsc/tests/gtphub/Makefile.am @@ -9,6 +9,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
diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index f2f914d..d571d8d 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -35,4 +35,4 @@ osmo_sgsn_LDADD = \ $(LIBCRYPTO_LIBS) -lrt
osmo_gtphub_SOURCES = gtphub_main.c gtphub.c -osmo_gtphub_LDADD = $(LIBOSMOCORE_LIBS) -lrt +osmo_gtphub_LDADD = -lgtp $(LIBOSMOCORE_LIBS) -lrt diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index a80243c..208b623 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,150 @@ 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_rc { + GTP_RC_UNKNOWN = 0, + GTP_RC_TINY = 1, /* no IEs (like ping/pong) */ + GTP_RC_PDU = 2, /* a real packet with IEs */ + + GTP_RC_TOOSHORT = -1, + GTP_RC_UNSUPPORTED_VERSION = -2, + GTP_RC_INVALID_IE = -3, +}; + +struct gtp_packet_desc { + const union gtp_packet *data; + int data_len; + int header_len; + int version; + int rc; +}; + +/* Validate GTP version 0 data; analogous to validate_gtp1_header(), see there. + */ +void validate_gtp0_header(struct gtp_packet_desc *p) +{ + const struct gtp0_header *pheader = &(p->data->gtp0.h); + p->rc = GTP_RC_UNKNOWN; + p->header_len = 0; + + OSMO_ASSERT(p->data_len >= 1); + OSMO_ASSERT(p->version == 0); + + if (p->data_len < GTP0_HEADER_SIZE) { + LOGERR("GTP0 packet too short: %d\n", p->data_len); + p->rc = GTP_RC_TOOSHORT; + return; + } + + if (p->data_len == GTP0_HEADER_SIZE) { + p->rc = GTP_RC_TINY; + p->header_len = GTP0_HEADER_SIZE; + return; + } + + /* Check packet length field versus length of packet */ + if (p->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), + p->data_len); + p->rc = GTP_RC_TOOSHORT; + return; + } + + p->header_len = GTP0_HEADER_SIZE; + p->rc = GTP_RC_PDU; +} + +/* Validate GTP version 1 data, and update p->rc with the result, as well as + * p->header_len in case of a valid header. */ +void validate_gtp1_header(struct gtp_packet_desc *p) +{ + const struct gtp1_header_long *pheader = &(p->data->gtp1l.h); + p->rc = GTP_RC_UNKNOWN; + p->header_len = 0; + + OSMO_ASSERT(p->data_len >= 1); + OSMO_ASSERT(p->version == 1); + + if ((p->data_len < GTP1_HEADER_SIZE_LONG) + && (p->data_len != GTP1_HEADER_SIZE_SHORT)){ + LOGERR("GTP packet too short: %d\n", p->data_len); + p->rc = GTP_RC_TOOSHORT; + return; + } + + if (p->data_len <= GTP1_HEADER_SIZE_LONG) { + p->rc = GTP_RC_TINY; + p->header_len = GTP1_HEADER_SIZE_SHORT; + return; + } + + /* Check packet length field versus length of packet */ + if (p->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), + p->data_len); + p->rc = GTP_RC_TOOSHORT; + return; + } + + p->rc = GTP_RC_PDU; + p->header_len = GTP1_HEADER_SIZE_LONG; +} + +/* Examine whether p->data of size p->data_len has a valid GTP header. Set + * p->version, p->rc and p->header_len. On error, p->rc <= 0 (see enum + * gtp_rc). p->data must point at a buffer with p->data_len set. */ +void validate_gtp_header(struct gtp_packet_desc *p) +{ + p->rc = GTP_RC_UNKNOWN; + + /* Need at least 1 byte in order to check version */ + if (p->data_len < (1)) { + LOGERR("Discarding packet - too small\n"); + p->rc = GTP_RC_TOOSHORT; + return; + } + + p->version = p->data->flags >> 5; + + switch (p->version) { + case 0: + validate_gtp0_header(p); + break; + case 1: + validate_gtp1_header(p); + break; + default: + LOGERR("Unsupported GTP version: %d\n", p->version); + p->rc = GTP_RC_UNSUPPORTED_VERSION; + break; + } +} + +/* Validate header, and index information elements. Write decoded packet + * information to *res. res->data will point at the given data buffer. On + * error, p->rc is set <= 0 (see enum gtp_rc). */ +static void gtp_decode(const uint8_t *data, int data_len, struct gtp_packet_desc *res) +{ + memset(res, '\0', sizeof(*res)); + res->data = (union gtp_packet*)data; + res->data_len = data_len; + + validate_gtp_header(res); + + if (res->rc == GTP_RC_TINY) + LOG("tiny: no IEs in this GTP packet\n"); +} +
/* general */
@@ -55,6 +201,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){}; @@ -217,6 +365,17 @@ static int gtp_relay(struct osmo_fd *from,
/* insert magic here */
+ struct gtp_packet_desc p; + gtp_decode(buf, received, &p); + + if (p.rc > 0) + LOG("Valid GTP header (v%d)\n", p.version); +#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);
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index 208b623..1d9c6d5 100644 --- a/openbsc/src/gprs/gtphub.c +++ b/openbsc/src/gprs/gtphub.c @@ -41,6 +41,8 @@ void *osmo_gtphub_ctx; #define LOG(fmt, args...) \ LOGP(DGTPHUB, LOGL_NOTICE, fmt, ##args)
+#define ZERO_STRUCT(struct_pointer) memset(struct_pointer, '\0', sizeof(*(struct_pointer))) + /* TODO move this to osmocom/core/select.h ? */ typedef int (*osmo_fd_cb_t)(struct osmo_fd *fd, unsigned int what);
@@ -182,7 +184,7 @@ void validate_gtp_header(struct gtp_packet_desc *p) * error, p->rc is set <= 0 (see enum gtp_rc). */ static void gtp_decode(const uint8_t *data, int data_len, struct gtp_packet_desc *res) { - memset(res, '\0', sizeof(*res)); + ZERO_STRUCT(res); res->data = (union gtp_packet*)data; res->data_len = data_len;
@@ -221,7 +223,7 @@ uint32_t tei_pool_next(struct tei_pool *pool)
void tei_map_init(struct tei_map *map, struct tei_pool *pool) { - *map = (struct tei_map){}; + ZERO_STRUCT(map); map->pool = pool; INIT_LLIST_HEAD(&map->mappings); } @@ -283,7 +285,7 @@ void tei_map_del(struct tei_map *map, uint32_t tei_orig)
void gtphub_zero(struct gtphub *hub) { - memset(hub, '\0', sizeof(*hub)); + ZERO_STRUCT(hub); }
static int gtphub_sock_init(struct osmo_fd *ofd, @@ -316,7 +318,7 @@ static int gtphub_gtp_bind_init(struct gtphub_bind *b, osmo_fd_cb_t cb, void *cb_data, unsigned int ofd_id) { - memset(b, '\0', sizeof(*b)); + ZERO_STRUCT(b);
tei_pool_init(&b->teip); INIT_LLIST_HEAD(&b->peers);
Sponsored-by: On-Waves ehi
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index 1d9c6d5..e849a8f 100644 --- a/openbsc/src/gprs/gtphub.c +++ b/openbsc/src/gprs/gtphub.c @@ -107,6 +107,7 @@ void validate_gtp0_header(struct gtp_packet_desc *p) return; }
+ LOG("GTP v0 TID = %" PRIu64 "\n", pheader->tid); p->header_len = GTP0_HEADER_SIZE; p->rc = GTP_RC_PDU; } @@ -129,6 +130,20 @@ void validate_gtp1_header(struct gtp_packet_desc *p) return; }
+ LOG("|GTPv1\n"); + LOG("| type = %" PRIu8 " 0x%02" PRIx8 "\n", + pheader->type, pheader->type); + LOG("| length = %" PRIu16 " 0x%04" PRIx16 "\n", + ntoh16(pheader->length), ntoh16(pheader->length)); + LOG("| TEI = %" PRIu32 " 0x%08" PRIx32 "\n", + ntoh32(pheader->tei), ntoh32(pheader->tei)); + LOG("| seq = %" PRIu16 " 0x%04" PRIx16 "\n", + ntoh16(pheader->seq), ntoh16(pheader->seq)); + LOG("| npdu = %" PRIu8 " 0x%02" PRIx8 "\n", + pheader->npdu, pheader->npdu); + LOG("| next = %" PRIu8 " 0x%02" PRIx8 "\n", + pheader->next, pheader->next); + if (p->data_len <= GTP1_HEADER_SIZE_LONG) { p->rc = GTP_RC_TINY; p->header_len = GTP1_HEADER_SIZE_SHORT; @@ -366,6 +381,7 @@ static int gtp_relay(struct osmo_fd *from, }
/* insert magic here */ + LOG("Received %d\n%s\n", (int)received, osmo_hexdump(buf, received));
struct gtp_packet_desc p; gtp_decode(buf, received, &p); @@ -403,7 +419,7 @@ int from_ggsns_read_cb(struct osmo_fd *from_ggsns_ofd, unsigned int what) { unsigned int port_idx = from_ggsns_ofd->priv_nr; OSMO_ASSERT(port_idx < GTPH_PORT_N); - LOG("reading from GGSN (%s)\n", gtphub_port_idx_names[port_idx]); + LOG("\n\n=== reading from GGSN (%s)\n", gtphub_port_idx_names[port_idx]); if (!(what & BSC_FD_READ)) return 0;
@@ -426,7 +442,7 @@ int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what) { unsigned int port_idx = from_sgsns_ofd->priv_nr; OSMO_ASSERT(port_idx < GTPH_PORT_N); - LOG("reading from SGSN (%s)\n", gtphub_port_idx_names[port_idx]); + LOG("\n\n=== reading from SGSN (%s)\n", gtphub_port_idx_names[port_idx]);
if (!(what & BSC_FD_READ)) return 0; diff --git a/openbsc/src/gprs/gtphub_main.c b/openbsc/src/gprs/gtphub_main.c index b43e28d..f3237fe 100644 --- a/openbsc/src/gprs/gtphub_main.c +++ b/openbsc/src/gprs/gtphub_main.c @@ -200,6 +200,7 @@ int main(int argc, char **argv) LOGERR("Cannot resolve '%s port %d'\n", ggsn_addr_str, ggsn_port); exit(-1); } + LOG("DEBUG: using GGSN %s port %d\n", ggsn_addr_str, ggsn_port);
log_cfg(cfg);
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index e849a8f..73e7c02 100644 --- a/openbsc/src/gprs/gtphub.c +++ b/openbsc/src/gprs/gtphub.c @@ -23,6 +23,7 @@ #include <errno.h> #include <inttypes.h> #include <netinet/in.h> +#include <arpa/inet.h>
#include <gtp.h>
@@ -33,6 +34,8 @@ #include <osmocom/core/logging.h> #include <osmocom/core/socket.h>
+#define GTPHUB_DEBUG 1 + void *osmo_gtphub_ctx;
#define LOGERR(fmt, args...) \ @@ -54,6 +57,23 @@ typedef int (*osmo_fd_cb_t)(struct osmo_fd *fd, unsigned int what); #define ntoh16(x) ntohs(x) #define ntoh32(x) ntohl(x)
+/* cheat to use gtpie.h API. + * TODO publish gtpie.h upon openggsn installation */ +union gtpie_member; +extern int gtpie_decaps(union gtpie_member *ie[], int version, + void *pack, unsigned len); +extern int gtpie_gettv0(union gtpie_member *ie[], int type, int instance, + void *dst, unsigned int size); +extern int gtpie_gettv1(union gtpie_member *ie[], int type, int instance, + uint8_t *dst); +extern int gtpie_gettlv(union gtpie_member *ie[], int type, int instance, + unsigned int *length, void *dst, unsigned int size); +#define GTPIE_IMSI 2 /* International Mobile Subscriber Identity 8 */ +#define GTPIE_NSAPI 20 /* NSAPI 1 */ +#define GTPIE_GSN_ADDR 133 /* GSN Address */ +#define GTPIE_SIZE 256 +/* end of things needed from gtpie.h */ + /* TODO move GTP header stuff to openggsn/gtp/ ? See gtp_decaps*() */
enum gtp_rc { @@ -72,6 +92,7 @@ struct gtp_packet_desc { int header_len; int version; int rc; + union gtpie_member *ie[GTPIE_SIZE]; };
/* Validate GTP version 0 data; analogous to validate_gtp1_header(), see there. @@ -194,6 +215,45 @@ void validate_gtp_header(struct gtp_packet_desc *p) } }
+ +/* Return the value of the i'th IMSI IEI by copying to *imsi. + * The first IEI is reached by passing i = 0. + * imsi must point at allocated space of (at least) 8 bytes. + * Return 1 on success, or 0 if not found. */ +static int get_ie_imsi(union gtpie_member *ie[], uint8_t *imsi, int i) +{ + return gtpie_gettv0(ie, GTPIE_IMSI, i, imsi, 8) == 0; +} + +/* Analogous to get_ie_imsi(). nsapi must point at a single uint8_t. */ +static int get_ie_nsapi(union gtpie_member *ie[], uint8_t *nsapi, int i) +{ + return gtpie_gettv1(ie, GTPIE_NSAPI, i, nsapi) == 0; +} + +static char imsi_digit_to_char(uint8_t nibble) +{ + nibble &= 0x0f; + if (nibble > 9) + return (nibble == 0x0f) ? '\0' : '?'; + return '0' + nibble; +} + +/* Return a human readable IMSI string, in a static buffer. + * imsi must point at 8 octets of IMSI IE encoded IMSI data. */ +static const char *imsi_to_str(uint8_t *imsi) +{ + static char str[17]; + int i; + + for (i = 0; i < 8; i++) { + str[2*i] = imsi_digit_to_char(imsi[i]); + str[2*i + 1] = imsi_digit_to_char(imsi[i] >> 4); + } + str[16] = '\0'; + return str; +} + /* Validate header, and index information elements. Write decoded packet * information to *res. res->data will point at the given data buffer. On * error, p->rc is set <= 0 (see enum gtp_rc). */ @@ -207,6 +267,43 @@ static void gtp_decode(const uint8_t *data, int data_len, struct gtp_packet_desc
if (res->rc == GTP_RC_TINY) LOG("tiny: no IEs in this GTP packet\n"); + + if (res->rc != GTP_RC_PDU) + return; + + if (gtpie_decaps(res->ie, res->version, + (void*)(data + res->header_len), + res->data_len - res->header_len) != 0) { + res->rc = GTP_RC_INVALID_IE; + return; + } + +#if GTPHUB_DEBUG + int i; + + for (i = 0; i < 10; i++) { + uint8_t imsi[8]; + if (!get_ie_imsi(res->ie, imsi, i)) + break; + LOG("- IMSI %s\n", imsi_to_str(imsi)); + } + + for (i = 0; i < 10; i++) { + uint8_t nsapi; + if (!get_ie_nsapi(res->ie, &nsapi, i)) + break; + LOG("- NSAPI %d\n", (int)nsapi); + } + + for (i = 0; i < 10; i++) { + unsigned int addr_len; + struct in_addr addr; + if (gtpie_gettlv(res->ie, GTPIE_GSN_ADDR, i, &addr_len, &addr, + sizeof(addr)) != 0) + break; + LOG("- addr %s\n", inet_ntoa(addr)); + } +#endif }
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index 73e7c02..9f3e021 100644 --- a/openbsc/src/gprs/gtphub.c +++ b/openbsc/src/gprs/gtphub.c @@ -265,6 +265,9 @@ static void gtp_decode(const uint8_t *data, int data_len, struct gtp_packet_desc
validate_gtp_header(res);
+ if (res->rc > 0) + LOG("Valid GTP header (v%d)\n", res->version); + if (res->rc == GTP_RC_TINY) LOG("tiny: no IEs in this GTP packet\n");
@@ -441,23 +444,20 @@ static int gtphub_gtp_bind_init(struct gtphub_bind *b, }
/* 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) + * and *from_addr_len. Return the number of bytes read. */ +/* Send datagram to to->fd using *to_addr. to_addr may be NULL, if an address + * is set on to->fd. */ +static int gtphub_read(struct osmo_fd *from, + struct sockaddr_storage *from_addr, + socklen_t *from_addr_len, + uint8_t *buf, size_t buf_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, + ssize_t received = recvfrom(from->fd, buf, buf_len, 0, (struct sockaddr*)from_addr, from_addr_len); /* TODO use recvmsg and get a MSG_TRUNC flag to make sure the message * is not truncated. Then maybe reduce buf's size. */ @@ -465,7 +465,7 @@ static int gtp_relay(struct osmo_fd *from, if (received <= 0) { if (errno != EAGAIN) LOGERR("error: %s\n", strerror(errno)); - return -errno; + return 0; }
if (from_addr) { @@ -474,25 +474,20 @@ static int gtp_relay(struct osmo_fd *from,
if (received <= 0) { LOGERR("error: %s\n", strerror(errno)); - return -EINVAL; + return 0; }
- /* insert magic here */ LOG("Received %d\n%s\n", (int)received, osmo_hexdump(buf, received)); + return received; +}
- struct gtp_packet_desc p; - gtp_decode(buf, received, &p); - - if (p.rc > 0) - LOG("Valid GTP header (v%d)\n", p.version); -#if 0 - else - // error has been logged - return 0; -#endif - +static int gtphub_write(struct osmo_fd *to, + struct sockaddr_storage *to_addr, + socklen_t to_addr_len, + uint8_t *buf, size_t buf_len) +{ errno = 0; - ssize_t sent = sendto(to->fd, buf, received, 0, + ssize_t sent = sendto(to->fd, buf, buf_len, 0, (struct sockaddr*)to_addr, to_addr_len);
if (to_addr) { @@ -504,8 +499,8 @@ static int gtp_relay(struct osmo_fd *from, return -EINVAL; }
- if (sent != received) - LOGERR("sent(%d) != received(%d)\n", (int)sent, (int)received); + if (sent != buf_len) + LOGERR("sent(%d) != data_len(%d)\n", (int)sent, (int)buf_len); else LOG("%d b ok\n", (int)sent);
@@ -530,9 +525,23 @@ int from_ggsns_read_cb(struct osmo_fd *from_ggsns_ofd, unsigned int what) return 0; }
- return gtp_relay(from_ggsns_ofd, NULL, NULL, - &hub->to_sgsns[port_idx].ofd, - &sgsn->addr.a, sgsn->addr.l); + static uint8_t buf[4096]; + size_t received = gtphub_read(from_ggsns_ofd, NULL, NULL, + buf, sizeof(buf)); + if (received < 1) + return 0; + + static struct gtp_packet_desc p; + gtp_decode(buf, received, &p); + +#if 0 + if (p.rc <= 0) + return 0; +#endif + + return gtphub_write(&hub->to_sgsns[port_idx].ofd, + &sgsn->addr.a, sgsn->addr.l, + (uint8_t*)p.data, p.data_len); }
int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what) @@ -560,9 +569,23 @@ int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what) if (!sgsn) sgsn = gtphub_peer_new(&hub->to_sgsns[port_idx]);
- return gtp_relay(from_sgsns_ofd, &sgsn->addr.a, &sgsn->addr.l, - &hub->to_ggsns[port_idx].ofd, - &ggsn->addr.a, ggsn->addr.l); + static uint8_t buf[4096]; + size_t received = gtphub_read(from_sgsns_ofd, &sgsn->addr.a, &sgsn->addr.l, + buf, sizeof(buf)); + if (received < 1) + return 0; + + static struct gtp_packet_desc p; + gtp_decode(buf, received, &p); + +#if 0 + if (p.rc <= 0) + return 0; +#endif + + return gtphub_write(&hub->to_ggsns[port_idx].ofd, + &ggsn->addr.a, ggsn->addr.l, + (uint8_t*)p.data, p.data_len); }
int gtphub_init(struct gtphub *hub, struct gtphub_cfg *cfg)
Avoid sequence number collisions and allow routing a GGSN's response back to the SGSN that sent a request: - Towards each GGSN, send other sequence numbers than received from an SGSN, - and remember the mapping (with timeouts). - When receiving from a GGSN, find the SGSN from the sequence number returned.
This covers only requests by the SGSN followed by GGSN responses, it does not cover requests initiated by a GGSN.
Sponsored-by: On-Waves ehi
diff --git a/openbsc/include/openbsc/gtphub.h b/openbsc/include/openbsc/gtphub.h index 7354786..ccea5aa 100644 --- a/openbsc/include/openbsc/gtphub.h +++ b/openbsc/include/openbsc/gtphub.h @@ -23,6 +23,7 @@
#include <stdint.h> #include <sys/socket.h> +#include <sys/time.h>
#include <osmocom/core/select.h>
@@ -107,6 +108,18 @@ struct gtphub_peer {
struct gtphub_addr addr; struct tei_map teim; + uint16_t next_peer_seq; /* the latest used sequence nr + 1 */ + struct llist_head seq_map; /* of struct gtphub_seq_mapping */ + int ref_count; /* references from other peers' seq_maps */ +}; + +struct gtphub_seq_mapping { + struct llist_head entry; + + uint16_t peer_seq; + struct gtphub_peer *from; + uint16_t from_seq; + struct timeval timeout; };
struct gtphub_bind { diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index 9f3e021..f6733e3 100644 --- a/openbsc/src/gprs/gtphub.c +++ b/openbsc/src/gprs/gtphub.c @@ -22,6 +22,7 @@ #include <string.h> #include <errno.h> #include <inttypes.h> +#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>
@@ -35,6 +36,7 @@ #include <osmocom/core/socket.h>
#define GTPHUB_DEBUG 1 +#define MAP_SEQ 1
void *osmo_gtphub_ctx;
@@ -57,6 +59,8 @@ typedef int (*osmo_fd_cb_t)(struct osmo_fd *fd, unsigned int what); #define ntoh16(x) ntohs(x) #define ntoh32(x) ntohl(x)
+#define hton16(x) htons(x) + /* cheat to use gtpie.h API. * TODO publish gtpie.h upon openggsn installation */ union gtpie_member; @@ -87,7 +91,7 @@ enum gtp_rc { };
struct gtp_packet_desc { - const union gtp_packet *data; + union gtp_packet *data; int data_len; int header_len; int version; @@ -481,6 +485,63 @@ static int gtphub_read(struct osmo_fd *from, return received; }
+#if MAP_SEQ + +inline uint16_t get_seq(struct gtp_packet_desc *p) +{ + OSMO_ASSERT(p->version == 1); + return ntoh16(p->data->gtp1l.h.seq); +} + +inline void set_seq(struct gtp_packet_desc *p, uint16_t seq) +{ + OSMO_ASSERT(p->version == 1); + p->data->gtp1l.h.seq = hton16(seq); +} + +static int gtphub_map_seq(struct gtp_packet_desc *p, + struct gtphub_peer *from_peer, struct gtphub_peer *to_peer) +{ + + struct gtphub_seq_mapping *m = talloc_zero(osmo_gtphub_ctx, struct gtphub_seq_mapping); + OSMO_ASSERT(m); + + m->peer_seq = to_peer->next_peer_seq++; + m->from = from_peer; + m->from_seq = get_seq(p); + LOG(" MAP %d --> %d\n", (int)m->from_seq, (int)m->peer_seq); + + llist_add(&m->entry, &to_peer->seq_map); + set_seq(p, m->peer_seq); + + return 0; +} + +static struct gtphub_peer *gtphub_unmap_seq(struct gtp_packet_desc *p, + struct gtphub_peer *from_peer) +{ + OSMO_ASSERT(p->version == 1); + + uint16_t from_seq = get_seq(p); + + struct gtphub_seq_mapping *mapping; + llist_for_each_entry(mapping, &from_peer->seq_map, entry) { + if (mapping->peer_seq == from_seq) + break; + } + + if (&mapping->entry == &from_peer->seq_map) { + /* not found. */ + return NULL; + } + + LOG("UNMAP %d <-- %d\n", (int)(mapping->from_seq), (int)from_seq); + set_seq(p, mapping->from_seq); + return mapping->from; +} + +#endif + static int gtphub_write(struct osmo_fd *to, struct sockaddr_storage *to_addr, socklen_t to_addr_len, @@ -517,11 +578,14 @@ int from_ggsns_read_cb(struct osmo_fd *from_ggsns_ofd, unsigned int what)
struct gtphub *hub = from_ggsns_ofd->data;
+ struct gtphub_peer *ggsn = NULL; + struct gtphub_peer *sgsn = NULL; + /* TODO this will not be hardcoded. */ - struct gtphub_peer *sgsn = llist_first(&hub->to_sgsns[port_idx].peers, - struct gtphub_peer, entry); - if (!sgsn) { - LOGERR("no sgsn"); + ggsn = llist_first(&hub->to_ggsns[port_idx].peers, + struct gtphub_peer, entry); + if (!ggsn) { + LOGERR("no ggsn\n"); return 0; }
@@ -539,6 +603,20 @@ int from_ggsns_read_cb(struct osmo_fd *from_ggsns_ofd, unsigned int what) return 0; #endif
+#if MAP_SEQ + sgsn = gtphub_unmap_seq(&p, ggsn); +#else + /* TODO this will not be hardcoded. */ + sgsn = llist_first(&hub->to_sgsns[port_idx].peers, + struct gtphub_peer, entry); +#endif + + if (!sgsn) { + LOGERR("no sgsn"); + return 0; + } + + return gtphub_write(&hub->to_sgsns[port_idx].ofd, &sgsn->addr.a, sgsn->addr.l, (uint8_t*)p.data, p.data_len); @@ -583,6 +661,10 @@ int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what) return 0; #endif
+#if MAP_SEQ + gtphub_map_seq(&p, sgsn, ggsn); +#endif + return gtphub_write(&hub->to_ggsns[port_idx].ofd, &ggsn->addr.a, ggsn->addr.l, (uint8_t*)p.data, p.data_len); @@ -616,7 +698,9 @@ struct gtphub_peer *gtphub_peer_new(struct gtphub_bind *bind) { struct gtphub_peer *n = talloc_zero(osmo_gtphub_ctx, struct gtphub_peer);
+ INIT_LLIST_HEAD(&n->seq_map); tei_map_init(&n->teim, &bind->teip); + n->next_peer_seq = rand(); /* TODO seed or use something else */
llist_add(&n->entry, &bind->peers); return n;
diff --git a/openbsc/src/gprs/gtphub_main.c b/openbsc/src/gprs/gtphub_main.c index f3237fe..509c49f 100644 --- a/openbsc/src/gprs/gtphub_main.c +++ b/openbsc/src/gprs/gtphub_main.c @@ -19,12 +19,15 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */
+#include <unistd.h> +#include <signal.h> #include <string.h> #include <errno.h>
#define _GNU_SOURCE #include <getopt.h>
+#include <osmocom/core/signal.h> #include <osmocom/core/application.h> #include <osmocom/core/logging.h> #include <osmocom/core/utils.h> @@ -148,10 +151,38 @@ void log_cfg(struct gtphub_cfg *cfg) a->addr_str, a->port); }
+static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGINT: + osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); + sleep(1); + exit(0); + break; + case SIGABRT: + /* in case of abort, we want to obtain a talloc report + * and then return to the caller, who will abort the process */ + case SIGUSR1: + case SIGUSR2: + talloc_report_full(osmo_gtphub_ctx, stderr); + break; + default: + break; + } +} + int main(int argc, char **argv) { osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub");
+ signal(SIGINT, &signal_handler); + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); + osmo_init_ignore_signals(); + osmo_init_logging(>phub_log_info);
int rc;