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:
parent
edb76f8da5
commit
8adb8441f6
|
@ -388,7 +388,8 @@ struct gtphub_peer_port {
|
||||||
struct gtphub_peer_addr *peer_addr;
|
struct gtphub_peer_addr *peer_addr;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
unsigned int ref_count; /* references from other peers' seq_maps */
|
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 {
|
struct gtphub_tunnel_endpoint {
|
||||||
|
|
|
@ -1362,21 +1362,8 @@ static struct gtphub_tunnel_endpoint *gtphub_unmap_tei(struct gtphub *hub,
|
||||||
return NULL;
|
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,
|
static void gtphub_map_restart_counter(struct gtphub *hub,
|
||||||
struct gtp_packet_desc *p,
|
struct gtp_packet_desc *p)
|
||||||
struct gtphub_peer_port *from,
|
|
||||||
struct gtphub_peer_port *to)
|
|
||||||
{
|
{
|
||||||
if (p->rc != GTP_RC_PDU_C)
|
if (p->rc != GTP_RC_PDU_C)
|
||||||
return;
|
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));
|
p->header_tei_rx, gtphub_port_str(from_port));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
OSMO_ASSERT(*unmapped_from_tun);
|
||||||
|
|
||||||
uint32_t unmapped_tei = to->tei_orig;
|
uint32_t unmapped_tei = to->tei_orig;
|
||||||
set_tei(p, unmapped_tei);
|
set_tei(p, unmapped_tei);
|
||||||
|
|
||||||
LOG(LOGL_DEBUG, "Unmapped TEI coming from %s: %" PRIx32 " -> %" PRIx32 " (to %s)\n",
|
LOG(LOGL_DEBUG, "Unmapped TEI coming from: %s\n",
|
||||||
gtphub_port_str(from_port), p->header_tei_rx, unmapped_tei,
|
gtphub_tunnel_str(*unmapped_from_tun));
|
||||||
gtphub_port_str2(to->peer));
|
|
||||||
|
|
||||||
|
/* May be NULL for an invalidated tunnel. */
|
||||||
*to_port_p = to->peer;
|
*to_port_p = to->peer;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1764,6 +1752,117 @@ static int gtphub_write(const struct osmo_fd *to,
|
||||||
return 0;
|
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)
|
static int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what)
|
||||||
{
|
{
|
||||||
unsigned int plane_idx = from_sgsns_ofd->priv_nr;
|
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_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
|
/* 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
|
* 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);
|
OSMO_ASSERT(pp);
|
||||||
pp->peer_addr = a;
|
pp->peer_addr = a;
|
||||||
pp->port = port;
|
pp->port = port;
|
||||||
|
pp->last_restart_count = -1;
|
||||||
|
|
||||||
if (gsn_addr_to_sockaddr(&a->addr, port, &pp->sa) != 0) {
|
if (gsn_addr_to_sockaddr(&a->addr, port, &pp->sa) != 0) {
|
||||||
talloc_free(pp);
|
talloc_free(pp);
|
||||||
|
|
Loading…
Reference in New Issue