aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/gprs/gtphub.c
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2015-12-01 00:23:45 +0100
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2015-12-03 11:44:19 +0100
commit10fc02447279a322e1d362a31dec5c8f8bafb6f8 (patch)
treeb2df2703d14a27c04058253907335d0d828b13e4 /openbsc/src/gprs/gtphub.c
parent2f67125c3237f63800c5601f1aaddda62ce192a2 (diff)
gtphub: handle Delete PDP Context.
During resolution of the header TEI, also return the tunnel struct that resolved the TEI, so the Delete PDP Ctx code does not need to look it up again. Upon Delete PDP Ctx Request, remember the IEs and that a request was made. Upon Delete PDP Ctx Response, find the pending delete and remove the corresponding tunnel, iff the response indicates success. Add a context deletion to regression tests, rename the test appropriately. Sponsored-by: On-Waves ehi
Diffstat (limited to 'openbsc/src/gprs/gtphub.c')
-rw-r--r--openbsc/src/gprs/gtphub.c169
1 files changed, 154 insertions, 15 deletions
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);