aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openbsc/include/openbsc/gtphub.h33
-rw-r--r--openbsc/src/gprs/gtphub.c349
-rw-r--r--openbsc/src/gprs/gtphub_ares.c2
-rw-r--r--openbsc/src/gprs/gtphub_vty.c158
-rw-r--r--openbsc/tests/gtphub/gtphub_test.c73
5 files changed, 462 insertions, 153 deletions
diff --git a/openbsc/include/openbsc/gtphub.h b/openbsc/include/openbsc/gtphub.h
index f2130844a..5db6f6430 100644
--- a/openbsc/include/openbsc/gtphub.h
+++ b/openbsc/include/openbsc/gtphub.h
@@ -135,6 +135,17 @@ enum gtphub_plane_idx {
GTPH_PLANE_N
};
+enum gtphub_side_idx {
+ GTPH_SIDE_GGSN = 0,
+ GTPH_SIDE_SGSN = 1,
+ GTPH_SIDE_N
+};
+
+static inline int other_side_idx(int side_idx)
+{
+ return (side_idx + 1) & 1;
+}
+
extern const char* const gtphub_plane_idx_names[GTPH_PLANE_N];
extern const uint16_t gtphub_plane_idx_default_port[GTPH_PLANE_N];
@@ -376,6 +387,19 @@ struct gtphub_peer_port {
struct osmo_sockaddr sa;
};
+struct gtphub_tunnel_endpoint {
+ struct gtphub_peer_port *peer;
+ uint32_t tei_orig; /* from/to peer */
+ uint32_t tei_repl; /* from/to the other tunnel endpoint */
+};
+
+struct gtphub_tunnel {
+ struct llist_head entry;
+ struct expiring_item expiry_entry;
+
+ struct gtphub_tunnel_endpoint endpoint[GTPH_SIDE_N][GTPH_PLANE_N];
+};
+
struct gtphub_bind {
struct gsn_addr local_addr;
uint16_t local_port;
@@ -416,9 +440,10 @@ struct gtphub {
* uint32_t; if a new TEI were mapped every second, this would take
* more than 100 years (in which a single given TEI must not time out)
* to cause a problem. */
- struct nr_map tei_map[GTPH_PLANE_N];
struct nr_pool tei_pool[GTPH_PLANE_N];
+ struct llist_head tunnels; /* struct gtphub_tunnel */
+
struct llist_head ggsn_lookups; /* opaque (gtphub_ares.c) */
struct llist_head resolved_ggsns; /* struct gtphub_resolved_ggsn */
@@ -454,6 +479,12 @@ const char *gtphub_peer_str(struct gtphub_peer *peer);
/* Same with a different static buffer. We often want to print two peers. */
const char *gtphub_peer_str2(struct gtphub_peer *peer);
+/* Return a human readable description of tun in a static buffer. */
+const char *gtphub_tunnel_str(struct gtphub_tunnel *tun);
+
+/* Return 1 if all of tun's endpoints are fully established, 0 otherwise. */
+int gtphub_tunnel_complete(struct gtphub_tunnel *tun);
+
int gtphub_from_sgsns_handle_buf(struct gtphub *hub,
unsigned int port_idx,
const struct osmo_sockaddr *from_addr,
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c
index 4200822aa..60c3cc191 100644
--- a/openbsc/src/gprs/gtphub.c
+++ b/openbsc/src/gprs/gtphub.c
@@ -86,9 +86,11 @@ struct gtp_packet_desc {
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;
time_t timestamp;
union gtpie_member *ie[GTPIE_SIZE];
};
@@ -269,7 +271,8 @@ void validate_gtp0_header(struct gtp_packet_desc *p)
p->type = ntoh8(pheader->type);
p->seq = ntoh16(pheader->seq);
- p->header_tei = 0; /* TODO */
+ 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;
@@ -311,7 +314,8 @@ void validate_gtp1_header(struct gtp_packet_desc *p)
}
p->type = ntoh8(pheader->type);
- p->header_tei = ntoh32(pheader->tei);
+ p->header_tei_rx = ntoh32(pheader->tei);
+ p->header_tei = p->header_tei_rx;
p->seq = ntoh16(pheader->seq);
LOG(LOGL_DEBUG, "GTPv1\n"
@@ -323,7 +327,7 @@ void validate_gtp1_header(struct gtp_packet_desc *p)
"| next = %" PRIu8 " 0x%02" PRIx8 "\n",
p->type, p->type,
ntoh16(pheader->length), ntoh16(pheader->length),
- p->header_tei, p->header_tei,
+ p->header_tei_rx, p->header_tei_rx,
p->seq, p->seq,
pheader->npdu, pheader->npdu,
pheader->next, pheader->next);
@@ -473,6 +477,7 @@ static int get_ie_apn_str(union gtpie_member *ie[], const char **apn_str)
* 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)
@@ -480,6 +485,7 @@ static void gtp_decode(const uint8_t *data, int data_len,
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;
@@ -897,12 +903,14 @@ static int gtphub_read(const struct osmo_fd *from,
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++;
}
inline void gtphub_port_ref_count_dec(struct gtphub_peer_port *pp)
{
+ OSMO_ASSERT(pp);
OSMO_ASSERT(pp->ref_count > 0);
pp->ref_count--;
}
@@ -934,6 +942,128 @@ static struct nr_mapping *gtphub_mapping_new()
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=%x / U %x=%x)",
+ c->tei_orig, c->tei_repl,
+ u->tei_orig, u->tei_repl);
+ return buf;
+}
+
+const char *gtphub_tunnel_str(struct gtphub_tunnel *tun)
+{
+ static char buf[512];
+ char *pos = buf;
+ int left = sizeof(buf);
+ int l;
+
+ 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;
+ int side_idx;
+ int plane_idx;
+ for (side_idx = 0; side_idx < GTPH_SIDE_N; side_idx++) {
+ for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) {
+ struct gtphub_tunnel_endpoint *te =
+ &tun->endpoint[side_idx][plane_idx];
+ if (!(te->peer && te->tei_orig && te->tei_repl))
+ 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 (side_idx = 0; side_idx < GTPH_SIDE_N; side_idx++) {
+ for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) {
+ /* clear ref count */
+ gtphub_tunnel_endpoint_set_peer(
+ &tun->endpoint[side_idx][plane_idx], NULL);
+ }
+ }
+
+ talloc_free(tun);
+}
+
+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);
+
+ 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)
{
@@ -985,6 +1115,7 @@ static const char *gtphub_port_str2(struct gtphub_peer_port *port)
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,
@@ -1031,22 +1162,6 @@ static struct nr_mapping *gtphub_mapping_have(struct nr_map *map,
return nrm;
}
-static uint32_t gtphub_tei_mapping_have(struct gtphub *hub,
- int plane_idx,
- struct gtphub_peer_port *from,
- uint32_t orig_tei,
- time_t now)
-{
- struct nr_mapping *nrm = gtphub_mapping_have(&hub->tei_map[plane_idx],
- from, orig_tei, now);
- LOG(LOGL_DEBUG, "New %s TEI: (from %s, TEI %u) <-- TEI %u\n",
- gtphub_plane_idx_names[plane_idx],
- gtphub_port_str(from),
- (unsigned int)orig_tei, (unsigned int)nrm->repl);
-
- return (uint32_t)nrm->repl;
-}
-
static void gtphub_map_seq(struct gtp_packet_desc *p,
struct gtphub_peer_port *from_port,
struct gtphub_peer_port *to_port)
@@ -1077,6 +1192,100 @@ static struct gtphub_peer_port *gtphub_unmap_seq(struct gtp_packet_desc *p,
return nrm->origin;
}
+static struct gtphub_tunnel *gtphub_tun_find(struct gtphub *hub,
+ int side_idx,
+ int plane_idx,
+ struct gtphub_peer_port *from,
+ uint32_t tei_orig,
+ uint32_t tei_repl)
+{
+ OSMO_ASSERT(from);
+
+ struct gtphub_tunnel *tun;
+ /* TODO: optimize: don't iterate *all* tunnels. */
+ llist_for_each_entry(tun, &hub->tunnels, entry) {
+ struct gtphub_tunnel_endpoint *te =
+ &tun->endpoint[side_idx][plane_idx];
+ if (((!tei_orig) || (te->tei_orig == tei_orig))
+ && ((!tei_repl) || (te->tei_repl == tei_repl))
+ && gsn_addr_same(&te->peer->peer_addr->addr, &from->peer_addr->addr))
+ return tun;
+ }
+ return NULL;
+}
+
+static void gtphub_expire_reused_tei(struct gtphub *hub,
+ int side_idx,
+ int plane_idx,
+ struct gtphub_peer_port *from,
+ uint32_t tei_orig,
+ struct gtphub_tunnel *except)
+{
+ struct gtphub_tunnel *exists, *e2;
+
+ llist_for_each_entry_safe(exists, e2, &hub->tunnels, entry) {
+ if (exists == except)
+ continue;
+
+ struct gtphub_tunnel_endpoint *te =
+ &exists->endpoint[side_idx][plane_idx];
+ if ((te->tei_orig == tei_orig)
+ && gsn_addr_same(&te->peer->peer_addr->addr,
+ &from->peer_addr->addr)) {
+
+ /* 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:"
+ " peer %s sent %s TEI %x,"
+ " previously used by tunnel %s...\n",
+ gtphub_port_str(from),
+ gtphub_plane_idx_names[plane_idx],
+ tei_orig,
+ gtphub_tunnel_str(exists));
+ LOG(LOGL_NOTICE, "...while establishing tunnel %s\n",
+ gtphub_tunnel_str(except));
+ expiring_item_del(&exists->expiry_entry);
+ /* continue to find more matches. There shouldn't be
+ * any, but let's make sure. */
+ }
+ }
+}
+
+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)
+{
+ 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 ((te_to->tei_repl == p->header_tei_rx)
+ && gsn_addr_same(&te_from->peer->peer_addr->addr,
+ &from->peer_addr->addr)) {
+ gtphub_tunnel_refresh(hub, tun, p->timestamp);
+ return te_to;
+ }
+ }
+ return NULL;
+}
+
+
static void gtphub_check_restart_counter(struct gtphub *hub,
struct gtp_packet_desc *p,
struct gtphub_peer_port *from)
@@ -1106,30 +1315,28 @@ static int gtphub_unmap_header_tei(struct gtphub_peer_port **to_port_p,
/* 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. */
- uint32_t tei = p->header_tei;
- if (!tei)
+ if (!p->header_tei_rx)
return 0;
/* to_peer has previously announced a TEI, which was stored and
- * mapped in from_peer's tei_map. */
- struct nr_mapping *nrm;
- nrm = nr_map_get_inv(&hub->tei_map[p->plane_idx], tei);
- if (!nrm) {
- LOG(LOGL_ERROR, "Received unknown TEI %" PRIu32 " from %s\n",
- tei, gtphub_port_str(from_port));
+ * mapped in a tunnel struct. */
+ struct gtphub_tunnel_endpoint *to;
+ to = gtphub_unmap_tei(hub, p, from_port);
+ if (!to) {
+ LOG(LOGL_ERROR, "Received unknown TEI %" PRIx32 " from %s\n",
+ p->header_tei_rx, gtphub_port_str(from_port));
return -1;
}
- struct gtphub_peer_port *to_port = nrm->origin;
- uint32_t unmapped_tei = nrm->orig;
+ uint32_t unmapped_tei = to->tei_orig;
set_tei(p, unmapped_tei);
- LOG(LOGL_DEBUG, "Unmapped TEI coming from %s: %u -> %u (to %s)\n",
- gtphub_port_str(from_port),
- (unsigned int)tei, (unsigned int)unmapped_tei,
- gtphub_port_str2(to_port));
+ LOG(LOGL_DEBUG, "Unmapped TEI coming from %s: %" PRIx32 " -> %" PRIx32 " (to %s)\n",
+ gtphub_port_str(from_port), p->header_tei_rx, unmapped_tei,
+ gtphub_port_str2(to->peer));
+
+ *to_port_p = to->peer;
- *to_port_p = to_port;
return 0;
}
@@ -1140,6 +1347,8 @@ static int gtphub_unmap_header_tei(struct gtphub_peer_port **to_port_p,
static int gtphub_handle_pdp_ctx_ies(struct gtphub *hub,
struct gtphub_bind from_bind_arr[],
struct gtphub_bind to_bind_arr[],
+ struct gtphub_peer_port *sgsn_ctrl,
+ struct gtphub_peer_port *ggsn_ctrl,
struct gtp_packet_desc *p)
{
OSMO_ASSERT(p->plane_idx == GTPH_PLANE_CTRL);
@@ -1162,15 +1371,41 @@ static int gtphub_handle_pdp_ctx_ies(struct gtphub *hub,
osmo_static_assert((GTPH_PLANE_CTRL == 0) && (GTPH_PLANE_USER == 1),
plane_nrs_match_GSN_addr_IE_indices);
+ struct gtphub_tunnel *tun = NULL;
+
+ if (p->type == GTP_CREATE_PDP_REQ) {
+ /* A new tunnel. */
+ tun = gtphub_tunnel_new();
+ llist_add(&tun->entry, &hub->tunnels);
+ gtphub_tunnel_refresh(hub, tun, p->timestamp);
+ gtphub_tunnel_endpoint_set_peer(&tun->endpoint[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL],
+ ggsn_ctrl);
+ } else if (p->type == GTP_CREATE_PDP_RSP) {
+ /* Find the tunnel created during request */
+ OSMO_ASSERT(sgsn_ctrl);
+ tun = gtphub_tun_find(hub, other_side_idx(p->side_idx),
+ p->plane_idx, sgsn_ctrl, 0, p->header_tei_rx);
+
+ if (!tun) {
+ LOG(LOGL_ERROR, "Create PDP Context Response: cannot"
+ " find matching request from SGSN %s, mapped TEI %x.\n",
+ gtphub_port_str(sgsn_ctrl), p->header_tei_rx);
+ return -1;
+ }
+ }
+
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 (plane_idx = 0; plane_idx < 2; plane_idx++) {
struct gsn_addr addr_from_ie;
uint32_t tei_from_ie;
int ie_idx;
- /* Fetch GSN Address and TEI from IEs */
+ /* 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(&addr_from_ie, p, plane_idx);
if (rc) {
LOG(LOGL_ERROR, "Cannot read %s GSN Address IE\n",
@@ -1203,14 +1438,20 @@ static int gtphub_handle_pdp_ctx_ies(struct gtphub *hub,
&addr_from_ie,
gtphub_plane_idx_default_port[plane_idx]);
+ gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][plane_idx],
+ peer_from_ie);
+
if (tei_from_ie) {
/* Create TEI mapping and replace in GTP packet IE */
- uint32_t mapped_tei =
- gtphub_tei_mapping_have(hub, plane_idx,
- peer_from_ie,
- tei_from_ie,
- p->timestamp);
+ uint32_t mapped_tei = nr_pool_next(&hub->tei_pool[plane_idx]);
+
+ tun->endpoint[side_idx][plane_idx].tei_orig = tei_from_ie;
+ tun->endpoint[side_idx][plane_idx].tei_repl = mapped_tei;
p->ie[ie_idx]->tv4.v = hton32(mapped_tei);
+
+ gtphub_expire_reused_tei(hub, side_idx, plane_idx,
+ peer_from_ie, tei_from_ie,
+ tun);
}
/* Replace the GSN address to reflect gtphub. */
@@ -1223,6 +1464,14 @@ static int gtphub_handle_pdp_ctx_ies(struct gtphub *hub,
}
}
+ 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_REQ) {
+ LOG(LOGL_DEBUG, "New tunnel: %s\n",
+ gtphub_tunnel_str(tun));
+ }
+
return 0;
}
@@ -1319,7 +1568,7 @@ static int gtphub_unmap(struct gtphub *hub,
gtphub_peer_str(from_peer),
(int)p->seq,
gtphub_port_str(from_seq),
- (unsigned int)p->header_tei,
+ (unsigned int)p->header_tei_rx,
gtphub_port_str2(from_tei)
);
}
@@ -1341,9 +1590,6 @@ static int gtphub_unmap(struct gtphub *hub,
return 0;
}
- LOG(LOGL_DEBUG, "from seq %p; from tei %p; unmapped => %p\n",
- from_seq, from_tei, unmapped);
-
if (unmapped_from_seq)
*unmapped_from_seq = from_seq;
if (unmapped_from_tei)
@@ -1420,7 +1666,7 @@ int gtphub_from_ggsns_handle_buf(struct gtphub *hub,
osmo_sockaddr_to_str(from_addr));
static struct gtp_packet_desc p;
- gtp_decode(buf, received, plane_idx, &p, now);
+ gtp_decode(buf, received, GTPH_SIDE_GGSN, plane_idx, &p, now);
if (p.rc <= 0)
return -1;
@@ -1502,7 +1748,7 @@ int gtphub_from_ggsns_handle_buf(struct gtphub *hub,
* are other addresses in the GTP message to set up apart from
* the sender. */
if (gtphub_handle_pdp_ctx_ies(hub, from_bind_arr, to_bind_arr,
- &p)
+ sgsn, ggsn, &p)
!= 0)
return -1;
}
@@ -1585,7 +1831,7 @@ int gtphub_from_sgsns_handle_buf(struct gtphub *hub,
osmo_sockaddr_to_str(from_addr));
static struct gtp_packet_desc p;
- gtp_decode(buf, received, plane_idx, &p, now);
+ gtp_decode(buf, received, GTPH_SIDE_SGSN, plane_idx, &p, now);
if (p.rc <= 0)
return -1;
@@ -1684,11 +1930,11 @@ int gtphub_from_sgsns_handle_buf(struct gtphub *hub,
}
if (plane_idx == GTPH_PLANE_CTRL) {
- /* This may be a Create PDP Context requst. If it is, there are
+ /* This may be a Create PDP Context request. If it is, there are
* other addresses in the GTP message to set up apart from the
* sender. */
if (gtphub_handle_pdp_ctx_ies(hub, from_bind_arr, to_bind_arr,
- &p)
+ sgsn, ggsn, &p)
!= 0)
return -1;
}
@@ -1848,15 +2094,14 @@ void gtphub_init(struct gtphub *hub)
{
gtphub_zero(hub);
+ INIT_LLIST_HEAD(&hub->tunnels);
+
expiry_init(&hub->expire_quickly, GTPH_EXPIRE_QUICKLY_SECS);
expiry_init(&hub->expire_slowly, GTPH_EXPIRE_SLOWLY_MINUTES * 60);
int plane_idx;
for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) {
nr_pool_init(&hub->tei_pool[plane_idx], 1, 0xffffffff);
- nr_map_init(&hub->tei_map[plane_idx],
- &hub->tei_pool[plane_idx],
- &hub->expire_slowly);
gtphub_bind_init(&hub->to_ggsns[plane_idx]);
gtphub_bind_init(&hub->to_sgsns[plane_idx]);
diff --git a/openbsc/src/gprs/gtphub_ares.c b/openbsc/src/gprs/gtphub_ares.c
index 7688b4788..947f2ddd5 100644
--- a/openbsc/src/gprs/gtphub_ares.c
+++ b/openbsc/src/gprs/gtphub_ares.c
@@ -214,7 +214,7 @@ struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub,
llist_add(&lookup->entry, &hub->ggsn_lookups);
lookup->expiry_entry.del_cb = ggsn_lookup_del_cb;
- expiry_add(&hub->expire_seq_maps, &lookup->expiry_entry, gtphub_now());
+ expiry_add(&hub->expire_quickly, &lookup->expiry_entry, gtphub_now());
start_ares_query(lookup);
diff --git a/openbsc/src/gprs/gtphub_vty.c b/openbsc/src/gprs/gtphub_vty.c
index 602508854..f99f81803 100644
--- a/openbsc/src/gprs/gtphub_vty.c
+++ b/openbsc/src/gprs/gtphub_vty.c
@@ -280,132 +280,112 @@ static void show_peers_summary(struct vty *vty)
}
*/
-static void show_tei_maps_summary(struct vty *vty)
+static void show_tunnels_summary(struct vty *vty)
{
time_t now = gtphub_now();
- unsigned long long int count_all = 0; /* ...just joking. */
-
const int w = 36;
- int max_expiry = g_hub->expire_tei_maps.expiry_in_seconds;
+ 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
+ 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,
- "TEI map summary%s"
- " Legend: '_'=0 '.'<=1%% ':'<=2%% '|'<=10%% '#'>10%% (%.1f m/step)%s",
+ "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;
- int plane_idx;
- for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) {
- unsigned int count = 0;
+ unsigned int count = 0;
- int histogram[w];
- memset(histogram, 0, sizeof(histogram));
+ int histogram[w];
+ memset(histogram, 0, sizeof(histogram));
- struct nr_mapping *m;
- llist_for_each_entry(m, &g_hub->tei_map[plane_idx].mappings, entry) {
- count ++;
- int expiry = m->expiry_entry.expiry - now;
- last_expiry = (last_expiry > expiry) ? last_expiry : expiry;
+ 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] ++;
- }
+ int hi = ((float)expiry) / seconds_per_step;
+ if (hi < 0)
+ hi = 0;
+ if (hi > (w - 1))
+ hi = w - 1;
+ histogram[hi] ++;
+ }
- vty_out(vty,
- " %4s: %u mappings, valid for %dm[",
- gtphub_plane_idx_names[plane_idx],
- 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);
-
- count_all += count;
+ 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, " %llu TEI mappings in total, last expiry in %.1f min%s",
- count_all,
+ 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_tei_maps_all(struct vty *vty)
+static void show_tunnels_all(struct vty *vty)
{
time_t now = gtphub_now();
- unsigned long long int count_all = 0; /* ...just joking. */
+ vty_out(vty, "All tunnels:%s"
+ "Legend: SGSN <-> GGSN, with each:%s"
+ " <IP-Ctrl>[/<IP-User>] (<TEI-Ctrl>=<mapped>/<TEI-User>=<mapped>)%s",
+ VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
- vty_out(vty, "All TEI mappings:%s", VTY_NEWLINE);
- int plane_idx;
- for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) {
+ unsigned int count = 0;
+ unsigned int incomplete = 0;
+ struct gtphub_tunnel *t;
+ llist_for_each_entry(t, &g_hub->tunnels, entry) {
vty_out(vty,
- "- %s Plane:%s"
- " (timeout) replaced-TEI <--> original-TEI from-peer%s",
- gtphub_plane_idx_names[plane_idx],
- VTY_NEWLINE, VTY_NEWLINE);
-
- unsigned int count = 0;
- struct nr_mapping *m;
- llist_for_each_entry(m, &g_hub->tei_map[plane_idx].mappings, entry) {
- struct gtphub_peer_port *pp = m->origin;
- vty_out(vty,
- " (%4dm) %8x <--> %8x %s",
- -(int)((m->expiry_entry.expiry - now) / 60),
- (uint32_t)m->repl,
- (uint32_t)m->orig,
- gsn_addr_to_str(&pp->peer_addr->addr));
- if (pp->port != gtphub_plane_idx_default_port[plane_idx])
- vty_out(vty, " port %d", (int)pp->port);
- vty_out(vty, VTY_NEWLINE);
- count ++;
- }
- vty_out(vty, " (%u %s TEI mappings)%s", count,
- gtphub_plane_idx_names[plane_idx], VTY_NEWLINE);
- count_all += count;
+ "(%4dm) %s%s",
+ -(int)((t->expiry_entry.expiry - now) / 60),
+ gtphub_tunnel_str(t),
+ VTY_NEWLINE);
+ count ++;
+ if (!gtphub_tunnel_complete(t))
+ incomplete ++;
}
- vty_out(vty, "- %llu TEI mappings in total%s", count_all, VTY_NEWLINE);
+ vty_out(vty, "Total: %u tunnels%s", count, VTY_NEWLINE);
}
-DEFUN(show_gtphub_tei_summary, show_gtphub_tei_summary_cmd, "show gtphub tei summary",
- SHOW_STR "TEI mappings summary")
+DEFUN(show_gtphub_tunnels_summary, show_gtphub_tunnels_summary_cmd, "show gtphub tunnels summary",
+ SHOW_STR "Summary of all tunnels")
{
- show_tei_maps_summary(vty);
+ show_tunnels_summary(vty);
return CMD_SUCCESS;
}
-DEFUN(show_gtphub_tei_dump, show_gtphub_tei_dump_cmd, "show gtphub tei dump",
- SHOW_STR "Dump all current TEI mappings")
+DEFUN(show_gtphub_tunnels_list, show_gtphub_tunnels_list_cmd, "show gtphub tunnels list",
+ SHOW_STR "List all tunnels")
{
- show_tei_maps_all(vty);
+ show_tunnels_all(vty);
return CMD_SUCCESS;
}
@@ -413,7 +393,7 @@ DEFUN(show_gtphub, show_gtphub_cmd, "show gtphub all",
SHOW_STR "Display information about the GTP hub")
{
show_bind_stats_all(vty);
- show_tei_maps_summary(vty);
+ show_tunnels_summary(vty);
return CMD_SUCCESS;
}
@@ -424,8 +404,8 @@ int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg)
g_cfg = global_cfg;
install_element_ve(&show_gtphub_cmd);
- install_element_ve(&show_gtphub_tei_summary_cmd);
- install_element_ve(&show_gtphub_tei_dump_cmd);
+ install_element_ve(&show_gtphub_tunnels_summary_cmd);
+ install_element_ve(&show_gtphub_tunnels_list_cmd);
install_element(CONFIG_NODE, &cfg_gtphub_cmd);
install_node(&gtphub_node, config_write_gtphub);
diff --git a/openbsc/tests/gtphub/gtphub_test.c b/openbsc/tests/gtphub/gtphub_test.c
index 2dc61eb8e..418100261 100644
--- a/openbsc/tests/gtphub/gtphub_test.c
+++ b/openbsc/tests/gtphub/gtphub_test.c
@@ -49,7 +49,7 @@
/* Convenience makro, note: only within this C file. */
#define LOG(label) \
- { LOGP(DGTPHUB, LOGL_NOTICE, "\n\n" label "\n"); \
+ { fprintf(stderr, "\n" label "\n"); \
printf(label "\n"); }
void gtphub_init(struct gtphub *hub);
@@ -597,7 +597,7 @@ static int setup_test_hub()
now = 345;
LVL2_ASSERT(send_from_sgsn("192.168.42.23", 423));
LVL2_ASSERT(resolve_to_ggsn("192.168.43.34", 2123));
- LVL2_ASSERT(send_from_ggsn("192.168.43.34", 321));
+ LVL2_ASSERT(send_from_ggsn("192.168.43.34", 434));
LVL2_ASSERT(resolve_to_sgsn("192.168.42.23", 2123));
#define GGSNS_CTRL_FD 1
@@ -615,7 +615,7 @@ static int setup_test_hub()
static int clear_test_hub()
{
/* expire all */
- gtphub_gc(hub, now + (60 * GTPH_TEI_MAPPING_EXPIRY_MINUTES) + 1);
+ gtphub_gc(hub, now + (60 * GTPH_EXPIRE_SLOWLY_MINUTES) + 1);
int plane_idx;
plane_idx = GTPH_PLANE_CTRL;
@@ -629,6 +629,32 @@ static int clear_test_hub()
return 1;
}
+static int tunnels_are(const char *expect)
+{
+ static char buf[4096];
+ char *pos = buf;
+ size_t len = sizeof(buf);
+ struct gtphub_tunnel *t;
+ llist_for_each_entry(t, &hub->tunnels, entry) {
+ size_t wrote = snprintf(pos, len, "%s @%d\n",
+ gtphub_tunnel_str(t),
+ (int)t->expiry_entry.expiry);
+ LVL2_ASSERT(wrote < len);
+ pos += wrote;
+ len -= wrote;
+ }
+ *pos = '\0';
+
+ if (strncmp(buf, expect, sizeof(buf)) != 0) {
+ fprintf(stderr, "FAILURE: tunnels_are() mismatches expected value:\n"
+ "EXPECTED:\n%s\n"
+ "IS:\n%s\n",
+ expect, buf);
+ LVL2_ASSERT("tunnels do not match expected listing.");
+ return 0;
+ }
+ return 1;
+}
static void test_echo(void)
{
@@ -893,6 +919,11 @@ static int create_pdp_ctx()
gtp_req_to_ggsn));
LVL2_ASSERT(was_resolved_for("240010123456789", "internet"));
+ LVL2_ASSERT(tunnels_are(
+ "192.168.42.23 (TEI C 321=1 / U 123=1)"
+ " <-> 192.168.43.34 / (uninitialized) (TEI C 0=0 / U 0=0)"
+ " @21945\n"));
+
const char *gtp_resp_from_ggsn =
MSG_PDP_CTX_RSP("004e",
"00000001", /* destination TEI (sent in req above) */
@@ -947,11 +978,10 @@ static void test_create_pdp_ctx(void)
* 0x00000765 == 1893 (TEI from GGSN Ctrl)
* 0x00000567 == 1383 (TEI from GGSN User)
* Mapped TEIs should be 1 and 2. */
- OSMO_ASSERT(nr_map_is(&hub->tei_map[GTPH_PLANE_CTRL],
- "(801->1@21945), (1893->2@21945), "));
- OSMO_ASSERT(nr_map_is(&hub->tei_map[GTPH_PLANE_USER],
- "(291->1@21945), (1383->2@21945), "));
-
+ OSMO_ASSERT(tunnels_are(
+ "192.168.42.23 (TEI C 321=1 / U 123=1)"
+ " <-> 192.168.43.34 (TEI C 765=2 / U 567=2)"
+ " @21945\n"));
OSMO_ASSERT(clear_test_hub());
}
@@ -963,11 +993,20 @@ static void test_user_data(void)
OSMO_ASSERT(create_pdp_ctx());
+ /* now == 345; now + (6 * 60 * 60) == 21600 + 345 == 21945. */
+ OSMO_ASSERT(tunnels_are(
+ "192.168.42.23 (TEI C 321=1 / U 123=1)"
+ " <-> 192.168.43.34 (TEI C 765=2 / U 567=2)"
+ " @21945\n"));
+
LOG("- user data starts");
- /* Now expect default port numbers for User. */
+ /* Now expect default port numbers for User plane. */
resolve_to_ggsn("192.168.43.34", 2152);
resolve_to_sgsn("192.168.42.23", 2152);
+ /* 10 minutes later */
+ now += 600;
+
const char *u_from_ggsn =
"32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr */
"ff" /* type 255: G-PDU */
@@ -999,6 +1038,13 @@ static void test_user_data(void)
u_from_ggsn,
u_to_sgsn));
+ /* Make sure the user plane messages have refreshed the TEI mapping
+ * timeouts: 21945 + 600 == 22545. */
+ OSMO_ASSERT(tunnels_are(
+ "192.168.42.23 (TEI C 321=1 / U 123=1)"
+ " <-> 192.168.43.34 (TEI C 765=2 / U 567=2)"
+ " @22545\n"));
+
const char *u_from_sgsn =
"32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr */
"ff" /* type 255: G-PDU */
@@ -1028,6 +1074,13 @@ static void test_user_data(void)
u_from_sgsn,
u_to_ggsn));
+ /* Make sure the user plane messages have refreshed the TEI mapping
+ * timeouts: 21945 + 600 == 22545. Both timeouts refreshed: */
+ OSMO_ASSERT(tunnels_are(
+ "192.168.42.23 (TEI C 321=1 / U 123=1)"
+ " <-> 192.168.43.34 (TEI C 765=2 / U 567=2)"
+ " @22545\n"));
+
OSMO_ASSERT(clear_test_hub());
}
@@ -1037,7 +1090,7 @@ static struct log_info_cat gtphub_categories[] = {
.name = "DGTPHUB",
.description = "GTP Hub",
.color = "\033[1;33m",
- .enabled = 1, .loglevel = LOGL_NOTICE,
+ .enabled = 1, .loglevel = LOGL_DEBUG,
},
};