aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openbsc/include/openbsc/gtphub.h1
-rw-r--r--openbsc/src/gprs/gtphub.c169
-rw-r--r--openbsc/tests/gtphub/gtphub_test.c76
-rw-r--r--openbsc/tests/gtphub/gtphub_test.ok2
4 files changed, 229 insertions, 19 deletions
diff --git a/openbsc/include/openbsc/gtphub.h b/openbsc/include/openbsc/gtphub.h
index c72a0cfbd..ea0f964a6 100644
--- a/openbsc/include/openbsc/gtphub.h
+++ b/openbsc/include/openbsc/gtphub.h
@@ -443,6 +443,7 @@ struct gtphub {
struct nr_pool tei_pool;
struct llist_head tunnels; /* struct gtphub_tunnel */
+ struct llist_head pending_deletes; /* opaque (gtphub.c) */
struct llist_head ggsn_lookups; /* opaque (gtphub_ares.c) */
struct llist_head resolved_ggsns; /* struct gtphub_resolved_ggsn */
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c
index 465520ee4..dcfa823fa 100644
--- a/openbsc/src/gprs/gtphub.c
+++ b/openbsc/src/gprs/gtphub.c
@@ -95,6 +95,15 @@ struct gtp_packet_desc {
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 */
@@ -1325,7 +1334,8 @@ static void gtphub_tunnel_refresh(struct gtphub *hub,
static struct gtphub_tunnel_endpoint *gtphub_unmap_tei(struct gtphub *hub,
struct gtp_packet_desc *p,
- struct gtphub_peer_port *from)
+ struct gtphub_peer_port *from,
+ struct gtphub_tunnel **unmapped_from_tun)
{
OSMO_ASSERT(from);
int other_side = other_side_idx(p->side_idx);
@@ -1341,9 +1351,14 @@ static struct gtphub_tunnel_endpoint *gtphub_unmap_tei(struct gtphub *hub,
&& 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;
}
@@ -1376,12 +1391,15 @@ static void gtphub_map_restart_counter(struct gtphub *hub,
}
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
@@ -1392,7 +1410,7 @@ static int gtphub_unmap_header_tei(struct gtphub_peer_port **to_port_p,
/* 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);
+ 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));
@@ -1549,12 +1567,132 @@ static int gtphub_handle_create_pdp_ctx(struct gtphub *hub,
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_tunnel *known_tun,
struct gtphub_peer_port *from_ctrl,
struct gtphub_peer_port *to_ctrl)
{
- /* TODO */
+ 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));
+ expiring_item_del(&known_tun->expiry_entry);
+ }
+
return 0;
}
@@ -1573,6 +1711,7 @@ static int gtphub_handle_update_pdp_ctx(struct gtphub *hub,
* the packet p. */
static int gtphub_handle_pdp_ctx(struct gtphub *hub,
struct gtp_packet_desc *p,
+ struct gtphub_tunnel *known_tun,
struct gtphub_peer_port *from_ctrl,
struct gtphub_peer_port *to_ctrl)
{
@@ -1586,7 +1725,7 @@ static int gtphub_handle_pdp_ctx(struct gtphub *hub,
case GTP_DELETE_PDP_REQ:
case GTP_DELETE_PDP_RSP:
- return gtphub_handle_delete_pdp_ctx(hub, p,
+ return gtphub_handle_delete_pdp_ctx(hub, p, known_tun,
from_ctrl, to_ctrl);
case GTP_UPDATE_PDP_REQ:
@@ -1601,7 +1740,6 @@ static int gtphub_handle_pdp_ctx(struct gtphub *hub,
}
-
static int gtphub_write(const struct osmo_fd *to,
const struct osmo_sockaddr *to_addr,
const uint8_t *buf, size_t buf_len)
@@ -1663,7 +1801,7 @@ static int gtphub_unmap(struct gtphub *hub,
struct gtphub_peer_port *to_proxy,
struct gtphub_peer_port **final_unmapped,
struct gtphub_peer_port **unmapped_from_seq,
- struct gtphub_peer_port **unmapped_from_tei)
+ struct gtphub_tunnel **unmapped_from_tun)
{
/* Always (try to) unmap sequence and TEI numbers, which need to be
* replaced in the packet. Either way, give precedence to the proxy, if
@@ -1672,17 +1810,18 @@ static int gtphub_unmap(struct gtphub *hub,
struct gtphub_peer_port *from_seq = NULL;
struct gtphub_peer_port *from_tei = NULL;
struct gtphub_peer_port *unmapped = NULL;
+ struct gtphub_tunnel *tun = NULL;
if (unmapped_from_seq)
*unmapped_from_seq = from_seq;
- if (unmapped_from_tei)
- *unmapped_from_tei = from_tei;
+ if (unmapped_from_tun)
+ *unmapped_from_tun = tun;
if (final_unmapped)
*final_unmapped = unmapped;
from_seq = gtphub_unmap_seq(p, from);
- if (gtphub_unmap_header_tei(&from_tei, hub, p, from) < 0)
+ if (gtphub_unmap_header_tei(&from_tei, &tun, hub, p, from) < 0)
return -1;
struct gtphub_peer *from_peer = from->peer_addr->peer;
@@ -1719,8 +1858,8 @@ static int gtphub_unmap(struct gtphub *hub,
if (unmapped_from_seq)
*unmapped_from_seq = from_seq;
- if (unmapped_from_tei)
- *unmapped_from_tei = from_tei;
+ if (unmapped_from_tun)
+ *unmapped_from_tun = tun;
if (final_unmapped)
*final_unmapped = unmapped;
return 0;
@@ -1894,11 +2033,10 @@ int gtphub_handle_buf(struct gtphub *hub,
struct gtphub_peer_port *to_peer_from_seq;
struct gtphub_peer_port *to_peer;
+ struct gtphub_tunnel *tun;
if (gtphub_unmap(hub, &p, from_peer,
hub->proxy[other_side_idx(side_idx)][plane_idx],
- &to_peer, &to_peer_from_seq,
- NULL /* not interested, got it in &to_peer already */
- )
+ &to_peer, &to_peer_from_seq, &tun)
!= 0) {
return -1;
}
@@ -1918,7 +2056,7 @@ int gtphub_handle_buf(struct gtphub *hub,
/* 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)
+ if (gtphub_handle_pdp_ctx(hub, &p, tun, from_peer, to_peer)
!= 0)
return -1;
}
@@ -2112,6 +2250,7 @@ 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);
diff --git a/openbsc/tests/gtphub/gtphub_test.c b/openbsc/tests/gtphub/gtphub_test.c
index b0ede11b9..fea55e03d 100644
--- a/openbsc/tests/gtphub/gtphub_test.c
+++ b/openbsc/tests/gtphub/gtphub_test.c
@@ -954,9 +954,75 @@ static int create_pdp_ctx()
return 1;
}
-static void test_create_pdp_ctx(void)
+#define MSG_DEL_PDP_CTX_REQ(tei, seq) \
+ "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr. */ \
+ "14" /* type 20: Delete PDP Context Request */ \
+ "0008" /* msg length = 8 + len (2 octets) */ \
+ tei /* TEI Ctrl */ \
+ seq /* Sequence nr (2 octets) */ \
+ "00" /* N-PDU 0 */ \
+ "00" /* No extensions */ \
+ /* IEs */ \
+ "13fe" /* 19: Teardown ind = 0 */ \
+ "1400" /* 20: NSAPI = 0*/ \
+
+#define MSG_DEL_PDP_CTX_RSP(tei, seq) \
+ "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr. */ \
+ "15" /* type 21: Delete PDP Context Response */ \
+ "0006" /* msg length = 8 + len (2 octets) */ \
+ tei /* TEI Ctrl */ \
+ seq /* Sequence nr (2 octets) */ \
+ "00" /* N-PDU 0 */ \
+ "00" /* No extensions */ \
+ /* IEs */ \
+ "01" /* 1: Cause */ \
+ "80" /* value = 0b10000000 = response, no rejection. */ \
+
+static int delete_pdp_ctx(void)
+{
+ now += GTPH_EXPIRE_QUICKLY_SECS + 1;
+ gtphub_gc(hub, now);
+
+ LVL2_ASSERT(tunnels_are(
+ "192.168.42.23 (TEI C 321=1 / U 123=2)"
+ " <-> 192.168.43.34 (TEI C 765=3 / U 567=4)"
+ " @21945\n"));
+
+ /* TEI Ctrl from above and next sequence after abcd. */
+ const char *gtp_req_from_sgsn = MSG_DEL_PDP_CTX_REQ("00000003", "abce");
+ const char *gtp_req_to_ggsn = MSG_DEL_PDP_CTX_REQ("00000765", "6d32");
+
+ LVL2_ASSERT(msg_from_sgsn_c(&sgsn_sender,
+ &resolved_ggsn_addr,
+ gtp_req_from_sgsn,
+ gtp_req_to_ggsn));
+
+ /* 21945 + 31 = 21976 */
+ LVL2_ASSERT(tunnels_are(
+ "192.168.42.23 (TEI C 321=1 / U 123=2)"
+ " <-> 192.168.43.34 (TEI C 765=3 / U 567=4)"
+ " @21976\n"));
+
+ const char *gtp_resp_from_ggsn =
+ MSG_DEL_PDP_CTX_RSP("00000001", "6d32");
+ const char *gtp_resp_to_sgsn =
+ MSG_DEL_PDP_CTX_RSP("00000321", "abce");
+
+ /* The response should go back to whichever port the request came from
+ * (unmapped by sequence nr) */
+ LVL2_ASSERT(msg_from_ggsn_c(&resolved_ggsn_addr,
+ &sgsn_sender,
+ gtp_resp_from_ggsn,
+ gtp_resp_to_sgsn));
+
+ LVL2_ASSERT(tunnels_are(""));
+
+ return 1;
+}
+
+static void test_one_pdp_ctx(void)
{
- LOG("test_create_pdp_ctx");
+ LOG("test_one_pdp_ctx");
OSMO_ASSERT(setup_test_hub());
OSMO_ASSERT(create_pdp_ctx());
@@ -982,6 +1048,10 @@ static void test_create_pdp_ctx(void)
"192.168.42.23 (TEI C 321=1 / U 123=2)"
" <-> 192.168.43.34 (TEI C 765=3 / U 567=4)"
" @21945\n"));
+
+ OSMO_ASSERT(delete_pdp_ctx());
+ OSMO_ASSERT(tunnels_are(""));
+
OSMO_ASSERT(clear_test_hub());
}
@@ -1108,7 +1178,7 @@ int main(int argc, char **argv)
test_nr_map_wrap();
test_expiry();
test_echo();
- test_create_pdp_ctx();
+ test_one_pdp_ctx();
test_user_data();
printf("Done\n");
diff --git a/openbsc/tests/gtphub/gtphub_test.ok b/openbsc/tests/gtphub/gtphub_test.ok
index 24b9aad12..8f66b9d59 100644
--- a/openbsc/tests/gtphub/gtphub_test.ok
+++ b/openbsc/tests/gtphub/gtphub_test.ok
@@ -1,5 +1,5 @@
test_echo
-test_create_pdp_ctx
+test_one_pdp_ctx
- __wrap_gtphub_resolve_ggsn_addr():
returning GGSN addr from imsi 240010123456789 ni internet: 192.168.43.34 port 2123
test_user_data