From patchwork Thu Oct 15 22:13:45 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Neels Hofmeyr X-Patchwork-Id: 530956 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.osmocom.org (unknown [IPv6:2a01:4f8:191:444b::2:7]) by ozlabs.org (Postfix) with ESMTP id 18A7714029E for ; Fri, 16 Oct 2015 09:14:54 +1100 (AEDT) Received: from lists.osmocom.org (lists.osmocom.org [144.76.43.76]) by lists.osmocom.org (Postfix) with ESMTP id DEB0A8A58; Thu, 15 Oct 2015 22:14:47 +0000 (UTC) X-Original-To: openbsc@lists.osmocom.org Delivered-To: openbsc@lists.osmocom.org Received: from mail.sysmocom.de (mail.sysmocom.de [IPv6:2a01:4f8:191:444c::2:4]) by lists.osmocom.org (Postfix) with ESMTP id 24CF089FD for ; Thu, 15 Oct 2015 22:14:46 +0000 (UTC) Received: from localhost.localdomain (unknown [37.120.3.39]) by mail.sysmocom.de (Postfix) with ESMTPSA id D65971BEEAE for ; Thu, 15 Oct 2015 22:14:15 +0000 (UTC) From: Neels Hofmeyr To: openbsc@lists.osmocom.org Subject: [PATCH 01/15] Add GTP hub stub, as simplistic UDP forwarder. Date: Fri, 16 Oct 2015 00:13:45 +0200 Message-Id: <1444947239-17582-2-git-send-email-nhofmeyr@sysmocom.de> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1444947239-17582-1-git-send-email-nhofmeyr@sysmocom.de> References: <1444947239-17582-1-git-send-email-nhofmeyr@sysmocom.de> X-BeenThere: openbsc@lists.osmocom.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Development of the OpenBSC GSM base station controller List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: openbsc-bounces@lists.osmocom.org Sender: "OpenBSC" 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 + * 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 . + */ + +#include +#include +#include +#include + +#include +#include + +#define _GNU_SOURCE +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#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 +/* 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 \r\n" + "License AGPLv3+: GNU AGPL version 2 or later \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); +}