gtphub: monitor GSNs' restart counters.

If a GSN indicates that it has reset, tear down each known tunnel for that GSN
individually (don't send the GSNs on the other side a different restart
counter, because they represent more than just this GSN).

Sponsored-by: On-Waves ehi
This commit is contained in:
Neels Hofmeyr 2015-12-01 15:20:18 +01:00
parent 3fdba2ed5a
commit bc44330671
2 changed files with 120 additions and 19 deletions

View File

@ -388,7 +388,8 @@ struct gtphub_peer_port {
struct gtphub_peer_addr *peer_addr;
uint16_t port;
unsigned int ref_count; /* references from other peers' seq_maps */
struct osmo_sockaddr sa;
struct osmo_sockaddr sa; /* a "cache" for (peer_addr->addr, port) */
int last_restart_count; /* 0..255 = valid, all else means unknown */
};
struct gtphub_tunnel_endpoint {

View File

@ -1362,21 +1362,8 @@ static struct gtphub_tunnel_endpoint *gtphub_unmap_tei(struct gtphub *hub,
return NULL;
}
static void gtphub_check_restart_counter(struct gtphub *hub,
struct gtp_packet_desc *p,
struct gtphub_peer_port *from)
{
/* TODO */
/* 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. */
}
static void gtphub_map_restart_counter(struct gtphub *hub,
struct gtp_packet_desc *p,
struct gtphub_peer_port *from,
struct gtphub_peer_port *to)
struct gtp_packet_desc *p)
{
if (p->rc != GTP_RC_PDU_C)
return;
@ -1416,14 +1403,15 @@ static int gtphub_unmap_header_tei(struct gtphub_peer_port **to_port_p,
p->header_tei_rx, gtphub_port_str(from_port));
return -1;
}
OSMO_ASSERT(*unmapped_from_tun);
uint32_t unmapped_tei = to->tei_orig;
set_tei(p, unmapped_tei);
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));
LOG(LOGL_DEBUG, "Unmapped TEI coming from: %s\n",
gtphub_tunnel_str(*unmapped_from_tun));
/* May be NULL for an invalidated tunnel. */
*to_port_p = to->peer;
return 0;
@ -1764,6 +1752,117 @@ static int gtphub_write(const struct osmo_fd *to,
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)
{
struct gtphub_tunnel *tun;
llist_for_each_entry(tun, &hub->tunnels, entry) {
int side_idx;
for_each_side(side_idx) {
if (pp != tun->endpoint[side_idx][GTPH_PLANE_CTRL].peer)
continue;
/* 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));
struct pending_delete *pd;
pd = pending_delete_new();
pd->tun = tun;
pd->teardown_ind = 0xff;
pd->nsapi = 0;
llist_add(&pd->entry, &hub->pending_deletes);
expiry_add(&hub->expire_quickly, &pd->expiry_entry, p->timestamp);
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);
}
}
}
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;
@ -2094,7 +2193,7 @@ int gtphub_handle_buf(struct gtphub *hub,
}
gtphub_check_restart_counter(hub, &p, from_peer);
gtphub_map_restart_counter(hub, &p, from_peer, to_peer);
gtphub_map_restart_counter(hub, &p);
/* 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
@ -2497,6 +2596,7 @@ static struct gtphub_peer_port *gtphub_addr_add_port(struct gtphub_peer_addr *a,
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);