aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2015-11-24 13:31:06 +0100
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2015-12-03 11:40:03 +0100
commite54cd1555a874b132116b1269264de7d6cc3d24d (patch)
treee508fd06bc542dd6f67abe698f55b8690a4aa683
parent2c8b58139f3e3c29309dd6518f77a62414de560c (diff)
gtphub: track tunnels explicitly.
So far, gtphub worked perfectly by only tracking single TEIs ... for probably most uses. But a Ctrl plane tunnel may have expired despite a still active corresponding User plane tunnel. The User plane would continue to work indefinitely, but if any Ctrl messages followed after more than six hours of Ctrl silence, they would have been dropped due to an expired TEI mapping. We want to - combine expiry of a user TEI with its ctrl TEI. (done in this patch) - upon delete PDP context, remove both user and ctrl TEI mappings. (future) - when a peer indicates a restart counter bump, invalidate its tunnels. (future) To facilitate these, track tunnels, complete with both SGSN's and GGSN's address, original and replaced TEIs, all for both user and ctrl plane, in a single struct. A single expiry entry handles the entire tunnel, instead of previously four separate expiries for each endpoint identifier. Add the concept of a "side", being either GGSN or SGSN, to index tunnel endpoint structs, and so on. Track the originating side in the gtp_packet_desc. Add header_tei_rx: set_tei() overwrites header_tei, but the originally received header TEI is still needed to match a Create PDP Context Response up with its Request (and for logging). Adjust the test suite to expect tunnel listing strings instead of TEI mappings, with a bonus of making it a lot easier to grok, and including the IP addresses. Add regression test for refreshing tunnel expiry upon use. Note: the current implementation is as slow as can possibly be, iterating all the tunnels all the time. Optimizations are kept for a future commit, on purpose. BTW, the sequence number mapping/unmapping structures remain unchanged. Sponsored-by: On-Waves ehi
-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,
},
};