@@ -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);
}
@@ -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;