aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/gprs/gb_proxy.c
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2010-05-11 10:01:17 +0200
committerHarald Welte <laforge@gnumonks.org>2010-05-11 10:03:34 +0200
commit0a4050c63b8ff1b80714aee6d6a133de79366eb9 (patch)
tree91750623cbc24f136b30618938917691393c2e60 /openbsc/src/gprs/gb_proxy.c
parentc1c1dd260a02d7679f0e1aff28f319defed950eb (diff)
[gprs] gb_proxy: Send proper BSSGP STATUS msg in error case
In order to reuse the existing bssgp_tx_* functions without pulling in the dependencies of gprs_bssgp.c, we have to move those functions to gprs_bssgp_util.c Furthermore, we can remove gbprox_nsi and replace it with bssgp_nsi, and we can do proper processing of BVC-RESET messages coming from the SGSN on the signalling BVC. In that case we need to send RESET messages to all the BSS.
Diffstat (limited to 'openbsc/src/gprs/gb_proxy.c')
-rw-r--r--openbsc/src/gprs/gb_proxy.c110
1 files changed, 70 insertions, 40 deletions
diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c
index 3c639437a..ff80e92d4 100644
--- a/openbsc/src/gprs/gb_proxy.c
+++ b/openbsc/src/gprs/gb_proxy.c
@@ -55,8 +55,6 @@ struct gbprox_peer {
/* Linked list of all Gb peers (except SGSN) */
static LLIST_HEAD(gbprox_bts_peers);
-extern struct gprs_ns_inst *gbprox_nsi;
-
/* Find the gbprox_peer by its BVCI */
static struct gbprox_peer *peer_by_bvci(uint16_t bvci)
{
@@ -127,30 +125,6 @@ static void strip_ns_hdr(struct msgb *msg)
msgb_pull(msg, strip_len);
}
-/* FIXME: this is copy+paste from gprs_bssgp.c */
-static inline struct msgb *bssgp_msgb_alloc(void)
-{
- return msgb_alloc_headroom(4096, 128, "BSSGP");
-}
-static int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei,
- uint16_t bvci, uint16_t ns_bvci)
-{
- struct msgb *msg = bssgp_msgb_alloc();
- struct bssgp_normal_hdr *bgph =
- (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
- uint16_t _bvci;
-
- msgb_nsei(msg) = nsei;
- msgb_bvci(msg) = ns_bvci;
-
- bgph->pdu_type = pdu_type;
- _bvci = htons(bvci);
- msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
-
- return gprs_ns_sendmsg(gbprox_nsi, msg);
-}
-
-
/* feed a message down the NS-VC associated with the specified peer */
static int gbprox_relay2sgsn(struct msgb *msg, uint16_t ns_bvci)
{
@@ -162,7 +136,7 @@ static int gbprox_relay2sgsn(struct msgb *msg, uint16_t ns_bvci)
strip_ns_hdr(msg);
- return gprs_ns_sendmsg(gbprox_nsi, msg);
+ return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/* feed a message down the NS-VC associated with the specified peer */
@@ -177,7 +151,7 @@ static int gbprox_relay2peer(struct msgb *msg, struct gbprox_peer *peer,
strip_ns_hdr(msg);
- return gprs_ns_sendmsg(gbprox_nsi, msg);
+ return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/* Send a message to a peer identified by ptp_bvci but using ns_bvci
@@ -274,9 +248,13 @@ static int gbprox_rx_sig_from_bss(struct msgb *msg, struct gprs_nsvc *nsvc,
/* Normally, we can simply pass on all signalling messages from BSS to SGSN */
return gbprox_relay2sgsn(msg, ns_bvci);
err_no_peer:
+ LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) cannot find peer based on RAC\n",
+ nsvc->nsei);
+ return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
err_mand_ie:
- /* FIXME: do something */
- ;
+ LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) missing mandatory RA IE\n",
+ nsvc->nsei);
+ return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
/* Receive paging request from SGSN, we need to relay to proper BSS */
@@ -298,6 +276,42 @@ static int gbprox_rx_paging(struct msgb *msg, struct tlv_parsed *tp,
return -EINVAL;
}
+/* Receive an incoming BVC-RESET message from the SGSN */
+static int rx_reset_from_sgsn(struct msgb *msg, struct tlv_parsed *tp,
+ struct gprs_nsvc *nsvc, uint16_t ns_bvci)
+{
+ struct gbprox_peer *peer;
+ uint16_t ptp_bvci;
+
+ if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
+ return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE,
+ NULL, msg);
+ }
+ ptp_bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
+
+ if (ptp_bvci >= 2) {
+ /* A reset for a PTP BVC was received, forward it to its
+ * respective peer */
+ peer = peer_by_bvci(ptp_bvci);
+ if (!peer) {
+ LOGP(DGPRS, LOGL_ERROR, "Cannot find BSS for BVCI %u\n",
+ ptp_bvci);
+ return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI,
+ NULL, msg);
+ }
+ return gbprox_relay2peer(msg, peer, ns_bvci);
+ }
+
+ /* A reset for the Signalling entity has been received
+ * from the SGSN. As the signalling BVCI is shared
+ * among all the BSS's that we multiplex, it needs to
+ * be relayed */
+ llist_for_each_entry(peer, &gbprox_bts_peers, list)
+ gbprox_relay2peer(msg, peer, ns_bvci);
+
+ return 0;
+}
+
/* Receive an incoming signalling message from the SGSN-side NS-VC */
static int gbprox_rx_sig_from_sgsn(struct msgb *msg, struct gprs_nsvc *nsvc,
uint16_t ns_bvci)
@@ -313,6 +327,7 @@ static int gbprox_rx_sig_from_sgsn(struct msgb *msg, struct gprs_nsvc *nsvc,
if (ns_bvci != 0) {
LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BVCI %u is not "
"signalling\n", nsvc->nsei, ns_bvci);
+ /* FIXME: Send proper error message */
return -EINVAL;
}
@@ -322,16 +337,18 @@ static int gbprox_rx_sig_from_sgsn(struct msgb *msg, struct gprs_nsvc *nsvc,
pdu_type == BSSGP_PDUT_DL_UNITDATA) {
LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) UNITDATA not allowed in "
"signalling\n", nsvc->nsei);
- return -EINVAL;
+ return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
}
rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
switch (pdu_type) {
+ case BSSGP_PDUT_BVC_RESET:
+ 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:
case BSSGP_PDUT_BVC_RESET_ACK:
/* simple case: BVCI IE is mandatory */
if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI))
@@ -345,9 +362,22 @@ static int gbprox_rx_sig_from_sgsn(struct msgb *msg, struct gprs_nsvc *nsvc,
rc = gbprox_rx_paging(msg, &tp, nsvc, ns_bvci);
break;
case BSSGP_PDUT_STATUS:
- /* FIXME: Some exception has occurred */
+ /* Some exception has occurred */
LOGP(DGPRS, LOGL_NOTICE,
- "NSEI=%u(SGSN) STATUS not implemented yet\n", nsvc->nsei);
+ "NSEI=%u(SGSN) STATUS ", nsvc->nsei);
+ if (!TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) {
+ LOGPC(DGPRS, LOGL_NOTICE, "\n");
+ goto err_mand_ie;
+ }
+ LOGPC(DGPRS, LOGL_NOTICE,
+ "cause=0x%02x(%s) ", *TLVP_VAL(&tp, BSSGP_IE_CAUSE),
+ bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE)));
+ if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) {
+ uint16_t *bvci = TLVP_VAL(&tp, BSSGP_IE_BVCI);
+ LOGPC(DGPRS, LOGL_NOTICE,
+ "BVCI=%u\n", ntohs(*bvci));
+ } else
+ LOGPC(DGPRS, LOGL_NOTICE, "\n");
break;
/* those only exist in the SGSN -> BSS direction */
case BSSGP_PDUT_SUSPEND_ACK:
@@ -365,9 +395,11 @@ static int gbprox_rx_sig_from_sgsn(struct msgb *msg, struct gprs_nsvc *nsvc,
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
LOGP(DGPRS, LOGL_ERROR,
"NSEI=%u(SGSN) INVOKE TRACE not supported\n", nsvc->nsei);
+ rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
break;
default:
DEBUGP(DGPRS, "BSSGP PDU type 0x%02x unknown\n", pdu_type);
+ rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
break;
}
@@ -375,13 +407,11 @@ static int gbprox_rx_sig_from_sgsn(struct msgb *msg, struct gprs_nsvc *nsvc,
err_mand_ie:
LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) missing mandatory IE\n",
nsvc->nsei);
- /* FIXME: this would pull gprs_bssgp.c in, which in turn has dependencies */
- //return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
- return;
+ return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
err_no_peer:
- LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) cannot find peer based on RAC\n");
- /* FIXME */
- return;
+ LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) cannot find peer based on RAC\n",
+ nsvc->nsei);
+ return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
}
/* Main input function for Gb proxy */