[gb_proxy] Track the state of blocked/unblocked BVC in the proxy
This allows us to reject any additional messages sent by the SGSN
after the BVC was blocked (+ acknowledged to be blocked)
diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c
index 934ceef..19f6cbf 100644
--- a/openbsc/src/gprs/gb_proxy.c
+++ b/openbsc/src/gprs/gb_proxy.c
@@ -47,6 +47,7 @@
/* BVCI used for Point-to-Point to this peer */
uint16_t bvci;
+ int blocked;
/* Routeing Area that this peer is part of (raw 04.08 encoding) */
uint8_t ra[6];
@@ -202,6 +203,30 @@
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
+static int block_unblock_peer(uint16_t ptp_bvci, uint8_t pdu_type)
+{
+ struct gbprox_peer *peer;
+
+ peer = peer_by_bvci(ptp_bvci);
+ if (!peer) {
+ LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n",
+ ptp_bvci);
+ return -ENOENT;
+ }
+
+ switch (pdu_type) {
+ case BSSGP_PDUT_BVC_BLOCK_ACK:
+ peer->blocked = 1;
+ break;
+ case BSSGP_PDUT_BVC_UNBLOCK_ACK:
+ peer->blocked = 0;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
/* Send a message to a peer identified by ptp_bvci but using ns_bvci
* in the NS hdr */
static int gbprox_relay2bvci(struct msgb *msg, uint16_t ptp_bvci,
@@ -428,8 +453,6 @@
rc = rx_reset_from_sgsn(msg, &tp, nsvc, ns_bvci);
break;
case BSSGP_PDUT_FLUSH_LL:
- case BSSGP_PDUT_BVC_BLOCK_ACK:
- case BSSGP_PDUT_BVC_UNBLOCK_ACK:
case BSSGP_PDUT_BVC_RESET_ACK:
/* simple case: BVCI IE is mandatory */
if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI))
@@ -474,6 +497,22 @@
goto err_no_peer;
rc = gbprox_relay2peer(msg, peer, ns_bvci);
break;
+ case BSSGP_PDUT_BVC_BLOCK_ACK:
+ case BSSGP_PDUT_BVC_UNBLOCK_ACK:
+ if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI))
+ goto err_mand_ie;
+ bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI));
+ if (bvci == 0) {
+ LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BSSGP "
+ "%sBLOCK_ACK for signalling BVCI ?!?\n", nsvc->nsei,
+ pdu_type == BSSGP_PDUT_BVC_UNBLOCK_ACK ? "UN":"");
+ /* should we send STATUS ? */
+ } else {
+ /* Mark BVC as (un)blocked */
+ block_unblock_peer(bvci, pdu_type);
+ }
+ rc = gbprox_relay2bvci(msg, bvci, ns_bvci);
+ break;
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
LOGP(DGPRS, LOGL_ERROR,
"NSEI=%u(SGSN) BSSGP INVOKE TRACE not supported\n",nsvc->nsei);
@@ -501,6 +540,7 @@
int gbprox_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci)
{
int rc;
+ struct gbprox_peer *peer;
/* Only BVCI=0 messages need special treatment */
if (ns_bvci == 0 || ns_bvci == 1) {
@@ -511,18 +551,24 @@
} else {
/* All other BVCI are PTP and thus can be simply forwarded */
if (!nsvc->remote_end_is_sgsn) {
- rc = gbprox_relay2sgsn(msg, ns_bvci);
- } else {
- struct gbprox_peer *peer = peer_by_bvci(ns_bvci);
- if (!peer) {
- LOGP(DGPRS, LOGL_INFO, "Allocationg new peer for "
- "BVCI=%u via NSVC=%u/NSEI=%u\n", ns_bvci,
- nsvc->nsvci, nsvc->nsei);
- peer = peer_alloc(ns_bvci);
- peer->nsvc = nsvc;
- }
- rc = gbprox_relay2peer(msg, peer, ns_bvci);
+ return gbprox_relay2sgsn(msg, ns_bvci);
}
+ /* else: SGSN -> BSS direction */
+ peer = peer_by_bvci(ns_bvci);
+ if (!peer) {
+ LOGP(DGPRS, LOGL_INFO, "Allocationg new peer for "
+ "BVCI=%u via NSVC=%u/NSEI=%u\n", ns_bvci,
+ nsvc->nsvci, nsvc->nsei);
+ peer = peer_alloc(ns_bvci);
+ peer->nsvc = nsvc;
+ }
+ if (peer->blocked) {
+ LOGP(DGPRS, LOGL_NOTICE, "Dropping PDU for "
+ "blocked BVCI=%u via NSVC=%u/NSEI=%u\n",
+ ns_bvci, nsvc->nsvci, nsvc->nsei);
+ return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, NULL, msg);
+ }
+ rc = gbprox_relay2peer(msg, peer, ns_bvci);
}
return rc;