diff options
-rw-r--r-- | include/osmocom/gprs/gprs_ns.h | 9 | ||||
-rw-r--r-- | src/gb/gprs_ns.c | 199 |
2 files changed, 153 insertions, 55 deletions
diff --git a/include/osmocom/gprs/gprs_ns.h b/include/osmocom/gprs/gprs_ns.h index 8ca5a887..c709312a 100644 --- a/include/osmocom/gprs/gprs_ns.h +++ b/include/osmocom/gprs/gprs_ns.h @@ -49,6 +49,15 @@ enum gprs_ns_evt { GPRS_NS_EVT_UNIT_DATA, }; +/*! \brief Osmocom NS VC create status */ +enum gprs_ns_cs { + GPRS_NS_CS_CREATED, /*!< A NSVC object has been created */ + GPRS_NS_CS_FOUND, /*!< A NSVC object has been found */ + GPRS_NS_CS_REJECTED, /*!< Rejected and answered message */ + GPRS_NS_CS_SKIPPED, /*!< Skipped message */ + GPRS_NS_CS_ERROR, /*!< Failed to process message */ +}; + struct gprs_nsvc; /*! \brief Osmocom GPRS callback function type */ typedef int gprs_ns_cb_t(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, diff --git a/src/gb/gprs_ns.c b/src/gb/gprs_ns.c index 5620b3a7..b3bb77de 100644 --- a/src/gb/gprs_ns.c +++ b/src/gb/gprs_ns.c @@ -730,6 +730,13 @@ static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg) return gprs_ns_tx_simple(nsvc, NS_PDUT_BLOCK_ACK); } +int gprs_ns_vc_create(struct gprs_ns_inst *nsi, struct msgb *msg, + struct gprs_nsvc *fallback_nsvc, + struct gprs_nsvc **new_nsvc); + +int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg, + struct gprs_nsvc *nsvc); + /*! \brief Receive incoming NS message from underlying transport layer * \param nsi NS instance to which the data belongs * \param[in] msg message buffer containing newly-received data @@ -743,72 +750,154 @@ static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg) int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, struct sockaddr_in *saddr, enum gprs_ns_ll ll) { - struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; struct gprs_nsvc *nsvc; int rc = 0; /* look up the NSVC based on source address */ nsvc = nsvc_by_rem_addr(nsi, saddr); + if (!nsvc) { - struct tlv_parsed tp; - uint16_t nsei; - if (nsh->pdu_type == NS_PDUT_STATUS) { - LOGP(DNS, LOGL_INFO, "Ignoring NS STATUS from %s:%u " - "for non-existing NS-VC\n", - inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); - return 0; - } - /* Only the RESET procedure creates a new NSVC */ - if (nsh->pdu_type != NS_PDUT_RESET) { - /* Since we have no NSVC, we have to use a fake */ - nsvc = nsi->unknown_nsvc; - log_set_context(GPRS_CTX_NSVC, nsvc); - LOGP(DNS, LOGL_INFO, "Rejecting NS PDU type 0x%0x " - "from %s:%u for non-existing NS-VC\n", - nsh->pdu_type, inet_ntoa(saddr->sin_addr), - ntohs(saddr->sin_port)); - nsvc->nsvci = nsvc->nsei = 0xfffe; - nsvc->ip.bts_addr = *saddr; - nsvc->state = NSE_S_ALIVE; + struct gprs_nsvc *fallback_nsvc; + + fallback_nsvc = nsi->unknown_nsvc; + log_set_context(GPRS_CTX_NSVC, fallback_nsvc); + fallback_nsvc->ip.bts_addr = *saddr; + fallback_nsvc->ll = ll; + + rc = gprs_ns_vc_create(nsi, msg, fallback_nsvc, &nsvc); + + switch (rc) { + case GPRS_NS_CS_CREATED: + case GPRS_NS_CS_FOUND: nsvc->ll = ll; -#if 0 - return gprs_ns_tx_reset(nsvc, NS_CAUSE_PDU_INCOMP_PSTATE); -#else - return gprs_ns_tx_status(nsvc, - NS_CAUSE_PDU_INCOMP_PSTATE, 0, - msg); -#endif + break; + case GPRS_NS_CS_SKIPPED: + case GPRS_NS_CS_REJECTED: + break; + default: + return rc; } - rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, - msgb_l2len(msg) - sizeof(*nsh), 0, 0); + + rc = 0; + } + + if (nsvc) { + nsvc->ip.bts_addr = *saddr; + rc = gprs_ns_process_msg(nsi, msg, nsvc); + } + + return rc; +} + +const char *gprs_ns_format_peer(struct gprs_nsvc *nsvc) +{ + static char buf[80]; + snprintf(buf, sizeof(buf), "%s:%u", + inet_ntoa(nsvc->ip.bts_addr.sin_addr), + ntohs(nsvc->ip.bts_addr.sin_port)); + + return buf; +} + +/*! \brief Create/get NS-VC independently from underlying transport layer + * \param nsi NS instance to which the data belongs + * \param[in] msg message buffer containing newly-received data + * \param[in] fallback_nsvc is used to send error messages back to the peer + * \param[out] new_nsvc contains a pointer to a NS-VC object if one has + * been created or found + * \returns < 0 in case of error, GPRS_NS_CS_SKIPPED if a message has been + * skipped, GPRS_NS_CS_REJECTED if a message has been rejected and + * answered accordingly, GPRS_NS_CS_CREATED if a new NS-VC object + * has been created and registered, and GPRS_NS_CS_FOUND if an + * existing NS-VC object has been found with the same NSEI. + * + * This contains the initial NS automaton state (NS-VC not yet attached). + */ +int gprs_ns_vc_create(struct gprs_ns_inst *nsi, struct msgb *msg, + struct gprs_nsvc *fallback_nsvc, + struct gprs_nsvc **new_nsvc) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h; + struct gprs_nsvc *existing_nsvc; + + struct tlv_parsed tp; + uint16_t nsei; + + int rc; + + if (nsh->pdu_type == NS_PDUT_STATUS) { + LOGP(DNS, LOGL_INFO, "Ignoring NS STATUS from %s " + "for non-existing NS-VC\n", + gprs_ns_format_peer(fallback_nsvc)); + return GPRS_NS_CS_SKIPPED; + } + + /* Only the RESET procedure creates a new NSVC */ + if (nsh->pdu_type != NS_PDUT_RESET) { + /* Since we have no NSVC, we have to use a fake */ + log_set_context(GPRS_CTX_NSVC, fallback_nsvc); + LOGP(DNS, LOGL_INFO, "Rejecting NS PDU type 0x%0x " + "from %s for non-existing NS-VC\n", + nsh->pdu_type, gprs_ns_format_peer(fallback_nsvc)); + fallback_nsvc->nsvci = fallback_nsvc->nsei = 0xfffe; + fallback_nsvc->state = NSE_S_ALIVE; + + rc = gprs_ns_tx_status(fallback_nsvc, + NS_CAUSE_PDU_INCOMP_PSTATE, 0, msg); if (rc < 0) { - LOGP(DNS, LOGL_ERROR, "Rx NS RESET Error %d during " - "TLV Parse\n", rc); + LOGP(DNS, LOGL_ERROR, "TX failed (%d) to peer %s\n", + rc, gprs_ns_format_peer(fallback_nsvc)); return rc; } - if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || - !TLVP_PRESENT(&tp, NS_IE_VCI) || - !TLVP_PRESENT(&tp, NS_IE_NSEI)) { - LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); - gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, - msg); - return -EINVAL; - } - nsei = ntohs(*(uint16_t *)TLVP_VAL(&tp, NS_IE_NSEI)); - /* Check if we already know this NSEI, the remote end might - * simply have changed addresses, or it is a SGSN */ - nsvc = gprs_nsvc_by_nsei(nsi, nsei); - if (!nsvc) { - nsvc = gprs_nsvc_create(nsi, 0xffff); - nsvc->ll = ll; - log_set_context(GPRS_CTX_NSVC, nsvc); - LOGP(DNS, LOGL_INFO, "Creating NS-VC for BSS at %s:%u\n", - inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); - } - /* Update the remote peer IP address/port */ - nsvc->ip.bts_addr = *saddr; - } else - msgb_nsei(msg) = nsvc->nsei; + return GPRS_NS_CS_REJECTED; + } + + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, + msgb_l2len(msg) - sizeof(*nsh), 0, 0); + if (rc < 0) { + LOGP(DNS, LOGL_ERROR, "Rx NS RESET Error %d during " + "TLV Parse\n", rc); + return rc; + } + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || + !TLVP_PRESENT(&tp, NS_IE_VCI) || !TLVP_PRESENT(&tp, NS_IE_NSEI)) { + LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + gprs_ns_tx_status(fallback_nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, + msg); + return -EINVAL; + } + nsei = ntohs(*(uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI)); + /* Check if we already know this NSEI, the remote end might + * simply have changed addresses, or it is a SGSN */ + existing_nsvc = gprs_nsvc_by_nsei(nsi, nsei); + if (!existing_nsvc) { + *new_nsvc = gprs_nsvc_create(nsi, 0xffff); + log_set_context(GPRS_CTX_NSVC, *new_nsvc); + LOGP(DNS, LOGL_INFO, "Creating NS-VC for BSS at %s\n", + gprs_ns_format_peer(fallback_nsvc)); + + return GPRS_NS_CS_CREATED; + } + + *new_nsvc = existing_nsvc; + return GPRS_NS_CS_FOUND; +} + +/*! \brief Process NS message independently from underlying transport layer + * \param nsi NS instance to which the data belongs + * \param[in] msg message buffer containing newly-received data + * \param[in] nsvc refers to the virtual connection + * \returns 0 in case of success, < 0 in case of error + * + * This contains the main NS automaton. + */ +int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg, + struct gprs_nsvc *nsvc) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + int rc = 0; + + msgb_nsei(msg) = nsvc->nsei; log_set_context(GPRS_CTX_NSVC, nsvc); |