aboutsummaryrefslogtreecommitdiffstats
path: root/src/gprs
diff options
context:
space:
mode:
authorPau Espin Pedrol <pespin@sysmocom.de>2019-08-30 19:50:48 +0200
committerPau Espin Pedrol <pespin@sysmocom.de>2019-09-02 14:03:04 +0200
commitea05d5c5f89f034a8d2c4151507f59a520535d9a (patch)
tree18f394d35c6c0db1f6921d90ec78a292bc4c641c /src/gprs
parent9d016fd499cb23ad6d9e4c2757dbc4e1c154107d (diff)
Move out gtphub to its own subdir
Diffstat (limited to 'src/gprs')
-rw-r--r--src/gprs/Makefile.am19
-rw-r--r--src/gprs/gtphub.c2960
-rw-r--r--src/gprs/gtphub_ares.c220
-rw-r--r--src/gprs/gtphub_main.c403
-rw-r--r--src/gprs/gtphub_sock.c60
-rw-r--r--src/gprs/gtphub_vty.c613
6 files changed, 0 insertions, 4275 deletions
diff --git a/src/gprs/Makefile.am b/src/gprs/Makefile.am
index 8f7f054e7..e5d50d375 100644
--- a/src/gprs/Makefile.am
+++ b/src/gprs/Makefile.am
@@ -56,7 +56,6 @@ libcommon_la_LIBADD = \
bin_PROGRAMS = \
osmo-sgsn \
- osmo-gtphub \
$(NULL)
osmo_sgsn_SOURCES = \
@@ -103,21 +102,3 @@ osmo_sgsn_LDADD += \
$(LIBASN1C_LIBS) \
$(NULL)
endif
-
-osmo_gtphub_SOURCES = \
- gtphub_main.c \
- gtphub.c \
- gtphub_sock.c \
- gtphub_ares.c \
- gtphub_vty.c \
- $(NULL)
-osmo_gtphub_LDADD = \
- libcommon.la \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOVTY_LIBS) \
- $(LIBCARES_LIBS) \
- $(LIBGTP_LIBS) \
- $(LIBOSMOSIGTRAN_LIBS) \
- -lrt \
- $(NULL)
diff --git a/src/gprs/gtphub.c b/src/gprs/gtphub.c
deleted file mode 100644
index ca5857b39..000000000
--- a/src/gprs/gtphub.c
+++ /dev/null
@@ -1,2960 +0,0 @@
-/* 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 <errno.h>
-#include <inttypes.h>
-#include <time.h>
-#include <limits.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <gtp.h>
-#include <gtpie.h>
-
-#include <osmocom/sgsn/gtphub.h>
-#include <osmocom/sgsn/debug.h>
-#include <osmocom/sgsn/gprs_utils.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/socket.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/stats.h>
-
-#include <osmocom/gsm/apn.h>
-
-
-static const int GTPH_GC_TICK_SECONDS = 1;
-
-void *osmo_gtphub_ctx;
-
-/* Convenience makro, note: only within this C file. */
-#define LOG(level, fmt, args...) \
- LOGP(DGTPHUB, level, 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);
-
-/* 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)
-
-#define __llist_last(head) (((head)->next == (head)) ? NULL : (head)->prev)
-#define llist_last(head, type, entry) \
- llist_entry(__llist_last(head), type, entry)
-
-/* 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_C = 2, /* a real packet with IEs */
- GTP_RC_PDU_U = 3, /* a real packet with User data */
-
- GTP_RC_TOOSHORT = -1,
- GTP_RC_UNSUPPORTED_VERSION = -2,
- GTP_RC_INVALID_IE = -3,
-};
-
-struct gtp_packet_desc {
- union gtp_packet *data;
- int data_len;
- int header_len;
- int version;
- uint8_t type;
- uint16_t seq;
- uint32_t header_tei_rx;
- uint32_t header_tei;
- int rc; /* enum gtp_rc */
- unsigned int plane_idx;
- unsigned int side_idx;
- struct gtphub_tunnel *tun;
- time_t timestamp;
- union gtpie_member *ie[GTPIE_SIZE];
-};
-
-struct pending_delete {
- struct llist_head entry;
- struct expiring_item expiry_entry;
-
- struct gtphub_tunnel *tun;
- uint8_t teardown_ind;
- uint8_t nsapi;
-};
-
-
-/* counters */
-
-enum gtphub_counters_io {
- GTPH_CTR_PKTS_IN = 0,
- GTPH_CTR_PKTS_OUT,
- GTPH_CTR_BYTES_IN,
- GTPH_CTR_BYTES_OUT
-};
-
-static const struct rate_ctr_desc gtphub_counters_io_desc[] = {
- { "packets.in", "Packets ( In)" },
- { "packets.out", "Packets (Out)" },
- { "bytes.in", "Bytes ( In)" },
- { "bytes.out", "Bytes (Out)" },
-};
-
-static const struct rate_ctr_group_desc gtphub_ctrg_io_desc = {
- .group_name_prefix = "gtphub:bind",
- .group_description = "I/O Statistics",
- .num_ctr = ARRAY_SIZE(gtphub_counters_io_desc),
- .ctr_desc = gtphub_counters_io_desc,
- .class_id = OSMO_STATS_CLASS_GLOBAL,
-};
-
-
-/* support */
-
-static const char *gtp_type_str(uint8_t type)
-{
- switch (type) {
- case 1:
- return " (Echo Request)";
- case 2:
- return " (Echo Response)";
- case 16:
- return " (Create PDP Ctx Request)";
- case 17:
- return " (Create PDP Ctx Response)";
- case 18:
- return " (Update PDP Ctx Request)";
- case 19:
- return " (Update PDP Ctx Response)";
- case 20:
- return " (Delete PDP Ctx Request)";
- case 21:
- return " (Delete PDP Ctx Response)";
- case 255:
- return " (User Data)";
- default:
- return "";
- }
-}
-
-void gsn_addr_copy(struct gsn_addr *gsna, const struct gsn_addr *src)
-{
- *gsna = *src;
-}
-
-int gsn_addr_from_sockaddr(struct gsn_addr *gsna, uint16_t *port,
- const struct osmo_sockaddr *sa)
-{
- char addr_str[256];
- char port_str[6];
-
- if (osmo_sockaddr_to_strs(addr_str, sizeof(addr_str),
- port_str, sizeof(port_str),
- sa, (NI_NUMERICHOST | NI_NUMERICSERV))
- != 0) {
- return -1;
- }
-
- if (port)
- *port = atoi(port_str);
-
- return gsn_addr_from_str(gsna, addr_str);
-}
-
-int gsn_addr_from_str(struct gsn_addr *gsna, const char *numeric_addr_str)
-{
- if ((!gsna) || (!numeric_addr_str))
- return -1;
-
- int af = AF_INET;
- gsna->len = 4;
- const char *pos = numeric_addr_str;
- for (; *pos; pos++) {
- if (*pos == ':') {
- af = AF_INET6;
- gsna->len = 16;
- break;
- }
- }
-
- int rc = inet_pton(af, numeric_addr_str, gsna->buf);
- if (rc != 1) {
- LOG(LOGL_ERROR, "Cannot resolve numeric address: '%s'\n",
- numeric_addr_str);
- return -1;
- }
- return 0;
-}
-
-const char *gsn_addr_to_str(const struct gsn_addr *gsna)
-{
- static char buf[INET6_ADDRSTRLEN + 1];
- return gsn_addr_to_strb(gsna, buf, sizeof(buf));
-}
-
-const char *gsn_addr_to_strb(const struct gsn_addr *gsna,
- char *strbuf,
- int strbuf_len)
-{
- int af;
- switch (gsna->len) {
- case 4:
- af = AF_INET;
- break;
- case 16:
- af = AF_INET6;
- break;
- default:
- return NULL;
- }
-
- const char *r = inet_ntop(af, gsna->buf, strbuf, strbuf_len);
- if (!r) {
- LOG(LOGL_ERROR, "Cannot convert gsn_addr to string:"
- " %s: len=%d, buf=%s\n",
- strerror(errno),
- (int)gsna->len,
- osmo_hexdump(gsna->buf, sizeof(gsna->buf)));
- }
- return r;
-}
-
-int gsn_addr_same(const struct gsn_addr *a, const struct gsn_addr *b)
-{
- if (a == b)
- return 1;
- if ((!a) || (!b))
- return 0;
- if (a->len != b->len)
- return 0;
- return (memcmp(a->buf, b->buf, a->len) == 0)? 1 : 0;
-}
-
-static int gsn_addr_get(struct gsn_addr *gsna, const struct gtp_packet_desc *p,
- int idx)
-{
- if (p->rc != GTP_RC_PDU_C)
- return -1;
-
- unsigned int len;
- /* gtpie.h fails to declare gtpie_gettlv()'s first arg as const. */
- if (gtpie_gettlv((union gtpie_member**)p->ie, GTPIE_GSN_ADDR, idx,
- &len, gsna->buf, sizeof(gsna->buf))
- != 0)
- return -1;
- gsna->len = len;
- return 0;
-}
-
-static int gsn_addr_put(const struct gsn_addr *gsna, struct gtp_packet_desc *p,
- int idx)
-{
- if (p->rc != GTP_RC_PDU_C)
- return -1;
-
- int ie_idx;
- ie_idx = gtpie_getie(p->ie, GTPIE_GSN_ADDR, idx);
-
- if (ie_idx < 0)
- return -1;
-
- struct gtpie_tlv *ie = &p->ie[ie_idx]->tlv;
- int ie_l = ntoh16(ie->l);
- if (ie_l != gsna->len) {
- LOG(LOGL_ERROR, "Not implemented:"
- " replace an IE address of different size:"
- " replace %d with %d\n", (int)ie_l, (int)gsna->len);
- return -1;
- }
-
- memcpy(ie->v, gsna->buf, (int)ie_l);
- return 0;
-}
-
-/* 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) {
- LOG(LOGL_ERROR, "GTP0 packet too short: %d\n", p->data_len);
- p->rc = GTP_RC_TOOSHORT;
- return;
- }
-
- p->type = ntoh8(pheader->type);
- p->seq = ntoh16(pheader->seq);
- p->header_tei_rx = 0; /* TODO */
- p->header_tei = p->header_tei_rx;
-
- 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)) {
- LOG(LOGL_ERROR, "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;
- }
-
- LOG(LOGL_DEBUG, "GTP v0 TID = %" PRIu64 "\n", pheader->tid);
- p->header_len = GTP0_HEADER_SIZE;
- p->rc = GTP_RC_PDU_C;
-}
-
-/* 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)){
- LOG(LOGL_ERROR, "GTP packet too short: %d\n", p->data_len);
- p->rc = GTP_RC_TOOSHORT;
- return;
- }
-
- p->type = ntoh8(pheader->type);
- p->header_tei_rx = ntoh32(pheader->tei);
- p->header_tei = p->header_tei_rx;
- p->seq = ntoh16(pheader->seq);
-
- LOG(LOGL_DEBUG, "| GTPv1\n");
- LOG(LOGL_DEBUG, "| type = %" PRIu8 " 0x%02" PRIx8 "\n", p->type, p->type);
- LOG(LOGL_DEBUG, "| length = %" PRIu16 " 0x%04" PRIx16 "\n", ntoh16(pheader->length), ntoh16(pheader->length));
- LOG(LOGL_DEBUG, "| TEI = %" PRIu32 " 0x%08" PRIx32 "\n", p->header_tei_rx, p->header_tei_rx);
- LOG(LOGL_DEBUG, "| seq = %" PRIu16 " 0x%04" PRIx16 "\n", p->seq, p->seq);
- LOG(LOGL_DEBUG, "| npdu = %" PRIu8 " 0x%02" PRIx8 "\n", pheader->npdu, pheader->npdu);
- LOG(LOGL_DEBUG, "| 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;
- return;
- }
-
- /* Check packet length field versus length of packet */
- int announced_len = ntoh16(pheader->length) + GTP1_HEADER_SIZE_SHORT;
- if (p->data_len != announced_len) {
- LOG(LOGL_ERROR, "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_C;
- 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) {
- LOG(LOGL_ERROR, "Discarding packet - too small: %d\n",
- p->data_len);
- 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:
- LOG(LOGL_ERROR, "Unsupported GTP version: %d\n", p->version);
- p->rc = GTP_RC_UNSUPPORTED_VERSION;
- break;
- }
-}
-
-
-/* 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[], int i, uint8_t *imsi)
-{
- 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[], int i, uint8_t *nsapi)
-{
- 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 int imsi_to_str(uint8_t *imsi, const char **imsi_str)
-{
- static char str[17];
- int i;
-
- for (i = 0; i < 8; i++) {
- char c;
- c = imsi_digit_to_char(imsi[i]);
- if (c == '?')
- return -1;
- str[2*i] = c;
-
- c = imsi_digit_to_char(imsi[i] >> 4);
- if (c == '?')
- return -1;
- str[2*i + 1] = c;
- }
- str[16] = '\0';
- *imsi_str = str;
- return 1;
-}
-
-/* Return 0 if not present, 1 if present and decoded successfully, -1 if
- * present but cannot be decoded. */
-static int get_ie_imsi_str(union gtpie_member *ie[], int i,
- const char **imsi_str)
-{
- uint8_t imsi_buf[8];
- if (!get_ie_imsi(ie, i, imsi_buf))
- return 0;
- return imsi_to_str(imsi_buf, imsi_str);
-}
-
-/* Return 0 if not present, 1 if present and decoded successfully, -1 if
- * present but cannot be decoded. */
-static int get_ie_apn_str(union gtpie_member *ie[], const char **apn_str)
-{
- static char apn_buf[GSM_APN_LENGTH];
- unsigned int len;
- if (gtpie_gettlv(ie, GTPIE_APN, 0,
- &len, apn_buf, sizeof(apn_buf)) != 0)
- return 0;
-
- if (len < 2) {
- LOG(LOGL_ERROR, "APN IE: invalid length: %d\n",
- (int)len);
- return -1;
- }
-
- if (len > (sizeof(apn_buf) - 1))
- len = sizeof(apn_buf) - 1;
- apn_buf[len] = '\0';
-
- *apn_str = osmo_apn_to_str(apn_buf, (uint8_t*)apn_buf, len);
- if (!(*apn_str)) {
- LOG(LOGL_ERROR, "APN IE: present but cannot be decoded: %s\n",
- osmo_hexdump((uint8_t*)apn_buf, len));
- return -1;
- }
- return 1;
-}
-
-
-/* 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,
- unsigned int from_side_idx,
- unsigned int from_plane_idx,
- struct gtp_packet_desc *res,
- time_t now)
-{
- ZERO_STRUCT(res);
- res->data = (union gtp_packet*)data;
- res->data_len = data_len;
- res->side_idx = from_side_idx;
- res->plane_idx = from_plane_idx;
- res->timestamp = now;
-
- validate_gtp_header(res);
-
- if (res->rc <= 0)
- return;
-
- LOG(LOGL_DEBUG, "Valid GTP header (v%d)\n", res->version);
-
- if (from_plane_idx == GTPH_PLANE_USER) {
- res->rc = GTP_RC_PDU_U;
- return;
- }
-
- if (res->rc != GTP_RC_PDU_C) {
- LOG(LOGL_DEBUG, "no IEs in this GTP packet\n");
- 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;
- LOG(LOGL_ERROR, "INVALID: cannot decode IEs."
- " Dropping GTP packet%s.\n",
- gtp_type_str(res->type)
- );
- return;
- }
-
-#if 1
- /* TODO if (<loglevel is debug>)
- (waiting for a commit from jerlbeck) */
- int i;
-
- for (i = 0; i < 10; i++) {
- const char *imsi;
- if (get_ie_imsi_str(res->ie, i, &imsi) < 1)
- break;
- LOG(LOGL_DEBUG, "| IMSI %s\n", imsi);
- }
-
- for (i = 0; i < 10; i++) {
- uint8_t nsapi;
- if (!get_ie_nsapi(res->ie, i, &nsapi))
- break;
- LOG(LOGL_DEBUG, "| NSAPI %d\n", (int)nsapi);
- }
-
- for (i = 0; i < 2; i++) {
- struct gsn_addr addr;
- if (gsn_addr_get(&addr, res, i) == 0)
- LOG(LOGL_DEBUG, "| addr %s\n", gsn_addr_to_str(&addr));
- }
-
- for (i = 0; i < 10; i++) {
- uint32_t tei;
- if (gtpie_gettv4(res->ie, GTPIE_TEI_DI, i, &tei) != 0)
- break;
- LOG(LOGL_DEBUG, "| TEI DI (USER) %" PRIu32 " 0x%08" PRIx32 "\n",
- tei, tei);
- }
-
- for (i = 0; i < 10; i++) {
- uint32_t tei;
- if (gtpie_gettv4(res->ie, GTPIE_TEI_C, i, &tei) != 0)
- break;
- LOG(LOGL_DEBUG, "| TEI (CTRL) %" PRIu32 " 0x%08" PRIx32 "\n",
- tei, tei);
- }
-#endif
-}
-
-
-/* expiry */
-
-void expiry_init(struct expiry *exq, int expiry_in_seconds)
-{
- ZERO_STRUCT(exq);
- exq->expiry_in_seconds = expiry_in_seconds;
- INIT_LLIST_HEAD(&exq->items);
-}
-
-void expiry_add(struct expiry *exq, struct expiring_item *item, time_t now)
-{
- item->expiry = now + exq->expiry_in_seconds;
-
- OSMO_ASSERT(llist_empty(&exq->items)
- || (item->expiry
- >= llist_last(&exq->items, struct expiring_item, entry)->expiry));
-
- /* Add/move to the tail to always sort by expiry, ascending. */
- llist_del(&item->entry);
- llist_add_tail(&item->entry, &exq->items);
-}
-
-int expiry_tick(struct expiry *exq, time_t now)
-{
- int expired = 0;
- struct expiring_item *m, *n;
- llist_for_each_entry_safe(m, n, &exq->items, entry) {
- if (m->expiry <= now) {
- expiring_item_del(m);
- expired ++;
- } else {
- /* The items are added sorted by expiry. So when we hit
- * an unexpired entry, only more unexpired ones will
- * follow. */
- break;
- }
- }
- return expired;
-}
-
-void expiry_clear(struct expiry *exq)
-{
- struct expiring_item *m, *n;
- llist_for_each_entry_safe(m, n, &exq->items, entry) {
- expiring_item_del(m);
- }
-}
-
-void expiring_item_init(struct expiring_item *item)
-{
- ZERO_STRUCT(item);
- INIT_LLIST_HEAD(&item->entry);
-}
-
-void expiring_item_del(struct expiring_item *item)
-{
- OSMO_ASSERT(item);
- llist_del(&item->entry);
- INIT_LLIST_HEAD(&item->entry);
- if (item->del_cb) {
- /* avoid loops */
- del_cb_t del_cb = item->del_cb;
- item->del_cb = 0;
- (del_cb)(item);
- }
-}
-
-
-/* nr_map, nr_pool */
-
-void nr_pool_init(struct nr_pool *pool, nr_t nr_min, nr_t nr_max)
-{
- *pool = (struct nr_pool){
- .nr_min = nr_min,
- .nr_max = nr_max,
- .last_nr = nr_max
- };
-}
-
-nr_t nr_pool_next(struct nr_pool *pool)
-{
- if (pool->last_nr >= pool->nr_max)
- pool->last_nr = pool->nr_min;
- else
- pool->last_nr ++;
-
- return pool->last_nr;
-}
-
-void nr_map_init(struct nr_map *map, struct nr_pool *pool,
- struct expiry *exq)
-{
- ZERO_STRUCT(map);
- map->pool = pool;
- map->add_items_to_expiry = exq;
- INIT_LLIST_HEAD(&map->mappings);
-}
-
-void nr_mapping_init(struct nr_mapping *m)
-{
- ZERO_STRUCT(m);
- INIT_LLIST_HEAD(&m->entry);
- expiring_item_init(&m->expiry_entry);
-}
-
-void nr_map_add(struct nr_map *map, struct nr_mapping *mapping, time_t now)
-{
- /* Generate a mapped number */
- mapping->repl = nr_pool_next(map->pool);
-
- /* Add to the tail to always yield a list sorted by expiry, in
- * ascending order. */
- llist_add_tail(&mapping->entry, &map->mappings);
- nr_map_refresh(map, mapping, now);
-}
-
-void nr_map_refresh(struct nr_map *map, struct nr_mapping *mapping, time_t now)
-{
- if (!map->add_items_to_expiry)
- return;
- expiry_add(map->add_items_to_expiry,
- &mapping->expiry_entry,
- now);
-}
-
-void nr_map_clear(struct nr_map *map)
-{
- struct nr_mapping *m;
- struct nr_mapping *n;
- llist_for_each_entry_safe(m, n, &map->mappings, entry) {
- nr_mapping_del(m);
- }
-}
-
-int nr_map_empty(const struct nr_map *map)
-{
- return llist_empty(&map->mappings);
-}
-
-struct nr_mapping *nr_map_get(const struct nr_map *map,
- void *origin, nr_t nr_orig)
-{
- struct nr_mapping *mapping;
- llist_for_each_entry(mapping, &map->mappings, entry) {
- if ((mapping->origin == origin)
- && (mapping->orig == nr_orig))
- return mapping;
- }
- /* Not found. */
- return NULL;
-}
-
-struct nr_mapping *nr_map_get_inv(const struct nr_map *map, nr_t nr_repl)
-{
- struct nr_mapping *mapping;
- llist_for_each_entry(mapping, &map->mappings, entry) {
- if (mapping->repl == nr_repl) {
- return mapping;
- }
- }
- /* Not found. */
- return NULL;
-}
-
-void nr_mapping_del(struct nr_mapping *mapping)
-{
- OSMO_ASSERT(mapping);
- llist_del(&mapping->entry);
- INIT_LLIST_HEAD(&mapping->entry);
- expiring_item_del(&mapping->expiry_entry);
-}
-
-
-/* gtphub */
-
-const char* const gtphub_plane_idx_names[GTPH_PLANE_N] = {
- "CTRL",
- "USER",
-};
-
-const uint16_t gtphub_plane_idx_default_port[GTPH_PLANE_N] = {
- 2123,
- 2152,
-};
-
-const char* const gtphub_side_idx_names[GTPH_SIDE_N] = {
- "SGSN",
- "GGSN",
-};
-
-time_t gtphub_now(void)
-{
- struct timespec now_tp;
- OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &now_tp) >= 0);
- return now_tp.tv_sec;
-}
-
-/* Remove a gtphub_peer from its list and free it. */
-static void gtphub_peer_del(struct gtphub_peer *peer)
-{
- OSMO_ASSERT(llist_empty(&peer->addresses));
- nr_map_clear(&peer->seq_map);
- llist_del(&peer->entry);
- talloc_free(peer);
-}
-
-static void gtphub_peer_addr_del(struct gtphub_peer_addr *pa)
-{
- OSMO_ASSERT(llist_empty(&pa->ports));
- llist_del(&pa->entry);
- talloc_free(pa);
-}
-
-static void gtphub_peer_port_del(struct gtphub_peer_port *pp)
-{
- OSMO_ASSERT(pp->ref_count == 0);
- llist_del(&pp->entry);
- rate_ctr_group_free(pp->counters_io);
- talloc_free(pp);
-}
-
-/* From the information in the gtp_packet_desc, return the address of a GGSN.
- * Return -1 on error. */
-static int gtphub_resolve_ggsn(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port **pp);
-
-/* See gtphub_ext.c (wrapped by unit test) */
-struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub,
- const char *imsi_str,
- const char *apn_ni_str);
-int gtphub_ares_init(struct gtphub *hub);
-
-static void gtphub_zero(struct gtphub *hub)
-{
- ZERO_STRUCT(hub);
- INIT_LLIST_HEAD(&hub->ggsn_lookups);
- INIT_LLIST_HEAD(&hub->resolved_ggsns);
-}
-
-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)
-{
- if (!addr->addr_str) {
- LOG(LOGL_FATAL, "Cannot bind: empty address.\n");
- return -1;
- }
- if (!addr->port) {
- LOG(LOGL_FATAL, "Cannot bind: zero port not permitted.\n");
- return -1;
- }
-
- 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) {
- LOG(LOGL_FATAL, "Cannot bind to %s port %d (rc %d)\n",
- addr->addr_str, (int)addr->port, rc);
- return -1;
- }
-
- return 0;
-}
-
-static void gtphub_sock_close(struct osmo_fd *ofd)
-{
- close(ofd->fd);
- osmo_fd_unregister(ofd);
- ofd->cb = NULL;
-}
-
-static void gtphub_bind_init(struct gtphub_bind *b, uint32_t idx)
-{
- ZERO_STRUCT(b);
-
- INIT_LLIST_HEAD(&b->peers);
-
- b->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx,
- &gtphub_ctrg_io_desc, idx);
- OSMO_ASSERT(b->counters_io);
-}
-
-static int gtphub_bind_start(struct gtphub_bind *b,
- const struct gtphub_cfg_bind *cfg,
- osmo_fd_cb_t cb, void *cb_data,
- unsigned int ofd_id)
-{
- LOG(LOGL_DEBUG, "Starting bind %s\n", b->label);
- if (gsn_addr_from_str(&b->local_addr, cfg->bind.addr_str) != 0) {
- LOG(LOGL_FATAL, "Invalid bind address for %s: %s\n",
- b->label, cfg->bind.addr_str);
- return -1;
- }
- if (gtphub_sock_init(&b->ofd, &cfg->bind, cb, cb_data, ofd_id) != 0) {
- LOG(LOGL_FATAL, "Cannot bind for %s: %s\n",
- b->label, cfg->bind.addr_str);
- return -1;
- }
- b->local_port = cfg->bind.port;
- return 0;
-}
-
-static void gtphub_bind_free(struct gtphub_bind *b)
-{
- OSMO_ASSERT(llist_empty(&b->peers));
- if (b->counters_io)
- rate_ctr_group_free(b->counters_io);
-}
-
-static void gtphub_bind_stop(struct gtphub_bind *b) {
- gtphub_sock_close(&b->ofd);
- gtphub_bind_free(b);
-}
-
-/* Recv datagram from from->fd, write sender's address to *from_addr.
- * Return the number of bytes read, zero on error. */
-static int gtphub_read(const struct osmo_fd *from,
- struct osmo_sockaddr *from_addr,
- uint8_t *buf, size_t buf_len)
-{
- OSMO_ASSERT(from_addr);
-
- /* recvfrom requires the available length set in *from_addr_len. */
- from_addr->l = sizeof(from_addr->a);
- errno = 0;
- ssize_t received = recvfrom(from->fd, buf, buf_len, 0,
- (struct sockaddr*)&from_addr->a,
- &from_addr->l);
- /* 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) {
- LOG((errno == EAGAIN? LOGL_DEBUG : LOGL_ERROR),
- "error: %s\n", strerror(errno));
- return 0;
- }
-
- LOG(LOGL_DEBUG, "Received %d bytes from %s: %s%s\n",
- (int)received, osmo_sockaddr_to_str(from_addr),
- osmo_hexdump(buf, received > 1000? 1000 : received),
- received > 1000 ? "..." : "");
-
- return received;
-}
-
-static inline void gtphub_port_ref_count_inc(struct gtphub_peer_port *pp)
-{
- OSMO_ASSERT(pp);
- OSMO_ASSERT(pp->ref_count < UINT_MAX);
- pp->ref_count++;
-}
-
-static inline void gtphub_port_ref_count_dec(struct gtphub_peer_port *pp)
-{
- OSMO_ASSERT(pp);
- OSMO_ASSERT(pp->ref_count > 0);
- pp->ref_count--;
-}
-
-static inline void set_seq(struct gtp_packet_desc *p, uint16_t seq)
-{
- OSMO_ASSERT(p->version == 1);
- p->data->gtp1l.h.seq = hton16(seq);
- p->seq = seq;
-}
-
-static inline void set_tei(struct gtp_packet_desc *p, uint32_t tei)
-{
- OSMO_ASSERT(p->version == 1);
- p->data->gtp1l.h.tei = hton32(tei);
- p->header_tei = tei;
-}
-
-static void gtphub_mapping_del_cb(struct expiring_item *expi);
-
-static struct nr_mapping *gtphub_mapping_new()
-{
- struct nr_mapping *nrm;
- nrm = talloc_zero(osmo_gtphub_ctx, struct nr_mapping);
- OSMO_ASSERT(nrm);
-
- nr_mapping_init(nrm);
- nrm->expiry_entry.del_cb = gtphub_mapping_del_cb;
- return nrm;
-}
-
-
-#define APPEND(args...) \
- l = snprintf(pos, left, args); \
- pos += l; \
- left -= l
-
-static const char *gtphub_tunnel_side_str(struct gtphub_tunnel *tun,
- int side_idx)
-{
- static char buf[256];
- char *pos = buf;
- int left = sizeof(buf);
- int l;
-
- struct gtphub_tunnel_endpoint *c, *u;
- c = &tun->endpoint[side_idx][GTPH_PLANE_CTRL];
- u = &tun->endpoint[side_idx][GTPH_PLANE_USER];
-
- /* print both only if they differ. */
- if (!c->peer) {
- APPEND("(uninitialized)");
- } else {
- APPEND("%s", gsn_addr_to_str(&c->peer->peer_addr->addr));
- }
-
- if (!u->peer) {
- if (c->peer) {
- APPEND("/(uninitialized)");
- }
- } else if ((!c->peer)
- || (!gsn_addr_same(&u->peer->peer_addr->addr,
- &c->peer->peer_addr->addr))) {
- APPEND("/%s", gsn_addr_to_str(&u->peer->peer_addr->addr));
- }
-
- APPEND(" (TEI C=%x U=%x)",
- c->tei_orig,
- u->tei_orig);
- return buf;
-}
-
-const char *gtphub_tunnel_str(struct gtphub_tunnel *tun)
-{
- static char buf[512];
- char *pos = buf;
- int left = sizeof(buf);
- int l;
-
- if (!tun)
- return "null-tunnel";
-
- APPEND("TEI=%x: ", tun->tei_repl);
- APPEND("%s", gtphub_tunnel_side_str(tun, GTPH_SIDE_SGSN));
- APPEND(" <-> %s", gtphub_tunnel_side_str(tun, GTPH_SIDE_GGSN));
-
- return buf;
-}
-
-#undef APPEND
-
-void gtphub_tunnel_endpoint_set_peer(struct gtphub_tunnel_endpoint *te,
- struct gtphub_peer_port *pp)
-{
- if (te->peer)
- gtphub_port_ref_count_dec(te->peer);
- te->peer = pp;
- if (te->peer)
- gtphub_port_ref_count_inc(te->peer);
-}
-
-int gtphub_tunnel_complete(struct gtphub_tunnel *tun)
-{
- if (!tun)
- return 0;
- if (!tun->tei_repl)
- return 0;
- int side_idx;
- int plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- struct gtphub_tunnel_endpoint *te =
- &tun->endpoint[side_idx][plane_idx];
- if (!(te->peer && te->tei_orig))
- return 0;
- }
- return 1;
-}
-
-static void gtphub_tunnel_del_cb(struct expiring_item *expi)
-{
- struct gtphub_tunnel *tun = container_of(expi,
- struct gtphub_tunnel,
- expiry_entry);
- LOG(LOGL_DEBUG, "expired: %s\n", gtphub_tunnel_str(tun));
-
- llist_del(&tun->entry);
- INIT_LLIST_HEAD(&tun->entry); /* mark unused */
-
- expi->del_cb = 0; /* avoid recursion loops */
- expiring_item_del(&tun->expiry_entry); /* usually already done, but make sure. */
-
- int side_idx;
- int plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx];
-
- /* clear ref count */
- gtphub_tunnel_endpoint_set_peer(te, NULL);
-
- rate_ctr_group_free(te->counters_io);
- }
-
- talloc_free(tun);
-}
-
-#define CTR_IDX(s, p, a, b) (a + s + (p + b) * 2)
-
-/* rate counter index for tunnels: [3; 6] */
-#define CTR_IDX_TUN(s, p) CTR_IDX(s, p, 1, 1)
-
-/* rate counter index for hubs: [7; 10] */
-#define CTR_IDX_HUB(s, p) CTR_IDX(s, p, 3, 2)
-
-static struct gtphub_tunnel *gtphub_tunnel_new()
-{
- struct gtphub_tunnel *tun;
- tun = talloc_zero(osmo_gtphub_ctx, struct gtphub_tunnel);
- OSMO_ASSERT(tun);
-
- INIT_LLIST_HEAD(&tun->entry);
- expiring_item_init(&tun->expiry_entry);
-
- int side_idx, plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx];
- te->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx,
- &gtphub_ctrg_io_desc,
- CTR_IDX_TUN(side_idx, plane_idx));
- if (!te->counters_io)
- return NULL;
- }
-
- tun->expiry_entry.del_cb = gtphub_tunnel_del_cb;
- return tun;
-}
-
-static const char *gtphub_peer_strb(struct gtphub_peer *peer, char *buf,
- int buflen)
-{
- if (llist_empty(&peer->addresses))
- return "(addressless)";
-
- struct gtphub_peer_addr *a = llist_first(&peer->addresses,
- struct gtphub_peer_addr,
- entry);
- return gsn_addr_to_strb(&a->addr, buf, buflen);
-}
-
-static const char *gtphub_port_strb(struct gtphub_peer_port *port, char *buf,
- int buflen)
-{
- if (!port)
- return "(null port)";
-
- snprintf(buf, buflen, "%s port %d",
- gsn_addr_to_str(&port->peer_addr->addr),
- (int)port->port);
- return buf;
-}
-
-const char *gtphub_peer_str(struct gtphub_peer *peer)
-{
- static char buf[256];
- return gtphub_peer_strb(peer, buf, sizeof(buf));
-}
-
-const char *gtphub_port_str(struct gtphub_peer_port *port)
-{
- static char buf[256];
- return gtphub_port_strb(port, buf, sizeof(buf));
-}
-
-static const char *gtphub_port_str2(struct gtphub_peer_port *port)
-{
- static char buf[256];
- return gtphub_port_strb(port, buf, sizeof(buf));
-}
-
-static void gtphub_mapping_del_cb(struct expiring_item *expi)
-{
- expi->del_cb = 0; /* avoid recursion loops */
- expiring_item_del(expi); /* usually already done, but make sure. */
-
- struct nr_mapping *nrm = container_of(expi,
- struct nr_mapping,
- expiry_entry);
- llist_del(&nrm->entry);
- INIT_LLIST_HEAD(&nrm->entry); /* mark unused */
-
- /* Just for log */
- struct gtphub_peer_port *from = nrm->origin;
- OSMO_ASSERT(from);
- LOG(LOGL_DEBUG, "expired: %d: nr mapping from %s: %u->%u\n",
- (int)nrm->expiry_entry.expiry,
- gtphub_port_str(from),
- (unsigned int)nrm->orig, (unsigned int)nrm->repl);
-
- gtphub_port_ref_count_dec(from);
-
- talloc_free(nrm);
-}
-
-static struct nr_mapping *gtphub_mapping_have(struct nr_map *map,
- struct gtphub_peer_port *from,
- nr_t orig_nr,
- time_t now)
-{
- struct nr_mapping *nrm;
-
- nrm = nr_map_get(map, from, orig_nr);
-
- if (!nrm) {
- nrm = gtphub_mapping_new();
- nrm->orig = orig_nr;
- nrm->origin = from;
- nr_map_add(map, nrm, now);
- gtphub_port_ref_count_inc(from);
- LOG(LOGL_DEBUG, "peer %s: sequence map %d --> %d\n",
- gtphub_port_str(from),
- (int)(nrm->orig), (int)(nrm->repl));
- } else {
- nr_map_refresh(map, nrm, now);
- }
-
- OSMO_ASSERT(nrm);
- return nrm;
-}
-
-static void gtphub_map_seq(struct gtp_packet_desc *p,
- struct gtphub_peer_port *from_port,
- struct gtphub_peer_port *to_port)
-{
- /* Store a mapping in to_peer's map, so when we later receive a GTP
- * packet back from to_peer, the seq nr can be unmapped back to its
- * origin (from_peer here). */
- struct nr_mapping *nrm;
- nrm = gtphub_mapping_have(&to_port->peer_addr->peer->seq_map,
- from_port, p->seq, p->timestamp);
-
- /* Change the GTP packet to yield the new, mapped seq nr */
- set_seq(p, nrm->repl);
-}
-
-static struct gtphub_peer_port *gtphub_unmap_seq(struct gtp_packet_desc *p,
- struct gtphub_peer_port *responding_port)
-{
- OSMO_ASSERT(p->version == 1);
- struct nr_mapping *nrm =
- nr_map_get_inv(&responding_port->peer_addr->peer->seq_map,
- p->seq);
- if (!nrm)
- return NULL;
- LOG(LOGL_DEBUG, "peer %p: sequence unmap %d <-- %d\n",
- nrm->origin, (int)(nrm->orig), (int)(nrm->repl));
- set_seq(p, nrm->orig);
- return nrm->origin;
-}
-
-static int gtphub_check_mapped_tei(struct gtphub_tunnel *new_tun,
- struct gtphub_tunnel *iterated_tun,
- uint32_t *tei_min,
- uint32_t *tei_max)
-{
- if (!new_tun->tei_repl || !iterated_tun->tei_repl)
- return 1;
-
- *tei_min = (*tei_min < iterated_tun->tei_repl)? *tei_min : iterated_tun->tei_repl;
- *tei_max = (*tei_max > iterated_tun->tei_repl)? *tei_max : iterated_tun->tei_repl;
-
- if (new_tun->tei_repl != iterated_tun->tei_repl)
- return 1;
-
- /* new_tun->tei_repl is already taken. Try to find one out of the known
- * range. */
- LOG(LOGL_DEBUG, "TEI replacement %d already taken.\n", new_tun->tei_repl);
-
- if ((*tei_max) < 0xffffffff) {
- (*tei_max)++;
- new_tun->tei_repl = *tei_max;
- LOG(LOGL_DEBUG, "Using TEI %d instead.\n", new_tun->tei_repl);
- return 1;
- } else if ((*tei_min) > 1) {
- (*tei_min)--;
- new_tun->tei_repl = *tei_min;
- LOG(LOGL_DEBUG, "Using TEI %d instead.\n", new_tun->tei_repl);
- return 1;
- }
-
- /* None seems to be available. */
- return 0;
-}
-
-static int gtphub_check_reused_teis(struct gtphub *hub,
- struct gtphub_tunnel *new_tun)
-{
- uint32_t tei_min = 0xffffffff;
- uint32_t tei_max = 0;
- int side_idx;
- int plane_idx;
- struct gtphub_tunnel_endpoint *te;
- struct gtphub_tunnel_endpoint *te2;
-
- struct gtphub_tunnel *tun, *ntun;
-
- llist_for_each_entry_safe(tun, ntun, &hub->tunnels, entry) {
- if (tun == new_tun)
- continue;
-
- /* Check whether the GSN sent a TEI that it is reusing from a
- * previous tunnel. */
- int tun_continue = 0;
- for_each_side(side_idx) {
- for_each_plane(plane_idx) {
- te = &tun->endpoint[side_idx][plane_idx];
- te2 = &new_tun->endpoint[side_idx][plane_idx];
- if ((te->tei_orig == 0)
- || (te->tei_orig != te2->tei_orig)
- || (!te->peer)
- || (!te2->peer)
- || !gsn_addr_same(&te->peer->peer_addr->addr,
- &te2->peer->peer_addr->addr))
- continue;
-
- /* The peer is reusing a TEI that I believe to
- * be part of another tunnel. The other tunnel
- * must be stale, then. */
- LOG(LOGL_NOTICE,
- "Expiring tunnel due to reused TEI:"
- " %s peer %s sent %s TEI %x,"
- " previously used by tunnel %s...\n",
- gtphub_side_idx_names[side_idx],
- gtphub_port_str(te->peer),
- gtphub_plane_idx_names[plane_idx],
- te->tei_orig,
- gtphub_tunnel_str(tun));
- LOG(LOGL_NOTICE, "...while establishing tunnel %s\n",
- gtphub_tunnel_str(new_tun));
-
- expiring_item_del(&tun->expiry_entry);
- /* continue to find more matches. There shouldn't be
- * any, but let's make sure. However, tun is deleted,
- * so we need to skip to the next tunnel. */
- tun_continue = 1;
- break;
- }
- if (tun_continue)
- break;
- }
- if (tun_continue)
- continue;
-
- /* Check whether the mapped TEI is already used by another
- * tunnel. */
- if (!gtphub_check_mapped_tei(new_tun, tun, &tei_min, &tei_max)) {
- LOG(LOGL_ERROR,
- "No mapped TEI is readily available."
- " Searching for holes between occupied"
- " TEIs not implemented.");
- return 0;
- }
-
- }
-
- return 1;
-}
-
-static void gtphub_tunnel_refresh(struct gtphub *hub,
- struct gtphub_tunnel *tun,
- time_t now)
-{
- expiry_add(&hub->expire_slowly,
- &tun->expiry_entry,
- now);
-}
-
-static struct gtphub_tunnel_endpoint *gtphub_unmap_tei(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from,
- struct gtphub_tunnel **unmapped_from_tun)
-{
- OSMO_ASSERT(from);
- int other_side = other_side_idx(p->side_idx);
-
- struct gtphub_tunnel *tun;
- llist_for_each_entry(tun, &hub->tunnels, entry) {
- struct gtphub_tunnel_endpoint *te_from =
- &tun->endpoint[p->side_idx][p->plane_idx];
- struct gtphub_tunnel_endpoint *te_to =
- &tun->endpoint[other_side][p->plane_idx];
- if ((tun->tei_repl == p->header_tei_rx)
- && te_from->peer
- && gsn_addr_same(&te_from->peer->peer_addr->addr,
- &from->peer_addr->addr)) {
- gtphub_tunnel_refresh(hub, tun, p->timestamp);
- if (unmapped_from_tun)
- *unmapped_from_tun = tun;
- return te_to;
- }
- }
-
- if (unmapped_from_tun)
- *unmapped_from_tun = NULL;
- return NULL;
-}
-
-static void gtphub_map_restart_counter(struct gtphub *hub,
- struct gtp_packet_desc *p)
-{
- if (p->rc != GTP_RC_PDU_C)
- return;
-
- int ie_idx;
- ie_idx = gtpie_getie(p->ie, GTPIE_RECOVERY, 0);
- if (ie_idx < 0)
- return;
-
- /* Always send gtphub's own restart counter */
- p->ie[ie_idx]->tv1.v = hton8(hub->restart_counter);
-}
-
-static int gtphub_unmap_header_tei(struct gtphub_peer_port **to_port_p,
- struct gtphub_tunnel **unmapped_from_tun,
- struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from_port)
-{
- OSMO_ASSERT(p->version == 1);
- *to_port_p = NULL;
- if (unmapped_from_tun)
- *unmapped_from_tun = NULL;
-
- /* If the header's TEI is zero, no PDP context has been established
- * yet. If nonzero, a mapping should actually already exist for this
- * TEI, since it must have been announced in a PDP context creation. */
- if (!p->header_tei_rx)
- return 0;
-
- /* to_peer has previously announced a TEI, which was stored and
- * mapped in a tunnel struct. */
- struct gtphub_tunnel_endpoint *to;
- to = gtphub_unmap_tei(hub, p, from_port, unmapped_from_tun);
- if (!to) {
- LOG(LOGL_ERROR, "Received unknown TEI %" PRIx32 " from %s\n",
- p->header_tei_rx, gtphub_port_str(from_port));
- return -1;
- }
-
- if (unmapped_from_tun) {
- OSMO_ASSERT(*unmapped_from_tun);
- LOG(LOGL_DEBUG, "Unmapped TEI coming from: %s\n",
- gtphub_tunnel_str(*unmapped_from_tun));
- }
-
- uint32_t unmapped_tei = to->tei_orig;
- set_tei(p, unmapped_tei);
-
- /* May be NULL for an invalidated tunnel. */
- *to_port_p = to->peer;
-
- return 0;
-}
-
-static int gtphub_handle_create_pdp_ctx(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from_ctrl,
- struct gtphub_peer_port *to_ctrl)
-{
- int plane_idx;
-
- osmo_static_assert((GTPH_PLANE_CTRL == 0) && (GTPH_PLANE_USER == 1),
- plane_nrs_match_GSN_addr_IE_indices);
-
- struct gtphub_tunnel *tun = p->tun;
-
- if (p->type == GTP_CREATE_PDP_REQ) {
- if (p->side_idx != GTPH_SIDE_SGSN) {
- LOG(LOGL_ERROR, "Wrong side: Create PDP Context"
- " Request from the GGSN side: %s",
- gtphub_port_str(from_ctrl));
- return -1;
- }
-
- if (tun) {
- LOG(LOGL_ERROR, "Not implemented: Received"
- " Create PDP Context Request for an already"
- " established tunnel:"
- " from %s, tunnel %s\n",
- gtphub_port_str(from_ctrl),
- gtphub_tunnel_str(p->tun));
- return -1;
- }
-
- /* A new tunnel. */
- tun = gtphub_tunnel_new();
- if (!tun) {
- LOG(LOGL_ERROR, "Failed to allocate new tunnel %s <-> %s\n",
- gtphub_port_str(from_ctrl), gtphub_port_str(to_ctrl));
- return -1;
- }
- p->tun = tun;
-
- /* Create TEI mapping */
- tun->tei_repl = nr_pool_next(&hub->tei_pool);
-
- llist_add(&tun->entry, &hub->tunnels);
- gtphub_tunnel_refresh(hub, tun, p->timestamp);
- /* The endpoint peers on this side (SGSN) will be set from IEs
- * below. Also set the GGSN Ctrl endpoint, for logging. */
- gtphub_tunnel_endpoint_set_peer(&tun->endpoint[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL],
- to_ctrl);
- } else if (p->type == GTP_CREATE_PDP_RSP) {
- if (p->side_idx != GTPH_SIDE_GGSN) {
- LOG(LOGL_ERROR, "Wrong side: Create PDP Context"
- " Response from the SGSN side: %s",
- gtphub_port_str(from_ctrl));
- return -1;
- }
-
- /* The tunnel should already have been resolved from the header
- * TEI and be available in tun (== p->tun). Just fill in the
- * GSN Addresses below.*/
- OSMO_ASSERT(tun);
- OSMO_ASSERT(tun->tei_repl == p->header_tei_rx);
- OSMO_ASSERT(to_ctrl);
- }
-
- uint8_t ie_type[] = { GTPIE_TEI_C, GTPIE_TEI_DI };
- int ie_mandatory = (p->type == GTP_CREATE_PDP_REQ);
- unsigned int side_idx = p->side_idx;
-
- for_each_plane(plane_idx) {
- int rc;
- struct gsn_addr use_addr;
- uint16_t use_port;
- uint32_t tei_from_ie;
- int ie_idx;
-
- /* Fetch GSN Address and TEI from IEs. As ensured by above
- * static asserts, plane_idx corresponds to the GSN Address IE
- * index (the first one = 0 = ctrl, second one = 1 = user). */
- rc = gsn_addr_get(&use_addr, p, plane_idx);
- if (rc) {
- LOG(LOGL_ERROR, "Cannot read %s GSN Address IE\n",
- gtphub_plane_idx_names[plane_idx]);
- return -1;
- }
- LOG(LOGL_DEBUG, "Read %s GSN addr %s (%d)\n",
- gtphub_plane_idx_names[plane_idx],
- gsn_addr_to_str(&use_addr),
- use_addr.len);
-
- ie_idx = gtpie_getie(p->ie, ie_type[plane_idx], 0);
- if (ie_idx < 0) {
- if (ie_mandatory) {
- LOG(LOGL_ERROR,
- "Create PDP Context message invalid:"
- " missing IE %d\n",
- (int)ie_type[plane_idx]);
- return -1;
- }
- tei_from_ie = 0;
- }
- else
- tei_from_ie = ntoh32(p->ie[ie_idx]->tv4.v);
-
- /* Make sure an entry for this peer address with default port
- * exists.
- *
- * Exception: if sgsn_use_sender is set, instead use the
- * sender's address and port for Ctrl -- the User port is not
- * known until the first User packet arrives.
- *
- * Note: doing this here is just an optimization, because
- * gtphub_handle_buf() has code to replace the tunnel
- * endpoints' addresses with the sender (needed for User
- * plane). We could just ignore sgsn_use_sender here. But if we
- * set up a default port here and replace it in
- * gtphub_handle_buf(), we'd be creating a peer port just to
- * expire it right away. */
- if (hub->sgsn_use_sender && (side_idx == GTPH_SIDE_SGSN)) {
- int rc = gsn_addr_from_sockaddr(&use_addr, &use_port, &from_ctrl->sa);
- if (rc < 0)
- LOG(LOGL_ERROR, "%s(): failed to obtain GSN address\n", __func__);
- } else {
- use_port = gtphub_plane_idx_default_port[plane_idx];
-
- }
-
- struct gtphub_peer_port *peer_from_ie;
- peer_from_ie = gtphub_port_have(hub,
- &hub->to_gsns[side_idx][plane_idx],
- &use_addr, use_port);
-
- gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][plane_idx],
- peer_from_ie);
-
- if (!tei_from_ie &&
- !tun->endpoint[side_idx][plane_idx].tei_orig) {
- LOG(LOGL_ERROR,
- "Create PDP Context message omits %s TEI, but"
- " no TEI has been announced for this tunnel: %s\n",
- gtphub_plane_idx_names[plane_idx],
- gtphub_tunnel_str(tun));
- return -1;
- }
-
- if (tei_from_ie) {
- /* Replace TEI in GTP packet IE */
- tun->endpoint[side_idx][plane_idx].tei_orig = tei_from_ie;
- p->ie[ie_idx]->tv4.v = hton32(tun->tei_repl);
-
- if (!gtphub_check_reused_teis(hub, tun)) {
- /* It's highly unlikely that all TEIs are
- * taken. But the code looking for an unused
- * TEI is, at the time of writing this comment,
- * not able to find gaps in the TEI space. To
- * explicitly alert the user of this problem,
- * rather abort than carry on. */
- LOG(LOGL_FATAL, "TEI range exhausted. Cannot create TEI mapping, aborting.\n");
- abort();
- }
- }
-
- /* Replace the GSN address to reflect gtphub. */
- rc = gsn_addr_put(&hub->to_gsns[other_side_idx(side_idx)][plane_idx].local_addr,
- p, plane_idx);
- if (rc) {
- LOG(LOGL_ERROR, "Cannot write %s GSN Address IE\n",
- gtphub_plane_idx_names[plane_idx]);
- return -1;
- }
- }
-
- if (p->type == GTP_CREATE_PDP_REQ) {
- LOG(LOGL_DEBUG, "New tunnel, first half: %s\n",
- gtphub_tunnel_str(tun));
- } else if (p->type == GTP_CREATE_PDP_RSP) {
- LOG(LOGL_DEBUG, "New tunnel: %s\n",
- gtphub_tunnel_str(tun));
- }
-
- return 0;
-}
-
-static void pending_delete_del_cb(struct expiring_item *expi)
-{
- struct pending_delete *pd;
- pd = container_of(expi, struct pending_delete, expiry_entry);
-
- llist_del(&pd->entry);
- INIT_LLIST_HEAD(&pd->entry);
-
- pd->expiry_entry.del_cb = 0;
- expiring_item_del(&pd->expiry_entry);
-
- talloc_free(pd);
-}
-
-static struct pending_delete *pending_delete_new(void)
-{
- struct pending_delete *pd = talloc_zero(osmo_gtphub_ctx, struct pending_delete);
- INIT_LLIST_HEAD(&pd->entry);
- expiring_item_init(&pd->expiry_entry);
- pd->expiry_entry.del_cb = pending_delete_del_cb;
- return pd;
-}
-
-static int gtphub_handle_delete_pdp_ctx(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from_ctrl,
- struct gtphub_peer_port *to_ctrl)
-{
- struct gtphub_tunnel *known_tun = p->tun;
-
- if (p->type == GTP_DELETE_PDP_REQ) {
- if (!known_tun) {
- LOG(LOGL_ERROR, "Cannot find tunnel for Delete PDP Context Request.\n");
- return -1;
- }
-
- /* Store the Delete Request until a successful Response is seen. */
- uint8_t teardown_ind;
- uint8_t nsapi;
-
- if (gtpie_gettv1(p->ie, GTPIE_TEARDOWN, 0, &teardown_ind) != 0) {
- LOG(LOGL_ERROR, "Missing Teardown Ind IE in Delete PDP Context Request.\n");
- return -1;
- }
-
- if (gtpie_gettv1(p->ie, GTPIE_NSAPI, 0, &nsapi) != 0) {
- LOG(LOGL_ERROR, "Missing NSAPI IE in Delete PDP Context Request.\n");
- return -1;
- }
-
- struct pending_delete *pd = NULL;
-
- struct pending_delete *pdi = NULL;
- llist_for_each_entry(pdi, &hub->pending_deletes, entry) {
- if ((pdi->tun == known_tun)
- && (pdi->teardown_ind == teardown_ind)
- && (pdi->nsapi == nsapi)) {
- pd = pdi;
- break;
- }
- }
-
- if (!pd) {
- pd = pending_delete_new();
- pd->tun = known_tun;
- pd->teardown_ind = teardown_ind;
- pd->nsapi = nsapi;
-
- LOG(LOGL_DEBUG, "Tunnel delete pending: %s\n",
- gtphub_tunnel_str(known_tun));
- llist_add(&pd->entry, &hub->pending_deletes);
- }
-
- /* Add or refresh timeout. */
- expiry_add(&hub->expire_quickly, &pd->expiry_entry, p->timestamp);
-
- /* If a pending_delete should expire before the response to
- * indicate success comes in, the responding peer will have the
- * tunnel deactivated, while the requesting peer gets no reply
- * and keeps the tunnel. The hope is that the requesting peer
- * will try again and get a useful response. */
- } else if (p->type == GTP_DELETE_PDP_RSP) {
- /* Find the Delete Request for this Response. */
- struct pending_delete *pd = NULL;
-
- struct pending_delete *pdi;
- llist_for_each_entry(pdi, &hub->pending_deletes, entry) {
- if (known_tun == pdi->tun) {
- pd = pdi;
- break;
- }
- }
-
- if (!pd) {
- LOG(LOGL_ERROR, "Delete PDP Context Response:"
- " Cannot find matching request.");
- /* If we delete the tunnel now, anyone can send a
- * Delete response to kill tunnels at will. */
- return -1;
- }
-
- /* TODO handle teardown_ind and nsapi */
-
- expiring_item_del(&pd->expiry_entry);
-
- uint8_t cause;
- if (gtpie_gettv1(p->ie, GTPIE_CAUSE, 0, &cause) != 0) {
- LOG(LOGL_ERROR, "Delete PDP Context Response:"
- " Missing Cause IE.");
- /* If we delete the tunnel now, at least one of the
- * peers may still think it is active. */
- return -1;
- }
-
- if (cause != GTPCAUSE_ACC_REQ) {
- LOG(LOGL_NOTICE,
- "Delete PDP Context Response indicates failure;"
- "for %s\n",
- gtphub_tunnel_str(known_tun));
- return -1;
- }
-
- LOG(LOGL_DEBUG, "Delete PDP Context: removing tunnel %s\n",
- gtphub_tunnel_str(known_tun));
- p->tun = NULL;
- expiring_item_del(&known_tun->expiry_entry);
- }
-
- return 0;
-}
-
-static int gtphub_handle_update_pdp_ctx(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from_ctrl,
- struct gtphub_peer_port *to_ctrl)
-{
- /* TODO */
- return 0;
-}
-
-/* Read GSN address IEs from p, and make sure these peer addresses exist in
- * bind[plane_idx] with default ports, in their respective planes (both Ctrl
- * and User). Map TEIs announced in IEs, and write mapped TEIs in-place into
- * the packet p. */
-static int gtphub_handle_pdp_ctx(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from_ctrl,
- struct gtphub_peer_port *to_ctrl)
-{
- OSMO_ASSERT(p->plane_idx == GTPH_PLANE_CTRL);
-
- switch (p->type) {
- case GTP_CREATE_PDP_REQ:
- case GTP_CREATE_PDP_RSP:
- return gtphub_handle_create_pdp_ctx(hub, p,
- from_ctrl, to_ctrl);
-
- case GTP_DELETE_PDP_REQ:
- case GTP_DELETE_PDP_RSP:
- return gtphub_handle_delete_pdp_ctx(hub, p,
- from_ctrl, to_ctrl);
-
- case GTP_UPDATE_PDP_REQ:
- case GTP_UPDATE_PDP_RSP:
- return gtphub_handle_update_pdp_ctx(hub, p,
- from_ctrl, to_ctrl);
-
- default:
- /* Nothing to do for this message type. */
- return 0;
- }
-
-}
-
-static int gtphub_send_del_pdp_ctx(struct gtphub *hub,
- struct gtphub_tunnel *tun,
- int to_side)
-{
- static uint8_t del_ctx_msg[16] = {
- 0x32, /* GTP v1 flags */
- GTP_DELETE_PDP_REQ,
- 0x00, 0x08, /* Length in network byte order */
- 0x00, 0x00, 0x00, 0x00, /* TEI to be replaced */
- 0, 0, /* Seq, to be replaced */
- 0, 0, /* no extensions */
- 0x13, 0xff, /* 19: Teardown ind = 1 */
- 0x14, 0 /* 20: NSAPI = 0 */
- };
-
- uint32_t *tei = (uint32_t*)&del_ctx_msg[4];
- uint16_t *seq = (uint16_t*)&del_ctx_msg[8];
-
- struct gtphub_tunnel_endpoint *te =
- &tun->endpoint[to_side][GTPH_PLANE_CTRL];
-
- if (! te->peer)
- return 0;
-
- *tei = hton32(te->tei_orig);
- *seq = hton16(nr_pool_next(&te->peer->peer_addr->peer->seq_pool));
-
- struct gtphub_bind *to_bind = &hub->to_gsns[to_side][GTPH_PLANE_CTRL];
- int rc = gtphub_write(&to_bind->ofd, &te->peer->sa,
- del_ctx_msg, sizeof(del_ctx_msg));
- if (rc != 0) {
- LOG(LOGL_ERROR,
- "Failed to send out-of-band Delete PDP Context Request to %s\n",
- gtphub_port_str(te->peer));
- }
- return rc;
-}
-
-/* Tell all peers on the other end of tunnels that PDP contexts are void. */
-static void gtphub_restarted(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *pp)
-{
- LOG(LOGL_NOTICE, "Peer has restarted: %s\n",
- gtphub_port_str(pp));
-
- int deleted_count = 0;
- struct gtphub_tunnel *tun;
- llist_for_each_entry(tun, &hub->tunnels, entry) {
- int side_idx;
- for_each_side(side_idx) {
- struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][GTPH_PLANE_CTRL];
- struct gtphub_tunnel_endpoint *te2 = &tun->endpoint[other_side_idx(side_idx)][GTPH_PLANE_CTRL];
- if ((!te->peer)
- || (!te2->tei_orig)
- || (pp->peer_addr->peer != te->peer->peer_addr->peer))
- continue;
-
- LOG(LOGL_DEBUG, "Deleting tunnel due to peer restart: %s\n",
- gtphub_tunnel_str(tun));
- deleted_count ++;
-
- /* Send a Delete PDP Context Request to the
- * peer on the other side, remember the pending
- * delete and wait for the response to delete
- * the tunnel. Clear this side of the tunnel to
- * make sure it isn't used.
- *
- * Should the delete message send fail, or if no
- * response is received, this tunnel will expire. If
- * its TEIs come up in a new PDP Context Request, it
- * will be removed. If messages for this tunnel should
- * come in (from the not restarted side), they will be
- * dropped because the tunnel is rendered unusable. */
- gtphub_send_del_pdp_ctx(hub, tun, other_side_idx(side_idx));
-
- gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][GTPH_PLANE_CTRL],
- NULL);
- gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][GTPH_PLANE_USER],
- NULL);
- }
- }
-
- if (deleted_count)
- LOG(LOGL_NOTICE, "Deleting %d tunnels due to restart of: %s\n",
- deleted_count,
- gtphub_port_str(pp));
-}
-
-static int get_restart_count(struct gtp_packet_desc *p)
-{
- int ie_idx;
- ie_idx = gtpie_getie(p->ie, GTPIE_RECOVERY, 0);
- if (ie_idx < 0)
- return -1;
- return ntoh8(p->ie[ie_idx]->tv1.v);
-}
-
-static void gtphub_check_restart_counter(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from)
-{
- /* If the peer is sending a Recovery IE (7.7.11) with a restart counter
- * that doesn't match the peer's previously sent restart counter, clear
- * that peer and cancel PDP contexts. */
-
- int restart = get_restart_count(p);
-
- if ((restart < 0) || (restart > 255))
- return;
-
- if ((from->last_restart_count >= 0) && (from->last_restart_count <= 255)) {
- if (from->last_restart_count != restart) {
- gtphub_restarted(hub, p, from);
- }
- }
-
- from->last_restart_count = restart;
-}
-
-static int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what)
-{
- unsigned int plane_idx = from_sgsns_ofd->priv_nr;
- OSMO_ASSERT(plane_idx < GTPH_PLANE_N);
- LOG(LOGL_DEBUG, "=== reading from SGSN (%s)\n",
- gtphub_plane_idx_names[plane_idx]);
-
- if (!(what & BSC_FD_READ))
- return 0;
-
- struct gtphub *hub = from_sgsns_ofd->data;
-
- static uint8_t buf[4096];
- struct osmo_sockaddr from_addr;
- struct osmo_sockaddr to_addr;
- struct osmo_fd *to_ofd;
- int len;
- uint8_t *reply_buf;
-
- len = gtphub_read(from_sgsns_ofd, &from_addr, buf, sizeof(buf));
- if (len < 1)
- return 0;
-
- len = gtphub_handle_buf(hub, GTPH_SIDE_SGSN, plane_idx, &from_addr,
- buf, len, gtphub_now(),
- &reply_buf, &to_ofd, &to_addr);
- if (len < 1)
- return 0;
-
- return gtphub_write(to_ofd, &to_addr, reply_buf, len);
-}
-
-static int from_ggsns_read_cb(struct osmo_fd *from_ggsns_ofd, unsigned int what)
-{
- unsigned int plane_idx = from_ggsns_ofd->priv_nr;
- OSMO_ASSERT(plane_idx < GTPH_PLANE_N);
- LOG(LOGL_DEBUG, "=== reading from GGSN (%s)\n",
- gtphub_plane_idx_names[plane_idx]);
- if (!(what & BSC_FD_READ))
- return 0;
-
- struct gtphub *hub = from_ggsns_ofd->data;
-
- static uint8_t buf[4096];
- struct osmo_sockaddr from_addr;
- struct osmo_sockaddr to_addr;
- struct osmo_fd *to_ofd;
- int len;
- uint8_t *reply_buf;
-
- len = gtphub_read(from_ggsns_ofd, &from_addr, buf, sizeof(buf));
- if (len < 1)
- return 0;
-
- len = gtphub_handle_buf(hub, GTPH_SIDE_GGSN, plane_idx, &from_addr,
- buf, len, gtphub_now(),
- &reply_buf, &to_ofd, &to_addr);
- if (len < 1)
- return 0;
-
- return gtphub_write(to_ofd, &to_addr, reply_buf, len);
-}
-
-static int gtphub_unmap(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from,
- struct gtphub_peer_port *to_proxy,
- struct gtphub_peer_port **final_unmapped,
- struct gtphub_peer_port **unmapped_from_seq)
-{
- /* Always (try to) unmap sequence and TEI numbers, which need to be
- * replaced in the packet. Either way, give precedence to the proxy, if
- * configured. */
-
- if (unmapped_from_seq)
- *unmapped_from_seq = NULL;
- if (final_unmapped)
- *final_unmapped = NULL;
- p->tun = NULL;
-
- struct gtphub_peer_port *from_seq = NULL;
- struct gtphub_peer_port *from_tei = NULL;
- struct gtphub_peer_port *unmapped = NULL;
-
- from_seq = gtphub_unmap_seq(p, from);
-
- if (gtphub_unmap_header_tei(&from_tei, &p->tun, hub, p, from) < 0)
- return -1;
-
- struct gtphub_peer *from_peer = from->peer_addr->peer;
- if (from_seq && from_tei && (from_seq != from_tei)) {
- LOG(LOGL_DEBUG,
- "Seq unmap and TEI unmap yield two different peers."
- " Using seq unmap."
- " (from %s %s: seq %d yields %s, tei %u yields %s)\n",
- gtphub_plane_idx_names[p->plane_idx],
- gtphub_peer_str(from_peer),
- (int)p->seq,
- gtphub_port_str(from_seq),
- (unsigned int)p->header_tei_rx,
- gtphub_port_str2(from_tei)
- );
- }
- unmapped = (from_seq? from_seq : from_tei);
-
- if (unmapped && to_proxy && (unmapped != to_proxy)) {
- LOG(LOGL_NOTICE,
- "Unmap yields a different peer than the configured proxy."
- " Using proxy."
- " unmapped: %s proxy: %s\n",
- gtphub_port_str(unmapped),
- gtphub_port_str2(to_proxy)
- );
- }
- unmapped = (to_proxy? to_proxy : unmapped);
-
- if (!unmapped) {
- /* Return no error, but returned pointers are all NULL. */
- return 0;
- }
-
- if (unmapped_from_seq)
- *unmapped_from_seq = from_seq;
- if (final_unmapped)
- *final_unmapped = unmapped;
- return 0;
-}
-
-static int gsn_addr_to_sockaddr(struct gsn_addr *src,
- uint16_t port,
- struct osmo_sockaddr *dst)
-{
- return osmo_sockaddr_init_udp(dst, gsn_addr_to_str(src), port);
-}
-
-/* If p is an Echo request, replace p's data with the matching response and
- * return 1. If p is no Echo request, return 0, or -1 if an invalid packet is
- * detected. */
-static int gtphub_handle_echo_req(struct gtphub *hub, struct gtp_packet_desc *p,
- uint8_t **reply_buf)
-{
- if (p->type != GTP_ECHO_REQ)
- return 0;
-
- static uint8_t echo_response_data[14] = {
- 0x32, /* GTP v1 flags */
- GTP_ECHO_RSP,
- 0x00, 14 - 8, /* Length in network byte order */
- 0x00, 0x00, 0x00, 0x00, /* Zero TEI */
- 0, 0, /* Seq, to be replaced */
- 0, 0, /* no extensions */
- 0x0e, /* Recovery IE */
- 0 /* Restart counter, to be replaced */
- };
- uint16_t *seq = (uint16_t*)&echo_response_data[8];
- uint8_t *recovery = &echo_response_data[13];
-
- *seq = hton16(p->seq);
- *recovery = hub->restart_counter;
-
- *reply_buf = echo_response_data;
-
- return sizeof(echo_response_data);
-}
-
-struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind,
- const struct osmo_sockaddr *addr);
-
-/* Parse buffer as GTP packet, replace elements in-place and return the ofd and
- * address to forward to. Return a pointer to the osmo_fd, but copy the
- * sockaddr to *to_addr. The reason for this is that the sockaddr may expire at
- * any moment, while the osmo_fd is guaranteed to persist. Return the number of
- * bytes to forward, 0 or less on failure. */
-int gtphub_handle_buf(struct gtphub *hub,
- unsigned int side_idx,
- unsigned int plane_idx,
- const struct osmo_sockaddr *from_addr,
- uint8_t *buf,
- size_t received,
- time_t now,
- uint8_t **reply_buf,
- struct osmo_fd **to_ofd,
- struct osmo_sockaddr *to_addr)
-{
- struct gtphub_bind *from_bind = &hub->to_gsns[side_idx][plane_idx];
- struct gtphub_bind *to_bind = &hub->to_gsns[other_side_idx(side_idx)][plane_idx];
-
- rate_ctr_add(&from_bind->counters_io->ctr[GTPH_CTR_BYTES_IN],
- received);
-
- struct gtp_packet_desc p;
- gtp_decode(buf, received, side_idx, plane_idx, &p, now);
-
- LOG(LOGL_DEBUG, "%s rx %s from %s %s%s\n",
- (side_idx == GTPH_SIDE_GGSN)? "<-" : "->",
- gtphub_plane_idx_names[plane_idx],
- gtphub_side_idx_names[side_idx],
- osmo_sockaddr_to_str(from_addr),
- gtp_type_str(p.type));
-
- if (p.rc <= 0) {
- LOG(LOGL_ERROR, "INVALID: dropping GTP packet%s from %s %s %s\n",
- gtp_type_str(p.type),
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- osmo_sockaddr_to_str(from_addr));
- return -1;
- }
-
- rate_ctr_inc(&from_bind->counters_io->ctr[GTPH_CTR_PKTS_IN]);
-
- int reply_len;
- reply_len = gtphub_handle_echo_req(hub, &p, reply_buf);
- if (reply_len > 0) {
- /* It was an echo. Nothing left to do. */
- osmo_sockaddr_copy(to_addr, from_addr);
- *to_ofd = &from_bind->ofd;
-
- rate_ctr_inc(&from_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]);
- rate_ctr_add(&from_bind->counters_io->ctr[GTPH_CTR_BYTES_OUT],
- reply_len);
- LOG(LOGL_DEBUG, "%s Echo response to %s: %d bytes to %s\n",
- (side_idx == GTPH_SIDE_GGSN)? "-->" : "<--",
- gtphub_side_idx_names[side_idx],
- (int)reply_len, osmo_sockaddr_to_str(to_addr));
- return reply_len;
- }
- if (reply_len < 0)
- return -1;
-
- *to_ofd = &to_bind->ofd;
-
- /* If a proxy is configured, check that it's indeed that proxy talking
- * to us. A proxy is a forced 1:1 connection, e.g. to another gtphub,
- * so no-one else is allowed to talk to us from that side. */
- struct gtphub_peer_port *from_peer = hub->proxy[side_idx][plane_idx];
- if (from_peer) {
- if (osmo_sockaddr_cmp(&from_peer->sa, from_addr) != 0) {
- LOG(LOGL_ERROR,
- "Rejecting: %s proxy configured, but GTP packet"
- " received on %s bind is from another sender:"
- " proxy: %s sender: %s\n",
- gtphub_side_idx_names[side_idx],
- gtphub_side_idx_names[side_idx],
- gtphub_port_str(from_peer),
- osmo_sockaddr_to_str(from_addr));
- return -1;
- }
- }
-
- if (!from_peer) {
- /* Find or create a peer with a matching address. The sender's
- * port may in fact differ. */
- from_peer = gtphub_known_addr_have_port(from_bind, from_addr);
- }
-
- /* If any PDP context has been created, we already have an entry for
- * this GSN. If we don't have an entry, a GGSN has nothing to tell us
- * about, while an SGSN may initiate a PDP context. */
- if (!from_peer) {
- if (side_idx == GTPH_SIDE_GGSN) {
- LOG(LOGL_ERROR, "Dropping packet%s: unknown GGSN peer: %s\n",
- gtp_type_str(p.type),
- osmo_sockaddr_to_str(from_addr));
- return -1;
- } else {
- /* SGSN */
- /* A new peer. If this is on the Ctrl plane, an SGSN
- * may make first contact without being known yet, so
- * create the peer struct for the current sender. */
- if (plane_idx != GTPH_PLANE_CTRL) {
- LOG(LOGL_ERROR,
- "Dropping packet%s: User plane peer was not"
- "announced by PDP Context: %s\n",
- gtp_type_str(p.type),
- osmo_sockaddr_to_str(from_addr));
- return -1;
- }
-
- struct gsn_addr from_gsna;
- uint16_t from_port;
- if (gsn_addr_from_sockaddr(&from_gsna, &from_port, from_addr) != 0)
- return -1;
-
- from_peer = gtphub_port_have(hub, from_bind, &from_gsna, from_port);
- }
- }
-
- if (!from_peer) {
- /* This could theoretically happen for invalid address data or
- * somesuch. */
- LOG(LOGL_ERROR, "Dropping packet%s: invalid %s peer: %s\n",
- gtp_type_str(p.type),
- gtphub_side_idx_names[side_idx],
- osmo_sockaddr_to_str(from_addr));
- return -1;
- }
-
- rate_ctr_add(&from_peer->counters_io->ctr[GTPH_CTR_BYTES_IN],
- received);
- rate_ctr_inc(&from_peer->counters_io->ctr[GTPH_CTR_PKTS_IN]);
-
- LOG(LOGL_DEBUG, "from %s peer: %s\n", gtphub_side_idx_names[side_idx],
- gtphub_port_str(from_peer));
-
- gtphub_check_restart_counter(hub, &p, from_peer);
- gtphub_map_restart_counter(hub, &p);
-
- struct gtphub_peer_port *to_peer_from_seq;
- struct gtphub_peer_port *to_peer;
- if (gtphub_unmap(hub, &p, from_peer,
- hub->proxy[other_side_idx(side_idx)][plane_idx],
- &to_peer, &to_peer_from_seq)
- != 0) {
- return -1;
- }
-
- if (p.tun) {
- struct gtphub_tunnel_endpoint *te = &p.tun->endpoint[p.side_idx][p.plane_idx];
- rate_ctr_add(&te->counters_io->ctr[GTPH_CTR_BYTES_IN],
- received);
- rate_ctr_inc(&te->counters_io->ctr[GTPH_CTR_PKTS_IN]);
- }
-
- if ((!to_peer) && (side_idx == GTPH_SIDE_SGSN)) {
- if (gtphub_resolve_ggsn(hub, &p, &to_peer) < 0)
- return -1;
- }
-
- if (!to_peer && p.tun && p.type == GTP_DELETE_PDP_RSP) {
- /* It's a delete confirmation for a tunnel that is partly
- * invalid, probably marked unsuable due to a restarted peer.
- * Remove the tunnel and be happy without forwarding. */
- expiring_item_del(&p.tun->expiry_entry);
- p.tun = NULL;
- return 0;
- }
-
- if (!to_peer) {
- LOG(LOGL_ERROR, "No %s to send to. Dropping packet%s"
- " (type=%" PRIu8 ", header-TEI=%" PRIx32 ", seq=%" PRIx16 ").\n",
- gtphub_side_idx_names[other_side_idx(side_idx)],
- gtp_type_str(p.type),
- p.type, p.header_tei_rx, p.seq
- );
- return -1;
- }
-
- if (plane_idx == GTPH_PLANE_CTRL) {
- /* This may be a Create PDP Context response. If it is, there
- * are other addresses in the GTP message to set up apart from
- * the sender. */
- if (gtphub_handle_pdp_ctx(hub, &p, from_peer, to_peer)
- != 0)
- return -1;
- }
-
- /* Either to_peer was resolved from an existing tunnel,
- * or a PDP Ctx and thus a tunnel has just been created,
- * or the tunnel has been deleted due to this message. */
- OSMO_ASSERT(p.tun || (p.type == GTP_DELETE_PDP_RSP));
-
- /* If the GGSN is replying to an SGSN request, the sequence nr has
- * already been unmapped above (to_peer_from_seq != NULL), and we need not
- * create a new mapping. */
- if (!to_peer_from_seq)
- gtphub_map_seq(&p, from_peer, to_peer);
-
- osmo_sockaddr_copy(to_addr, &to_peer->sa);
-
- *reply_buf = (uint8_t*)p.data;
-
- if (received) {
- rate_ctr_inc(&to_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]);
- rate_ctr_add(&to_bind->counters_io->ctr[GTPH_CTR_BYTES_OUT],
- received);
-
- rate_ctr_inc(&to_peer->counters_io->ctr[GTPH_CTR_PKTS_OUT]);
- rate_ctr_add(&to_peer->counters_io->ctr[GTPH_CTR_BYTES_OUT],
- received);
- }
-
- if (p.tun) {
- struct gtphub_tunnel_endpoint *te = &p.tun->endpoint[other_side_idx(p.side_idx)][p.plane_idx];
- rate_ctr_inc(&te->counters_io->ctr[GTPH_CTR_PKTS_OUT]);
- rate_ctr_add(&te->counters_io->ctr[GTPH_CTR_BYTES_OUT],
- received);
- }
-
- LOG(LOGL_DEBUG, "%s Forward to %s:"
- " header-TEI %" PRIx32", seq %" PRIx16", %d bytes to %s\n",
- (side_idx == GTPH_SIDE_SGSN)? "-->" : "<--",
- gtphub_side_idx_names[other_side_idx(side_idx)],
- p.header_tei, p.seq,
- (int)received, osmo_sockaddr_to_str(to_addr));
- return received;
-}
-
-static void resolved_gssn_del_cb(struct expiring_item *expi)
-{
- struct gtphub_resolved_ggsn *ggsn;
- ggsn = container_of(expi, struct gtphub_resolved_ggsn, expiry_entry);
-
- gtphub_port_ref_count_dec(ggsn->peer);
- llist_del(&ggsn->entry);
-
- ggsn->expiry_entry.del_cb = 0;
- expiring_item_del(&ggsn->expiry_entry);
-
- talloc_free(ggsn);
-}
-
-void gtphub_resolved_ggsn(struct gtphub *hub, const char *apn_oi_str,
- struct gsn_addr *resolved_addr,
- time_t now)
-{
- struct gtphub_peer_port *pp;
- struct gtphub_resolved_ggsn *ggsn;
-
- LOG(LOGL_DEBUG, "Resolved GGSN callback: %s %s\n",
- apn_oi_str, osmo_hexdump((unsigned char*)resolved_addr,
- sizeof(*resolved_addr)));
-
- pp = gtphub_port_have(hub, &hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL],
- resolved_addr, 2123);
- if (!pp) {
- LOG(LOGL_ERROR, "Internal: Cannot create/find peer '%s'\n",
- gsn_addr_to_str(resolved_addr));
- return;
- }
-
- ggsn = talloc_zero(osmo_gtphub_ctx, struct gtphub_resolved_ggsn);
- OSMO_ASSERT(ggsn);
- INIT_LLIST_HEAD(&ggsn->entry);
- expiring_item_init(&ggsn->expiry_entry);
-
- ggsn->peer = pp;
- gtphub_port_ref_count_inc(pp);
-
- osmo_strlcpy(ggsn->apn_oi_str, apn_oi_str, sizeof(ggsn->apn_oi_str));
-
- ggsn->expiry_entry.del_cb = resolved_gssn_del_cb;
- expiry_add(&hub->expire_slowly, &ggsn->expiry_entry, now);
-
- llist_add(&ggsn->entry, &hub->resolved_ggsns);
-}
-
-static int gtphub_gc_peer_port(struct gtphub_peer_port *pp)
-{
- return pp->ref_count == 0;
-}
-
-static int gtphub_gc_peer_addr(struct gtphub_peer_addr *pa)
-{
- struct gtphub_peer_port *pp, *npp;
- llist_for_each_entry_safe(pp, npp, &pa->ports, entry) {
- if (gtphub_gc_peer_port(pp)) {
- LOG(LOGL_DEBUG, "expired: peer %s\n",
- gtphub_port_str(pp));
- gtphub_peer_port_del(pp);
- }
- }
- return llist_empty(&pa->ports);
-}
-
-static int gtphub_gc_peer(struct gtphub_peer *p)
-{
- struct gtphub_peer_addr *pa, *npa;
- llist_for_each_entry_safe(pa, npa, &p->addresses, entry) {
- if (gtphub_gc_peer_addr(pa)) {
- gtphub_peer_addr_del(pa);
- }
- }
-
- /* Note that there's a ref_count in each gtphub_peer_port instance
- * listed within p->addresses, referenced by TEI mappings from
- * hub->tei_map. As long as those don't expire, this peer will stay. */
-
- return llist_empty(&p->addresses)
- && nr_map_empty(&p->seq_map);
-}
-
-static void gtphub_gc_bind(struct gtphub_bind *b)
-{
- struct gtphub_peer *p, *n;
- llist_for_each_entry_safe(p, n, &b->peers, entry) {
- if (gtphub_gc_peer(p)) {
- gtphub_peer_del(p);
- }
- }
-}
-
-void gtphub_gc(struct gtphub *hub, time_t now)
-{
- int expired;
- expired = expiry_tick(&hub->expire_quickly, now);
- expired += expiry_tick(&hub->expire_slowly, now);
-
- /* ... */
-
- if (expired) {
- int s, p;
- for_each_side_and_plane(s, p) {
- gtphub_gc_bind(&hub->to_gsns[s][p]);
- }
- }
-}
-
-static void gtphub_gc_cb(void *data)
-{
- struct gtphub *hub = data;
- gtphub_gc(hub, gtphub_now());
- osmo_timer_schedule(&hub->gc_timer, GTPH_GC_TICK_SECONDS, 0);
-}
-
-static void gtphub_gc_start(struct gtphub *hub)
-{
- osmo_timer_setup(&hub->gc_timer, gtphub_gc_cb, hub);
- osmo_timer_schedule(&hub->gc_timer, GTPH_GC_TICK_SECONDS, 0);
-}
-
-/* called by unit tests */
-void gtphub_init(struct gtphub *hub)
-{
- gtphub_zero(hub);
-
- INIT_LLIST_HEAD(&hub->tunnels);
- INIT_LLIST_HEAD(&hub->pending_deletes);
-
- expiry_init(&hub->expire_quickly, GTPH_EXPIRE_QUICKLY_SECS);
- expiry_init(&hub->expire_slowly, GTPH_EXPIRE_SLOWLY_MINUTES * 60);
-
- nr_pool_init(&hub->tei_pool, 1, 0xffffffff);
-
- int side_idx;
- int plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- gtphub_bind_init(&hub->to_gsns[side_idx][plane_idx], CTR_IDX_HUB(side_idx, plane_idx));
- }
-
- hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].label = "SGSN Ctrl";
- hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].label = "GGSN Ctrl";
- hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].label = "SGSN User";
- hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].label = "GGSN User";
-}
-
-/* For the test suite, this is kept separate from gtphub_stop(), which also
- * closes sockets. The test suite avoids using sockets and would cause
- * segfaults when trying to close uninitialized ofds. */
-void gtphub_free(struct gtphub *hub)
-{
- /* By expiring all mappings, a garbage collection should free
- * everything else. A gtphub_bind_free() will assert that everything is
- * indeed empty. */
- expiry_clear(&hub->expire_quickly);
- expiry_clear(&hub->expire_slowly);
-
- int side_idx;
- int plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- gtphub_gc_bind(&hub->to_gsns[side_idx][plane_idx]);
- gtphub_bind_free(&hub->to_gsns[side_idx][plane_idx]);
- }
-}
-
-void gtphub_stop(struct gtphub *hub)
-{
- int side_idx;
- int plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- gtphub_bind_stop(&hub->to_gsns[side_idx][plane_idx]);
- }
- gtphub_free(hub);
-}
-
-static int gtphub_make_proxy(struct gtphub *hub,
- struct gtphub_peer_port **pp,
- struct gtphub_bind *bind,
- const struct gtphub_cfg_addr *addr)
-{
- if (!addr->addr_str)
- return 0;
-
- struct gsn_addr gsna;
- if (gsn_addr_from_str(&gsna, addr->addr_str) != 0)
- return -1;
-
- *pp = gtphub_port_have(hub, bind, &gsna, addr->port);
-
- /* This is *the* proxy. Make sure it is never expired. */
- gtphub_port_ref_count_inc(*pp);
- return 0;
-}
-
-int gtphub_start(struct gtphub *hub, struct gtphub_cfg *cfg,
- uint8_t restart_counter)
-{
- gtphub_init(hub);
-
- hub->restart_counter = restart_counter;
- hub->sgsn_use_sender = cfg->sgsn_use_sender? 1 : 0;
-
- /* If a Ctrl plane proxy is configured, ares will never be used. */
- if (!cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str) {
- if (gtphub_ares_init(hub) != 0) {
- LOG(LOGL_FATAL, "Failed to initialize ares\n");
- return -1;
- }
- }
-
- int side_idx;
- int plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- int rc;
- rc = gtphub_bind_start(&hub->to_gsns[side_idx][plane_idx],
- &cfg->to_gsns[side_idx][plane_idx],
- (side_idx == GTPH_SIDE_SGSN)
- ? from_sgsns_read_cb
- : from_ggsns_read_cb,
- hub, plane_idx);
- if (rc) {
- LOG(LOGL_FATAL, "Failed to bind for %ss (%s)\n",
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx]);
- return rc;
- }
- }
-
- for_each_side_and_plane(side_idx, plane_idx) {
- if (gtphub_make_proxy(hub,
- &hub->proxy[side_idx][plane_idx],
- &hub->to_gsns[side_idx][plane_idx],
- &cfg->proxy[side_idx][plane_idx])
- != 0) {
- LOG(LOGL_FATAL, "Cannot configure %s proxy"
- " %s port %d.\n",
- gtphub_side_idx_names[side_idx],
- cfg->proxy[side_idx][plane_idx].addr_str,
- (int)cfg->proxy[side_idx][plane_idx].port);
- return -1;
- }
- }
-
- for_each_side_and_plane(side_idx, plane_idx) {
- if (hub->proxy[side_idx][plane_idx])
- LOG(LOGL_NOTICE, "Using %s %s proxy %s\n",
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- gtphub_port_str(hub->proxy[side_idx][plane_idx]));
- }
-
- if (hub->sgsn_use_sender)
- LOG(LOGL_NOTICE, "Using sender address and port for SGSN instead of GSN Addr IE and default ports.\n");
-
- gtphub_gc_start(hub);
- return 0;
-}
-
-static struct gtphub_peer_addr *gtphub_peer_find_addr(const struct gtphub_peer *peer,
- const struct gsn_addr *addr)
-{
- struct gtphub_peer_addr *a;
- llist_for_each_entry(a, &peer->addresses, entry) {
- if (gsn_addr_same(&a->addr, addr))
- return a;
- }
- return NULL;
-}
-
-static struct gtphub_peer_port *gtphub_addr_find_port(const struct gtphub_peer_addr *a,
- uint16_t port)
-{
- OSMO_ASSERT(port);
- struct gtphub_peer_port *pp;
- llist_for_each_entry(pp, &a->ports, entry) {
- if (pp->port == port)
- return pp;
- }
- return NULL;
-}
-
-static struct gtphub_peer_addr *gtphub_addr_find(const struct gtphub_bind *bind,
- const struct gsn_addr *addr)
-{
- struct gtphub_peer *peer;
- llist_for_each_entry(peer, &bind->peers, entry) {
- struct gtphub_peer_addr *a = gtphub_peer_find_addr(peer, addr);
- if (a)
- return a;
- }
- return NULL;
-}
-
-static struct gtphub_peer_port *gtphub_port_find(const struct gtphub_bind *bind,
- const struct gsn_addr *addr,
- uint16_t port)
-{
- struct gtphub_peer_addr *a = gtphub_addr_find(bind, addr);
- if (!a)
- return NULL;
- return gtphub_addr_find_port(a, port);
-}
-
-struct gtphub_peer_port *gtphub_port_find_sa(const struct gtphub_bind *bind,
- const struct osmo_sockaddr *addr)
-{
- struct gsn_addr gsna;
- uint16_t port;
- if (gsn_addr_from_sockaddr(&gsna, &port, addr) != 0)
- return NULL;
-
- return gtphub_port_find(bind, &gsna, port);
-}
-
-static struct gtphub_peer *gtphub_peer_new(struct gtphub *hub,
- struct gtphub_bind *bind)
-{
- struct gtphub_peer *peer = talloc_zero(osmo_gtphub_ctx,
- struct gtphub_peer);
- OSMO_ASSERT(peer);
-
- INIT_LLIST_HEAD(&peer->addresses);
-
- nr_pool_init(&peer->seq_pool, 0, 0xffff);
- nr_map_init(&peer->seq_map, &peer->seq_pool, &hub->expire_quickly);
-
- /* TODO use something random to pick the initial sequence nr.
- 0x6d31 produces the ASCII character sequence 'm1', currently used in
- gtphub_nc_test.sh. */
- peer->seq_pool.last_nr = 0x6d31 - 1;
-
- llist_add(&peer->entry, &bind->peers);
- return peer;
-}
-
-static struct gtphub_peer_addr *gtphub_peer_add_addr(struct gtphub_peer *peer,
- const struct gsn_addr *addr)
-{
- struct gtphub_peer_addr *a;
- a = talloc_zero(osmo_gtphub_ctx, struct gtphub_peer_addr);
- OSMO_ASSERT(a);
- a->peer = peer;
- gsn_addr_copy(&a->addr, addr);
- INIT_LLIST_HEAD(&a->ports);
- llist_add(&a->entry, &peer->addresses);
-
- return a;
-}
-
-static struct gtphub_peer_addr *gtphub_addr_have(struct gtphub *hub,
- struct gtphub_bind *bind,
- const struct gsn_addr *addr)
-{
- struct gtphub_peer_addr *a = gtphub_addr_find(bind, addr);
- if (a)
- return a;
-
- /* If we haven't found an address, that means we need to create an
- * entirely new peer for the new address. More addresses may be added
- * to this peer later, but not via this function. */
- struct gtphub_peer *peer = gtphub_peer_new(hub, bind);
-
- a = gtphub_peer_add_addr(peer, addr);
-
- LOG(LOGL_DEBUG, "New peer address: %s %s\n",
- bind->label,
- gsn_addr_to_str(&a->addr));
-
- return a;
-}
-
-static struct gtphub_peer_port *gtphub_addr_add_port(struct gtphub_peer_addr *a,
- uint16_t port)
-{
- struct gtphub_peer_port *pp;
-
- pp = talloc_zero(osmo_gtphub_ctx, struct gtphub_peer_port);
- OSMO_ASSERT(pp);
- pp->peer_addr = a;
- pp->port = port;
- pp->last_restart_count = -1;
-
- if (gsn_addr_to_sockaddr(&a->addr, port, &pp->sa) != 0) {
- talloc_free(pp);
- return NULL;
- }
-
- pp->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx,
- &gtphub_ctrg_io_desc, port);
- if (!pp->counters_io) {
- LOG(LOGL_ERROR, "Failed to allocate rate counters for %s:%u\n", gsn_addr_to_str(&a->addr), port);
- talloc_free(pp);
- return NULL;
- }
-
- llist_add(&pp->entry, &a->ports);
-
- LOG(LOGL_DEBUG, "New peer port: %s port %d\n",
- gsn_addr_to_str(&a->addr),
- (int)port);
-
- return pp;
-}
-
-struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub,
- struct gtphub_bind *bind,
- const struct gsn_addr *addr,
- uint16_t port)
-{
- struct gtphub_peer_addr *a = gtphub_addr_have(hub, bind, addr);
-
- struct gtphub_peer_port *pp = gtphub_addr_find_port(a, port);
- if (pp)
- return pp;
-
- return gtphub_addr_add_port(a, port);
-}
-
-/* Find a GGSN peer with a matching address. If the address is known but the
- * port not, create a new port for that peer address. */
-struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind,
- const struct osmo_sockaddr *addr)
-{
- struct gtphub_peer_addr *pa;
- struct gtphub_peer_port *pp;
-
- struct gsn_addr gsna;
- uint16_t port;
- int rc = gsn_addr_from_sockaddr(&gsna, &port, addr);
- if (rc < 0)
- LOG(LOGL_ERROR, "%s(): failed to obtain GSN address\n", __func__);
-
- pa = gtphub_addr_find(bind, &gsna);
- if (!pa)
- return NULL;
-
- pp = gtphub_addr_find_port(pa, port);
-
- if (!pp)
- pp = gtphub_addr_add_port(pa, port);
-
- return pp;
-}
-
-
-/* Return 0 if the message in p is not applicable for GGSN resolution, -1 if
- * resolution should be possible but failed, and 1 if resolution was
- * successful. *pp will be set to NULL if <1 is returned. */
-static int gtphub_resolve_ggsn(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port **pp)
-{
- *pp = NULL;
-
- /* TODO determine from message type whether IEs should be present? */
-
- int rc;
- const char *imsi_str;
- rc = get_ie_imsi_str(p->ie, 0, &imsi_str);
- if (rc < 1)
- return rc;
- OSMO_ASSERT(imsi_str);
-
- const char *apn_str;
- rc = get_ie_apn_str(p->ie, &apn_str);
- if (rc < 1)
- return rc;
- OSMO_ASSERT(apn_str);
-
- *pp = gtphub_resolve_ggsn_addr(hub, imsi_str, apn_str);
- return (*pp)? 1 : -1;
-}
-
-
-/* TODO move to osmocom/core/socket.c ? */
-/* use this in osmo_sock_init() to remove dup. */
-/* Internal: call getaddrinfo for osmo_sockaddr_init(). The caller is required
- to call freeaddrinfo(*result), iff zero is returned. */
-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 ? */
-int osmo_sockaddr_init(struct osmo_sockaddr *addr,
- 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) {
- LOG(LOGL_ERROR, "getaddrinfo returned error %d\n", (int)rc);
- return -EINVAL;
- }
-
- OSMO_ASSERT(res->ai_addrlen <= sizeof(addr->a));
- memcpy(&addr->a, res->ai_addr, res->ai_addrlen);
- addr->l = res->ai_addrlen;
- freeaddrinfo(res);
-
- return 0;
-}
-
-int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len,
- char *port_str, size_t port_str_len,
- const struct osmo_sockaddr *addr,
- int flags)
-{
- int rc;
-
- if ((addr->l < 1) || (addr->l > sizeof(addr->a))) {
- LOGP(DGTPHUB, LOGL_ERROR, "Invalid address size: %d\n", addr->l);
- return -1;
- }
-
- if (addr->l > sizeof(addr->a)) {
- LOGP(DGTPHUB, LOGL_ERROR, "Invalid address: too long: %d\n",
- addr->l);
- return -1;
- }
-
- rc = getnameinfo((struct sockaddr*)&addr->a, addr->l,
- addr_str, addr_str_len,
- port_str, port_str_len,
- flags);
-
- if (rc)
- LOGP(DGTPHUB, LOGL_ERROR, "Invalid address: %s: %s\n",
- gai_strerror(rc), osmo_hexdump((uint8_t*)&addr->a,
- addr->l));
-
- return rc;
-}
-
-const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr,
- char *buf, size_t buf_len)
-{
- const int portbuf_len = 6;
- OSMO_ASSERT(buf_len > portbuf_len);
- char *portbuf = buf + buf_len - portbuf_len;
- buf_len -= portbuf_len;
- if (osmo_sockaddr_to_strs(buf, buf_len,
- portbuf, portbuf_len,
- addr,
- NI_NUMERICHOST | NI_NUMERICSERV))
- return NULL;
-
- char *pos = buf + strnlen(buf, buf_len-1);
- size_t len = buf_len - (pos - buf);
-
- snprintf(pos, len, " port %s", portbuf);
- buf[buf_len-1] = '\0';
-
- return buf;
-}
-
-const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *addr)
-{
- static char buf[256];
- const char *result = osmo_sockaddr_to_strb(addr, buf, sizeof(buf));
- if (! result)
- return "(invalid)";
- return result;
-}
-
-int osmo_sockaddr_cmp(const struct osmo_sockaddr *a,
- const struct osmo_sockaddr *b)
-{
- if (a == b)
- return 0;
- if (!a)
- return -1;
- if (!b)
- return 1;
- if (a->l != b->l) {
- /* Lengths are not the same, but determine the order. Will
- * anyone ever sort a list by osmo_sockaddr though...? */
- int cmp = memcmp(&a->a, &b->a, (a->l < b->l)? a->l : b->l);
- if (cmp == 0) {
- if (a->l < b->l)
- return -1;
- else
- return 1;
- }
- return cmp;
- }
- return memcmp(&a->a, &b->a, a->l);
-}
-
-void osmo_sockaddr_copy(struct osmo_sockaddr *dst,
- const struct osmo_sockaddr *src)
-{
- OSMO_ASSERT(src->l <= sizeof(dst->a));
- memcpy(&dst->a, &src->a, src->l);
- dst->l = src->l;
-}
diff --git a/src/gprs/gtphub_ares.c b/src/gprs/gtphub_ares.c
deleted file mode 100644
index 87dc860c4..000000000
--- a/src/gprs/gtphub_ares.c
+++ /dev/null
@@ -1,220 +0,0 @@
-/* GTP Hub Implementation */
-
-/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * gtphub_ares.c.
- *
- * This file is kept separate so that these functions can be wrapped for
- * gtphub_test.c. When a function and its callers are in the same compilational
- * unit, the wrappability may be optimized away.
- *
- * 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 <unistd.h>
-
-#include <osmocom/sgsn/gtphub.h>
-#include <osmocom/sgsn/debug.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/apn.h>
-
-/* TODO split GRX ares from sgsn into a separate struct and allow use without
- * globals. */
-#include <osmocom/sgsn/sgsn.h>
-extern struct sgsn_instance *sgsn;
-
-struct sgsn_instance sgsn_inst = { 0 };
-struct sgsn_instance *sgsn = &sgsn_inst;
-
-extern void *osmo_gtphub_ctx;
-
-int gtphub_ares_init(struct gtphub *hub)
-{
- return sgsn_ares_init(sgsn);
-}
-
-struct ggsn_lookup {
- struct llist_head entry;
- struct expiring_item expiry_entry;
-
- struct gtphub *hub;
-
- char imsi_str[GSM23003_IMSI_MAX_DIGITS+1];
- char apn_ni_str[GSM_APN_LENGTH];
- char apn_oi_str[GSM_APN_LENGTH];
- int have_3dig_mnc;
-};
-
-static int start_ares_query(struct ggsn_lookup *lookup);
-
-static void ggsn_lookup_cb(void *arg, int status, int timeouts,
- struct hostent *hostent)
-{
- struct ggsn_lookup *lookup = arg;
- LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_cb(%p / %p)", lookup,
- &lookup->expiry_entry);
-
- if (status != ARES_SUCCESS) {
- LOGP(DGTPHUB, LOGL_ERROR, "DNS query failed.\n");
-
- /* Need to try with three digits now */
- if (!lookup->have_3dig_mnc) {
- lookup->have_3dig_mnc = 1;
- if (start_ares_query(lookup) == 0)
- return;
- }
-
- LOGP(DGTPHUB, LOGL_ERROR, "Failed to resolve GGSN. (%p)\n",
- lookup);
- goto remove_from_queue;
- }
-
- struct gsn_addr resolved_addr;
- if (hostent->h_length > sizeof(resolved_addr.buf)) {
- LOGP(DGTPHUB, LOGL_ERROR, "Addr size too large: %d > %d\n",
- (int)hostent->h_length, (int)sizeof(resolved_addr.buf));
- goto remove_from_queue;
- }
-
- /* Get the first addr from the list */
- char *addr0 = hostent->h_addr_list[0];
- if (!addr0) {
- LOGP(DGTPHUB, LOGL_ERROR, "No host address.\n");
- goto remove_from_queue;
- }
-
- memcpy(resolved_addr.buf, addr0, hostent->h_length);
- resolved_addr.len = hostent->h_length;
-
- LOGP(DGTPHUB, LOGL_NOTICE, "resolved addr %s\n",
- osmo_hexdump((unsigned char*)&resolved_addr,
- sizeof(resolved_addr)));
-
- gtphub_resolved_ggsn(lookup->hub, lookup->apn_oi_str, &resolved_addr,
- gtphub_now());
-
-remove_from_queue:
- LOGP(DGTPHUB, LOGL_ERROR, "Removing GGSN lookup. (%p / %p)\n", lookup,
- &lookup->expiry_entry);
- expiring_item_del(&lookup->expiry_entry);
-}
-
-static void make_addr_str(struct ggsn_lookup *lookup)
-{
- char *apn_oi_str;
- apn_oi_str = osmo_apn_qualify_from_imsi(lookup->imsi_str,
- lookup->apn_ni_str,
- lookup->have_3dig_mnc);
- osmo_strlcpy(lookup->apn_oi_str, apn_oi_str,
- sizeof(lookup->apn_oi_str));
-}
-
-static int start_ares_query(struct ggsn_lookup *lookup)
-{
- LOGP(DGTPHUB, LOGL_DEBUG, "Going to query %s (%p / %p)\n",
- lookup->apn_oi_str, lookup, &lookup->expiry_entry);
-
- int rc = sgsn_ares_query(sgsn, lookup->apn_oi_str, ggsn_lookup_cb,
- lookup);
- if (rc != 0)
- LOGP(DGTPHUB, LOGL_ERROR, "Failed to start ares query.\n");
- return rc;
-}
-
-static void ggsn_lookup_del_cb(struct expiring_item *expi)
-{
- struct ggsn_lookup *lookup;
- lookup = container_of(expi, struct ggsn_lookup, expiry_entry);
-
- LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_del_cb(%p / %p)\n", lookup,
- expi);
-
- lookup->expiry_entry.del_cb = 0;
- expiring_item_del(expi);
-
- llist_del(&lookup->entry);
- talloc_free(lookup);
-}
-
-struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub,
- const char *imsi_str,
- const char *apn_ni_str)
-{
- OSMO_ASSERT(imsi_str);
- OSMO_ASSERT(apn_ni_str);
-
- struct ggsn_lookup *lookup = talloc_zero(osmo_gtphub_ctx,
- struct ggsn_lookup);
- OSMO_ASSERT(lookup);
-
- LOGP(DGTPHUB, LOGL_DEBUG, "Request to resolve IMSI"
- " '%s' with APN-NI '%s' (%p / %p)\n",
- imsi_str, apn_ni_str, lookup, &lookup->expiry_entry);
-
- expiring_item_init(&lookup->expiry_entry);
- lookup->hub = hub;
-
- osmo_strlcpy(lookup->imsi_str, imsi_str, sizeof(lookup->imsi_str));
- osmo_strlcpy(lookup->apn_ni_str, apn_ni_str,
- sizeof(lookup->apn_ni_str));
-
- make_addr_str(lookup);
-
- struct ggsn_lookup *active;
- llist_for_each_entry(active, &hub->ggsn_lookups, entry) {
- if (strncmp(active->apn_oi_str, lookup->apn_oi_str,
- sizeof(lookup->apn_oi_str)) == 0) {
- LOGP(DGTPHUB, LOGL_DEBUG,
- "Query already pending for %s\n",
- lookup->apn_oi_str);
- /* A query already pending. Just tip our hat. */
- return NULL;
- }
- }
-
- struct gtphub_resolved_ggsn *resolved;
- llist_for_each_entry(resolved, &hub->resolved_ggsns, entry) {
- if (strncmp(resolved->apn_oi_str, lookup->apn_oi_str,
- sizeof(lookup->apn_oi_str)) == 0) {
- LOGP(DGTPHUB, LOGL_DEBUG,
- "GGSN resolved from cache: %s -> %s\n",
- lookup->apn_oi_str,
- gtphub_port_str(resolved->peer));
- return resolved->peer;
- }
- }
-
- /* Kick off a resolution, but so far return nothing. The hope is that
- * the peer will resend the request (a couple of times), and by then
- * the GGSN will be resolved. */
- LOGP(DGTPHUB, LOGL_DEBUG,
- "Sending out DNS query for %s..."
- " (Returning failure, hoping for a retry once resolution"
- " has concluded)\n",
- lookup->apn_oi_str);
-
- llist_add(&lookup->entry, &hub->ggsn_lookups);
-
- lookup->expiry_entry.del_cb = ggsn_lookup_del_cb;
- expiry_add(&hub->expire_quickly, &lookup->expiry_entry, gtphub_now());
-
- start_ares_query(lookup);
-
- return NULL;
-}
diff --git a/src/gprs/gtphub_main.c b/src/gprs/gtphub_main.c
deleted file mode 100644
index f693f0993..000000000
--- a/src/gprs/gtphub_main.c
+++ /dev/null
@@ -1,403 +0,0 @@
-/* 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 <unistd.h>
-#include <signal.h>
-#include <string.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <sys/stat.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>
-#include <osmocom/core/rate_ctr.h>
-
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/telnet_interface.h>
-#include <osmocom/vty/ports.h>
-#include <osmocom/vty/misc.h>
-
-#include <osmocom/sgsn/debug.h>
-#include <osmocom/sgsn/gtphub.h>
-#include <osmocom/sgsn/vty.h>
-
-#include "../../bscconfig.h"
-
-#if BUILD_IU
-#include <osmocom/sigtran/osmo_ss7.h>
-#endif
-
-extern void *osmo_gtphub_ctx;
-void *tall_sgsn_ctx;
-
-const char *gtphub_copyright =
- "Copyright (C) 2015 sysmocom s.f.m.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_INFO,
- },
-};
-
-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),
-};
-
-void log_cfg(struct gtphub_cfg *cfg)
-{
- int side_idx, plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- struct gtphub_cfg_addr *a;
- a = &cfg->to_gsns[side_idx][plane_idx].bind;
- LOGP(DGTPHUB, LOGL_NOTICE,
- "to-%ss bind, %s: %s port %d\n",
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- a->addr_str, a->port);
- }
-}
-
-static void signal_handler(int signal)
-{
- fprintf(stdout, "signal %d received\n", signal);
-
- switch (signal) {
- case SIGINT:
- case SIGTERM:
- 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;
- }
-}
-
-#if BUILD_IU
-int gtphub_vty_go_parent(struct vty *vty)
-{
- switch (vty->node) {
- default:
- osmo_ss7_vty_go_parent(vty);
- }
-
- return vty->node;
-}
-#endif
-
-int gtphub_vty_is_config_node(struct vty *vty, int node)
-{
- /* Check if libosmo-sccp declares the node in
- * question as config node */
-#if BUILD_IU
- if (osmo_ss7_is_config_node(vty, node))
- return 1;
-#endif
-
- switch (node) {
- /* add items that are not config */
- case CONFIG_NODE:
- return 0;
-
- default:
- return 1;
- }
-}
-
-static struct vty_app_info vty_info = {
- .name = "OsmoGTPhub",
- .version = PACKAGE_VERSION,
-#if BUILD_IU
- .go_parent_cb = gtphub_vty_go_parent,
-#endif
- .is_config_node = gtphub_vty_is_config_node,
-};
-
-struct cmdline_cfg {
- const char *config_file;
- const char *restart_counter_file;
- int daemonize;
-};
-
-static uint8_t next_restart_count(const char *path)
-{
- int umask_was = umask(022);
-
- uint8_t counter = 0;
-
- FILE *f = fopen(path, "r");
- if (f) {
- int rc = fscanf(f, "%hhu", &counter);
-
- if (rc != 1)
- goto failed_to_read;
-
- char c;
- while (fread(&c, 1, 1, f) > 0) {
- switch (c) {
- case ' ':
- case '\t':
- case '\n':
- case '\r':
- break;
- default:
- goto failed_to_read;
- }
- }
- fclose(f);
- }
-
- counter ++;
-
- f = fopen(path, "w");
- if (!f)
- goto failed_to_write;
- if (fprintf(f, "%" PRIu8 "\n", counter) < 2)
- goto failed_to_write;
- if (fclose(f)) {
- f = NULL;
- goto failed_to_write;
- }
-
- umask(umask_was);
-
- LOGP(DGTPHUB, LOGL_NOTICE, "Restarted with counter %hhu\n", counter);
- return counter;
-
-failed_to_read:
- fclose(f);
- umask(umask_was);
- LOGP(DGTPHUB, LOGL_FATAL, "Restart counter file cannot be parsed:"
- " %s\n", path);
- exit(1);
-
-failed_to_write:
- if (f)
- fclose(f);
- umask(umask_was);
- LOGP(DGTPHUB, LOGL_FATAL, "Restart counter file cannot be written:"
- " %s\n", path);
- exit(1);
-}
-
-static void print_help(struct cmdline_cfg *ccfg)
-{
- printf("gtphub commandline options\n");
- printf(" -h,--help This text.\n");
- printf(" -D,--daemonize Fork the process into a background daemon.\n");
- printf(" -d,--debug <cat> Enable Debugging for this category.\n");
- printf(" Pass '-d list' to get a category listing.\n");
- printf(" -s,--disable-color\n");
- printf(" -c,--config-file <path> The config file to use [%s].\n",
- ccfg->config_file);
- printf(" -e,--log-level <nr> Set a global log level.\n");
- printf(" -r,--restart-file <path> File for counting restarts [%s].\n",
- ccfg->restart_counter_file);
- printf(" -V,--version Print the version.\n");
-}
-
-static void list_categories(void)
-{
- printf("Available debug categories:\n");
- int i;
- for (i = 0; i < gtphub_log_info.num_cat; ++i) {
- if (!gtphub_log_info.cat[i].name)
- continue;
-
- printf("%s\n", gtphub_log_info.cat[i].name);
- }
-}
-
-static void handle_options(struct cmdline_cfg *ccfg, int argc, char **argv)
-{
- while (1) {
- int option_index = 0, c;
- static struct option long_options[] = {
- {"help", 0, 0, 'h'},
- {"debug", 1, 0, 'd'},
- {"daemonize", 0, 0, 'D'},
- {"config-file", 1, 0, 'c'},
- {"disable-color", 0, 0, 's'},
- {"timestamp", 0, 0, 'T'},
- {"log-level", 1, 0, 'e'},
- {"restart-file", 1, 0, 'r'},
- { "version", 0, 0, 'V' },
- {NULL, 0, 0, 0}
- };
-
- c = getopt_long(argc, argv, "hd:Dc:sTe:r:V",
- long_options, &option_index);
- if (c == -1) {
- if (optind < argc) {
- LOGP(DGTPHUB, LOGL_FATAL,
- "Excess commandline arguments ('%s').\n",
- argv[optind]);
- exit(2);
- }
- break;
- }
-
- switch (c) {
- case 'h':
- //print_usage();
- print_help(ccfg);
- exit(0);
- case 's':
- log_set_use_color(osmo_stderr_target, 0);
- break;
- case 'd':
- if (strcmp("list", optarg) == 0) {
- list_categories();
- exit(0);
- } else
- log_parse_category_mask(osmo_stderr_target, optarg);
- break;
- case 'D':
- ccfg->daemonize = 1;
- break;
- case 'c':
- ccfg->config_file = optarg;
- break;
- case 'T':
- log_set_print_timestamp(osmo_stderr_target, 1);
- break;
- case 'e':
- log_set_log_level(osmo_stderr_target, atoi(optarg));
- break;
- case 'r':
- ccfg->restart_counter_file = optarg;
- break;
- case 'V':
- print_version(1);
- exit(EXIT_SUCCESS);
- break;
- default:
- LOGP(DGTPHUB, LOGL_FATAL, "Invalid command line argument, abort.\n");
- exit(1);
- break;
- }
- }
-}
-
-int main(int argc, char **argv)
-{
- int rc;
-
- struct cmdline_cfg _ccfg;
- struct cmdline_cfg *ccfg = &_ccfg;
- memset(ccfg, '\0', sizeof(*ccfg));
- ccfg->config_file = "./gtphub.conf";
- ccfg->restart_counter_file = "./gtphub_restart_count";
-
- struct gtphub_cfg _cfg;
- struct gtphub_cfg *cfg = &_cfg;
- memset(cfg, '\0', sizeof(*cfg));
-
- struct gtphub _hub;
- struct gtphub *hub = &_hub;
-
- osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub");
- msgb_talloc_ctx_init(osmo_gtphub_ctx, 0);
- vty_info.tall_ctx = osmo_gtphub_ctx;
-
- signal(SIGINT, &signal_handler);
- signal(SIGTERM, &signal_handler);
- signal(SIGABRT, &signal_handler);
- signal(SIGUSR1, &signal_handler);
- signal(SIGUSR2, &signal_handler);
- osmo_init_ignore_signals();
-
- osmo_init_logging2(osmo_gtphub_ctx, &gtphub_log_info);
-
- vty_info.copyright = gtphub_copyright;
- vty_init(&vty_info);
- logging_vty_add_cmds();
- osmo_talloc_vty_add_cmds();
- gtphub_vty_init(hub, cfg);
-
- rate_ctr_init(osmo_gtphub_ctx);
-
- handle_options(ccfg, argc, argv);
-
- rc = gtphub_cfg_read(cfg, ccfg->config_file);
- if (rc < 0) {
- LOGP(DGTPHUB, LOGL_FATAL, "Cannot parse config file '%s'\n",
- ccfg->config_file);
- exit(2);
- }
-
- /* start telnet after reading config for vty_get_bind_addr() */
- rc = telnet_init_dynif(osmo_gtphub_ctx, 0, vty_get_bind_addr(),
- OSMO_VTY_PORT_GTPHUB);
- if (rc < 0)
- exit(1);
-
- if (gtphub_start(hub, cfg,
- next_restart_count(ccfg->restart_counter_file))
- != 0)
- return -1;
-
- log_cfg(cfg);
-
- if (ccfg->daemonize) {
- rc = osmo_daemonize();
- if (rc < 0) {
- LOGP(DGTPHUB, LOGL_FATAL, "Error during daemonize");
- exit(1);
- }
- }
-
- while (1) {
- rc = osmo_select_main(0);
- if (rc < 0)
- exit(3);
- }
-
- /* not reached */
- exit(0);
-}
diff --git a/src/gprs/gtphub_sock.c b/src/gprs/gtphub_sock.c
deleted file mode 100644
index 1acd5a624..000000000
--- a/src/gprs/gtphub_sock.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/* GTP Hub Implementation */
-
-/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * gtphub_sock.c.
- *
- * This file is kept separate so that these functions can be wrapped for
- * gtphub_test.c. When a function and its callers are in the same compilational
- * unit, the wrappability may be optimized away.
- *
- * 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 <osmocom/sgsn/gtphub.h>
-#include <osmocom/sgsn/debug.h>
-
-/* Convenience makro, note: only within this C file. */
-#define LOG(level, fmt, args...) \
- LOGP(DGTPHUB, level, fmt, ##args)
-
-int gtphub_write(const struct osmo_fd *to,
- const struct osmo_sockaddr *to_addr,
- const uint8_t *buf, size_t buf_len)
-{
- errno = 0;
- ssize_t sent = sendto(to->fd, buf, buf_len, 0,
- (struct sockaddr*)&to_addr->a, to_addr->l);
- LOG(LOGL_DEBUG, "to %s\n", osmo_sockaddr_to_str(to_addr));
-
- if (sent == -1) {
- LOG(LOGL_ERROR, "error: %s\n", strerror(errno));
- return -EINVAL;
- }
-
- if (sent != buf_len)
- LOG(LOGL_ERROR, "sent(%d) != data_len(%d)\n",
- (int)sent, (int)buf_len);
- else
- LOG(LOGL_DEBUG, "Sent %d: %s%s\n",
- (int)sent,
- osmo_hexdump(buf, sent > 1000? 1000 : sent),
- sent > 1000 ? "..." : "");
-
- return 0;
-}
-
diff --git a/src/gprs/gtphub_vty.c b/src/gprs/gtphub_vty.c
deleted file mode 100644
index abc08fd69..000000000
--- a/src/gprs/gtphub_vty.c
+++ /dev/null
@@ -1,613 +0,0 @@
-/* (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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <string.h>
-#include <inttypes.h>
-
-#include <ares.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/misc.h>
-
-#include <osmocom/sgsn/vty.h>
-#include <osmocom/sgsn/gtphub.h>
-
-/* TODO split GRX ares from sgsn into a separate struct and allow use without
- * globals. */
-#include <osmocom/sgsn/sgsn.h>
-extern struct sgsn_instance *sgsn;
-extern void *tall_sgsn_ctx;
-
-static struct gtphub *g_hub = 0;
-static struct gtphub_cfg *g_cfg = 0;
-
-static struct cmd_node gtphub_node = {
- GTPHUB_NODE,
- "%s(config-gtphub)# ",
- 1,
-};
-
-#define GTPH_DEFAULT_CONTROL_PORT 2123
-#define GTPH_DEFAULT_USER_PORT 2152
-
-static void write_addrs(struct vty *vty, const char *name,
- struct gtphub_cfg_addr *c, struct gtphub_cfg_addr *u)
-{
- if ((c->port == GTPH_DEFAULT_CONTROL_PORT)
- && (u->port == GTPH_DEFAULT_USER_PORT)
- && (strcmp(c->addr_str, u->addr_str) == 0)) {
- /* Default port numbers and same IP address: write "short"
- * variant. */
- vty_out(vty, " %s %s%s",
- name,
- c->addr_str,
- VTY_NEWLINE);
- return;
- }
-
- vty_out(vty, " %s ctrl %s %d user %s %d%s",
- name,
- c->addr_str, (int)c->port,
- u->addr_str, (int)u->port,
- VTY_NEWLINE);
-
- struct ares_addr_node *server;
- for (server = sgsn->ares_servers; server; server = server->next)
- vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE);
-}
-
-static int config_write_gtphub(struct vty *vty)
-{
- vty_out(vty, "gtphub%s", VTY_NEWLINE);
-
- write_addrs(vty, "bind-to-sgsns",
- &g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind,
- &g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind);
-
- write_addrs(vty, "bind-to-ggsns",
- &g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind,
- &g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind);
-
- if (g_cfg->sgsn_use_sender) {
- vty_out(vty, "sgsn-use-sender%s", VTY_NEWLINE);
- }
-
- if (g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str) {
- write_addrs(vty, "sgsn-proxy",
- &g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL],
- &g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER]);
- }
-
- if (g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str) {
- write_addrs(vty, "ggsn-proxy",
- &g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL],
- &g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER]);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub, cfg_gtphub_cmd,
- "gtphub",
- "Configure the GTP hub\n")
-{
- vty->node = GTPHUB_NODE;
- return CMD_SUCCESS;
-}
-
-#define BIND_ARGS "ctrl ADDR <0-65535> user ADDR <0-65535>"
-#define BIND_DOCS \
- "Set GTP-C bind\n" \
- "GTP-C local IP address (v4 or v6)\n" \
- "GTP-C local port\n" \
- "Set GTP-U bind\n" \
- "GTP-U local IP address (v4 or v6)\n" \
- "GTP-U local port\n"
-
-
-DEFUN(cfg_gtphub_bind_to_sgsns_short, cfg_gtphub_bind_to_sgsns_short_cmd,
- "bind-to-sgsns ADDR",
- "GTP Hub Parameters\n"
- "Set the local bind address to listen for SGSNs, for both GTP-C and GTP-U\n"
- "Local IP address (v4 or v6)\n"
- )
-{
- int i;
- for_each_plane(i)
- g_cfg->to_gsns[GTPH_SIDE_SGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT;
- g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub_bind_to_ggsns_short, cfg_gtphub_bind_to_ggsns_short_cmd,
- "bind-to-ggsns ADDR",
- "GTP Hub Parameters\n"
- "Set the local bind address to listen for GGSNs, for both GTP-C and GTP-U\n"
- "Local IP address (v4 or v6)\n"
- )
-{
- int i;
- for_each_plane(i)
- g_cfg->to_gsns[GTPH_SIDE_GGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT;
- g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT;
- return CMD_SUCCESS;
-}
-
-
-static int handle_binds(struct gtphub_cfg_bind *b, const char **argv)
-{
- b[GTPH_PLANE_CTRL].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- b[GTPH_PLANE_CTRL].bind.port = atoi(argv[1]);
- b[GTPH_PLANE_USER].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
- b[GTPH_PLANE_USER].bind.port = atoi(argv[3]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub_bind_to_sgsns, cfg_gtphub_bind_to_sgsns_cmd,
- "bind-to-sgsns " BIND_ARGS,
- "GTP Hub Parameters\n"
- "Set the local bind addresses and ports to listen for SGSNs\n"
- BIND_DOCS
- )
-{
- return handle_binds(g_cfg->to_gsns[GTPH_SIDE_SGSN], argv);
-}
-
-DEFUN(cfg_gtphub_bind_to_ggsns, cfg_gtphub_bind_to_ggsns_cmd,
- "bind-to-ggsns " BIND_ARGS,
- "GTP Hub Parameters\n"
- "Set the local bind addresses and ports to listen for GGSNs\n"
- BIND_DOCS
- )
-{
- return handle_binds(g_cfg->to_gsns[GTPH_SIDE_GGSN], argv);
-}
-
-DEFUN(cfg_gtphub_ggsn_proxy_short, cfg_gtphub_ggsn_proxy_short_cmd,
- "ggsn-proxy ADDR",
- "GTP Hub Parameters\n"
- "Redirect all GGSN bound traffic to default ports on this address (another gtphub)\n"
- "Remote IP address (v4 or v6)\n"
- )
-{
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT;
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub_ggsn_proxy, cfg_gtphub_ggsn_proxy_cmd,
- "ggsn-proxy " BIND_ARGS,
- "GTP Hub Parameters\n"
- "Redirect all GGSN bound traffic to these addresses and ports (another gtphub)\n"
- BIND_DOCS
- )
-{
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]);
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = atoi(argv[3]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub_sgsn_proxy_short, cfg_gtphub_sgsn_proxy_short_cmd,
- "sgsn-proxy ADDR",
- "GTP Hub Parameters\n"
- "Redirect all SGSN bound traffic to default ports on this address (another gtphub)\n"
- "Remote IP address (v4 or v6)\n"
- )
-{
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT;
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub_sgsn_proxy, cfg_gtphub_sgsn_proxy_cmd,
- "sgsn-proxy " BIND_ARGS,
- "GTP Hub Parameters\n"
- "Redirect all SGSN bound traffic to these addresses and ports (another gtphub)\n"
- BIND_DOCS
- )
-{
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]);
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = atoi(argv[3]);
- return CMD_SUCCESS;
-}
-
-
-#define SGSN_USE_SENDER_STR \
- "Ignore SGSN's Address IEs, use sender address and port (useful over NAT)\n"
-
-DEFUN(cfg_gtphub_sgsn_use_sender,
- cfg_gtphub_sgsn_use_sender_cmd,
- "sgsn-use-sender",
- SGSN_USE_SENDER_STR)
-{
- g_cfg->sgsn_use_sender = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub_no_sgsn_use_sender,
- cfg_gtphub_no_sgsn_use_sender_cmd,
- "no sgsn-use-sender",
- NO_STR SGSN_USE_SENDER_STR)
-{
- g_cfg->sgsn_use_sender = 0;
- return CMD_SUCCESS;
-}
-
-
-/* Copied from sgsn_vty.h */
-DEFUN(cfg_grx_ggsn, cfg_grx_ggsn_cmd,
- "grx-dns-add A.B.C.D",
- "Add DNS server\nIPv4 address\n")
-{
- struct ares_addr_node *node = talloc_zero(tall_sgsn_ctx, struct ares_addr_node);
- node->family = AF_INET;
- inet_aton(argv[0], &node->addr.addr4);
-
- node->next = sgsn->ares_servers;
- sgsn->ares_servers = node;
- return CMD_SUCCESS;
-}
-
-
-static void show_bind_stats_all(struct vty *vty)
-{
- int plane_idx;
- for_each_plane(plane_idx) {
- vty_out(vty, "- %s Plane:%s",
- gtphub_plane_idx_names[plane_idx], VTY_NEWLINE);
-
- int side_idx;
- for_each_side(side_idx) {
- struct gtphub_bind *b = &g_hub->to_gsns[side_idx][plane_idx];
- vty_out(vty, " - local addr to/from %ss: %s port %d%s",
- gtphub_side_idx_names[side_idx],
- gsn_addr_to_str(&b->local_addr), (int)b->local_port,
- VTY_NEWLINE);
- vty_out_rate_ctr_group(vty, " ", b->counters_io);
- }
- }
-}
-
-static void show_tunnel_stats(struct vty *vty, struct gtphub_tunnel *tun)
-{
- int plane_idx;
- for_each_plane(plane_idx) {
- vty_out(vty, "- %s Plane:%s",
- gtphub_plane_idx_names[plane_idx], VTY_NEWLINE);
-
- int side_idx;
- for_each_side(side_idx) {
- struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx];
- vty_out(vty, " - to/from %s:%s",
- gtphub_side_idx_names[side_idx],
- VTY_NEWLINE);
- vty_out_rate_ctr_group(vty, " ", te->counters_io);
- }
- }
-}
-
-static void show_peer_summary(struct vty *vty, const char *prefix,
- int side_idx, int plane_idx,
- struct gtphub_peer *p, int with_io_stats)
-{
- struct gtphub_peer_addr *pa;
- int p2l = strlen(prefix) + 4 + 1;
- char prefix2[p2l];
- memset(prefix2, ' ', p2l - 1);
- prefix2[p2l - 1] = '\0';
-
- if (with_io_stats) {
- llist_for_each_entry(pa, &p->addresses, entry) {
- vty_out(vty, "%s- %s %s %s%s", prefix,
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- gsn_addr_to_str(&pa->addr),
- VTY_NEWLINE);
-
-
- struct gtphub_peer_port *pp;
- llist_for_each_entry(pp, &pa->ports, entry) {
- vty_out(vty, "%s Port %" PRIu16 "%s", prefix, pp->port, VTY_NEWLINE);
- vty_out_rate_ctr_group(vty, prefix2, pp->counters_io);
- }
- }
- } else {
- llist_for_each_entry(pa, &p->addresses, entry) {
- vty_out(vty, "%s- %s %s %s", prefix,
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- gsn_addr_to_str(&pa->addr));
- struct gtphub_peer_port *pp;
- llist_for_each_entry(pp, &pa->ports, entry) {
- vty_out(vty, ":%" PRIu16, pp->port);
- }
- vty_out(vty, VTY_NEWLINE);
- }
- }
-}
-
-static void show_peers_summary(struct vty *vty)
-{
- int side_idx;
- int plane_idx;
-
- int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}};
-
- for_each_side(side_idx) {
- for_each_plane(plane_idx) {
- struct gtphub_peer *p;
- llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) {
- count[side_idx][plane_idx] ++;
- }
- }
- }
-
- vty_out(vty, "Peers Count:%s", VTY_NEWLINE);
- for_each_side_and_plane(side_idx, plane_idx) {
- vty_out(vty, " %s %s peers: %d%s",
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- count[side_idx][plane_idx],
- VTY_NEWLINE);
- }
-}
-
-static void show_peers_all(struct vty *vty, int with_io_stats)
-{
- int side_idx;
- int plane_idx;
-
- int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}};
-
- vty_out(vty, "All Peers%s%s",
- with_io_stats? " with I/O stats" : "",
- VTY_NEWLINE);
- for_each_side(side_idx) {
- vty_out(vty, "- %s%s", gtphub_side_idx_names[side_idx], VTY_NEWLINE);
- for_each_plane(plane_idx) {
- struct gtphub_peer *p;
- llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) {
- count[side_idx][plane_idx] ++;
- show_peer_summary(vty, " ", side_idx, plane_idx, p, with_io_stats);
- }
- }
- }
- for_each_side_and_plane(side_idx, plane_idx) {
- vty_out(vty, "%s %s peers: %d%s",
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- count[side_idx][plane_idx],
- VTY_NEWLINE);
- }
-}
-
-
-static void show_tunnels_summary(struct vty *vty)
-{
- time_t now = gtphub_now();
-
- const int w = 36;
- int max_expiry = g_hub->expire_slowly.expiry_in_seconds;
- float seconds_per_step = ((float)max_expiry) / w;
-
- /* Print TEI mapping expiry in an ASCII histogram, like:
- TEI map summary
- Legend: '_'=0 '.'<=1% ':'<=2% '|'<=10% '#'>10% (10.0 m/step)
- CTRL: 30 mappings, valid for 360m[# :. | . : . ]1m
- USER: 30 mappings, valid for 360m[# :. | . : . ]1m
- 4 TEI mappings in total, last expiry in 359.4 min
- */
- vty_out(vty,
- "Tunnels summary%s"
- " Legend: ' '=0 '.'<=1%% ':'<=2%% '|'<=10%% '#'>10%% (%.1f m/step)%s",
- VTY_NEWLINE,
- seconds_per_step / 60.,
- VTY_NEWLINE);
-
- int last_expiry = 0;
-
- unsigned int count = 0;
-
- int histogram[w];
- memset(histogram, 0, sizeof(histogram));
-
- struct gtphub_tunnel *t;
- llist_for_each_entry(t, &g_hub->tunnels, entry) {
- count ++;
- int expiry = t->expiry_entry.expiry - now;
- last_expiry = (last_expiry > expiry) ? last_expiry : expiry;
-
- int hi = ((float)expiry) / seconds_per_step;
- if (hi < 0)
- hi = 0;
- if (hi > (w - 1))
- hi = w - 1;
- histogram[hi] ++;
- }
-
- vty_out(vty,
- " %u tunnels, valid for %dm[",
- count, max_expiry / 60);
-
- int i;
- for (i = w - 1; i >= 0; i--) {
- char c;
- int val = histogram[i];
- int percent = 100. * val / count;
- if (!val)
- c = ' ';
- else if (percent <= 1)
- c = '.';
- else if (percent <= 2)
- c = ':';
- else if (percent <= 10)
- c = '|';
- else c = '#';
- vty_out(vty, "%c", c);
- }
- vty_out(vty, "]1m%s", VTY_NEWLINE);
-
- vty_out(vty, " last expiry in %.1f min%s",
- ((float)last_expiry) / 60.,
- VTY_NEWLINE);
-}
-
-static void show_tunnels_all(struct vty *vty, int with_io_stats)
-{
- time_t now = gtphub_now();
-
- vty_out(vty, "All tunnels%s:%s"
- "Legend: TEI=<hex>: SGSN <-> GGSN (expiry in minutes), with each:%s"
- " <IP-Ctrl>[/<IP-User>] (TEI C=<TEI-Ctrl-hex> U=<TEI-User-hex>)%s",
- with_io_stats? "with I/O stats" : "",
- VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
-
- unsigned int count = 0;
- unsigned int incomplete = 0;
- struct gtphub_tunnel *tun;
- llist_for_each_entry(tun, &g_hub->tunnels, entry) {
- vty_out(vty,
- "%s (expiry in %dm)%s",
- gtphub_tunnel_str(tun),
- (int)((tun->expiry_entry.expiry - now) / 60),
- VTY_NEWLINE);
- count ++;
- if (!gtphub_tunnel_complete(tun))
- incomplete ++;
- if (with_io_stats)
- show_tunnel_stats(vty, tun);
- }
- vty_out(vty, "Total: %u tunnels (of which %u incomplete)%s",
- count, incomplete, VTY_NEWLINE);
-}
-
-#define SHOW_GTPHUB_STRS SHOW_STR "Show info on running GTP hub\n"
-#define SHOW_GTPHUB_PEERS_STRS SHOW_GTPHUB_STRS "Active peers\n"
-#define SHOW_GTPHUB_TUNS_STRS SHOW_GTPHUB_STRS "Active tunnels\n"
-
-DEFUN(show_gtphub_peers_summary, show_gtphub_peers_summary_cmd, "show gtphub peers summary",
- SHOW_GTPHUB_PEERS_STRS "Summary of all peers\n")
-{
- show_peers_summary(vty);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gtphub_peers_list, show_gtphub_peers_list_cmd, "show gtphub peers list",
- SHOW_GTPHUB_PEERS_STRS "List all peers\n")
-{
- show_peers_all(vty, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gtphub_peers_stats, show_gtphub_peers_stats_cmd, "show gtphub peers stats",
- SHOW_GTPHUB_PEERS_STRS "List all peers with I/O stats\n")
-{
- show_peers_all(vty, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gtphub_tunnels_summary, show_gtphub_tunnels_summary_cmd, "show gtphub tunnels summary",
- SHOW_GTPHUB_TUNS_STRS "Summary of all tunnels\n")
-{
- show_tunnels_summary(vty);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gtphub_tunnels_list, show_gtphub_tunnels_list_cmd, "show gtphub tunnels list",
- SHOW_GTPHUB_TUNS_STRS "List all tunnels\n")
-{
- show_tunnels_all(vty, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gtphub_tunnels_stats, show_gtphub_tunnels_stats_cmd, "show gtphub tunnels stats",
- SHOW_GTPHUB_TUNS_STRS "List all tunnels with I/O stats\n")
-{
- show_tunnels_all(vty, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gtphub, show_gtphub_cmd, "show gtphub all",
- SHOW_GTPHUB_STRS "Summarize everything about the GTP hub\n")
-{
- show_bind_stats_all(vty);
- show_peers_summary(vty);
- show_tunnels_summary(vty);
- return CMD_SUCCESS;
-}
-
-
-int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg)
-{
- g_hub = global_hub;
- g_cfg = global_cfg;
-
- install_element_ve(&show_gtphub_cmd);
- install_element_ve(&show_gtphub_peers_summary_cmd);
- install_element_ve(&show_gtphub_peers_list_cmd);
- install_element_ve(&show_gtphub_peers_stats_cmd);
- install_element_ve(&show_gtphub_tunnels_summary_cmd);
- install_element_ve(&show_gtphub_tunnels_list_cmd);
- install_element_ve(&show_gtphub_tunnels_stats_cmd);
-
- install_element(CONFIG_NODE, &cfg_gtphub_cmd);
- install_node(&gtphub_node, config_write_gtphub);
-
- install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_short_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_short_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_short_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_short_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_use_sender_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_no_sgsn_use_sender_cmd);
- install_element(GTPHUB_NODE, &cfg_grx_ggsn_cmd);
-
- return 0;
-}
-
-int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file)
-{
- int rc;
-
- rc = vty_read_config_file(config_file, NULL);
- if (rc < 0) {
- fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
- return rc;
- }
-
- return 0;
-}