-- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 From 9ba500559a1609fa4bd0a7a7ea7f4d73c65d9440 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 14 Mar 2010 15:45:01 +0800 Subject: Create new 'gprs-sgsn' branch on top of 'gprs-conf' This branch contains the partial SGSN/GGSN implementation that was originally developed as part of the gprs branch. --- openbsc/include/openbsc/gprs_bssgp.h | 138 ++++++++++++ openbsc/include/openbsc/gprs_ns.h | 61 ++++++ openbsc/src/gprs_bssgp.c | 397 +++++++++++++++++++++++++++++++++++ openbsc/src/gprs_ns.c | 348 ++++++++++++++++++++++++++++++ 4 files changed, 944 insertions(+) create mode 100644 openbsc/include/openbsc/gprs_bssgp.h create mode 100644 openbsc/include/openbsc/gprs_ns.h create mode 100644 openbsc/src/gprs_bssgp.c create mode 100644 openbsc/src/gprs_ns.c diff --git a/openbsc/include/openbsc/gprs_bssgp.h b/openbsc/include/openbsc/gprs_bssgp.h new file mode 100644 index 00000000..f85ac48e --- /dev/null +++ b/openbsc/include/openbsc/gprs_bssgp.h @@ -0,0 +1,138 @@ +#ifndef _GPRS_BSSGP_H +#define _GPRS_BSSGP_H + +/* Section 11.3.26 / Table 11.27 */ +enum bssgp_pdu_type { + /* PDUs between RL and BSSGP SAPs */ + BSSGP_PDUT_DL_UNITDATA = 0x00, + BSSGP_PDUT_UL_UNITDATA = 0x01, + BSSGP_PDUT_RA_CAPABILITY = 0x02, + BSSGP_PDUT_PTM_UNITDATA = 0x03, + /* PDUs between GMM SAPs */ + BSSGP_PDUT_PAGING_PS = 0x06, + BSSGP_PDUT_PAGING_CS = 0x07, + BSSGP_PDUT_RA_CAPA_UDPATE = 0x08, + BSSGP_PDUT_RA_CAPA_UPDATE_ACK = 0x09, + BSSGP_PDUT_RADIO_STATUS = 0x0a, + BSSGP_PDUT_SUSPEND = 0x0b, + BSSGP_PDUT_SUSPEND_ACK = 0x0c, + BSSGP_PDUT_SUSPEND_NACK = 0x0d, + BSSGP_PDUT_RESUME = 0x0e, + BSSGP_PDUT_RESUME_ACK = 0x0f, + BSSGP_PDUT_RESUME_NACK = 0x10, + /* PDus between NM SAPs */ + BSSGP_PDUT_BVC_BLOCK = 0x20, + BSSGP_PDUT_BVC_BLOCK_ACK = 0x21, + BSSGP_PDUT_BVC_RESET = 0x22, + BSSGP_PDUT_BVC_RESET_ACK = 0x23, + BSSGP_PDUT_BVC_UNBLOCK = 0x24, + BSSGP_PDUT_BVC_UNBLOCK_ACK = 0x25, + BSSGP_PDUT_FLOW_CONTROL_BVC = 0x26, + BSSGP_PDUT_FLOW_CONTROL_BVC_ACK = 0x27, + BSSGP_PDUT_FLOW_CONTROL_MS = 0x28, + BSSGP_PDUT_FLOW_CONTROL_MS_ACK = 0x29, + BSSGP_PDUT_FLUSH_LL = 0x2a, + BSSGP_PDUT_FLUSH_LL_ACK = 0x2b, + BSSGP_PDUT_LLC_DISCARD = 0x2c, + BSSGP_PDUT_SGSN_INVOKE_TRACE = 0x40, + BSSGP_PDUT_STATUS = 0x41, + /* PDUs between PFM SAP's */ + BSSGP_PDUT_DOWNLOAD_BSS_PFC = 0x50, + BSSGP_PDUT_CREATE_BSS_PFC = 0x51, + BSSGP_PDUT_CREATE_BSS_PFC_ACK = 0x52, + BSSGP_PDUT_CREATE_BSS_PFC_NACK = 0x53, + BSSGP_PDUT_MODIFY_BSS_PFC = 0x54, + BSSGP_PDUT_MODIFY_BSS_PFC_ACK = 0x55, + BSSGP_PDUT_DELETE_BSS_PFC = 0x56, + BSSGP_PDUT_DELETE_BSS_PFC_ACK = 0x57, +}; + +/* Section 10.2.1 and 10.2.2 */ +struct bssgp_ud_hdr { + u_int8_t pdu_type; + u_int32_t tlli; + u_int8_t qos_profile[3]; + u_int8_t data[0]; /* TLV's */ +} __attribute__((packed)); + +struct bssgp_normal_hdr { + u_int8_t pdu_type; + u_int8_t data[0]; /* TLV's */ +}; + +enum bssgp_iei_type { + BSSGP_IE_ALIGNMENT = 0x00, + BSSGP_IE_BMAX_DEFAULT_MS = 0x01, + BSSGP_IE_BSS_AREA_ID = 0x02, + BSSGP_IE_BUCKET_LEAK_RATE = 0x03, + BSSGP_IE_BVCI = 0x04, + BSSGP_IE_BVC_BUCKET_SIZE = 0x05, + BSSGP_IE_BVC_MEASUREMENT = 0x06, + BSSGP_IE_CAUSE = 0x07, + BSSGP_IE_CELL_ID = 0x08, + BSSGP_IE_CHAN_NEEDED = 0x09, + BSSGP_IE_DRX_PARAMS = 0x0a, + BSSGP_IE_EMLPP_PRIO = 0x0b, + BSSGP_IE_FLUSH_ACTION = 0x0c, + BSSGP_IE_IMSI = 0x0d, + BSSGP_IE_LLC_PDU = 0x0e, + BSSGP_IE_LLC_FRAMES_DISCARDED = 0x0f, + BSSGP_IE_LOCATION_AREA = 0x10, + BSSGP_IE_MOBILE_ID = 0x11, + BSSGP_IE_MS_BUCKET_SIZE = 0x12, + BSSGP_IE_MS_RADIO_ACCESS_CAP = 0x13, + BSSGP_IE_OMC_ID = 0x14, + BSSGP_IE_PDU_IN_ERROR = 0x15, + BSSGP_IE_PDU_LIFETIME = 0x16, + BSSGP_IE_PRIORITY = 0x17, + BSSGP_IE_QOS_PROFILE = 0x18, + BSSGP_IE_RADIO_CAUSE = 0x19, + BSSGP_IE_RA_CAP_UPD_CAUSE = 0x1a, + BSSGP_IE_ROUTEING_AREA = 0x1b, + BSSGP_IE_R_DEFAULT_MS = 0x1c, + BSSGP_IE_SUSPEND_REF_NR = 0x1d, + BSSGP_IE_TAG = 0x1e, + BSSGP_IE_TLLI = 0x1f, + BSSGP_IE_TMSI = 0x20, + BSSGP_IE_TRACE_REFERENC = 0x21, + BSSGP_IE_TRACE_TYPE = 0x22, + BSSGP_IE_TRANSACTION_ID = 0x23, + BSSGP_IE_TRIGGER_ID = 0x24, + BSSGP_IE_NUM_OCT_AFF = 0x25, + BSSGP_IE_LSA_ID_LIST = 0x26, + BSSGP_IE_LSA_INFORMATION = 0x27, + BSSGP_IE_PACKET_FLOW_ID = 0x28, + BSSGP_IE_PACKET_FLOW_TIMER = 0x29, + BSSGP_IE_AGG_BSS_QOS_PROFILE = 0x3a, + BSSGP_IE_FEATURE_BITMAP = 0x3b, + BSSGP_IE_BUCKET_FULL_RATIO = 0x3c, + BSSGP_IE_SERVICE_UTRAN_CCO = 0x3d, +}; + +/* Section 11.3.8 / Table 11.10: Cause coding */ +enum gprs_bssgp_cause { + BSSGP_CAUSE_PROC_OVERLOAD = 0x00, + BSSGP_CAUSE_EQUIP_FAIL = 0x01, + BSSGP_CAUSE_TRASIT_NET_FAIL = 0x02, + BSSGP_CAUSE_CAPA_GREATER_0KPBS = 0x03, + BSSGP_CAUSE_UNKNOWN_MS = 0x04, + BSSGP_CAUSE_UNKNOWN_BVCI = 0x05, + BSSGP_CAUSE_CELL_TRAF_CONG = 0x06, + BSSGP_CAUSE_SGSN_CONG = 0x07, + BSSGP_CAUSE_OML_INTERV = 0x08, + BSSGP_CAUSE_BVCI_BLOCKED = 0x09, + BSSGP_CAUSE_PFC_CREATE_FAIL = 0x0a, + BSSGP_CAUSE_SEM_INCORR_PDU = 0x20, + BSSGP_CAUSE_INV_MAND_INF = 0x21, + BSSGP_CAUSE_MISSING_MAND_IE = 0x22, + BSSGP_CAUSE_MISSING_COND_IE = 0x23, + BSSGP_CAUSE_UNEXP_COND_IE = 0x24, + BSSGP_CAUSE_COND_IE_ERR = 0x25, + BSSGP_CAUSE_PDU_INCOMP_STATE = 0x26, + BSSGP_CAUSE_PROTO_ERR_UNSPEC = 0x27, + BSSGP_CAUSE_PDU_INCOMP_FEAT = 0x28, +}; + +extern int gprs_bssgp_rcvmsg(struct msgb *msg, u_int16_t bvci); + +#endif /* _GPRS_BSSGP_H */ diff --git a/openbsc/include/openbsc/gprs_ns.h b/openbsc/include/openbsc/gprs_ns.h new file mode 100644 index 00000000..90f1adf3 --- /dev/null +++ b/openbsc/include/openbsc/gprs_ns.h @@ -0,0 +1,61 @@ +#ifndef _GPRS_NS_H +#define _GPRS_NS_H + +struct gprs_ns_hdr { + u_int8_t pdu_type; + u_int8_t data[0]; +} __attribute__((packed)); + +/* TS 08.16, Section 10.3.7, Table 14 */ +enum ns_pdu_type { + NS_PDUT_UNITDATA = 0x00, + NS_PDUT_RESET = 0x02, + NS_PDUT_RESET_ACK = 0x03, + NS_PDUT_BLOCK = 0x04, + NS_PDUT_BLOCK_ACK = 0x05, + NS_PDUT_UNBLOCK = 0x06, + NS_PDUT_UNBLOCK_ACK = 0x07, + NS_PDUT_STATUS = 0x08, + NS_PDUT_ALIVE = 0x0a, + NS_PDUT_ALIVE_ACK = 0x0b, +}; + +/* TS 08.16, Section 10.3, Table 12 */ +enum ns_ctrl_ie { + NS_IE_CAUSE = 0x00, + NS_IE_VCI = 0x01, + NS_IE_PDU = 0x02, + NS_IE_BVCI = 0x03, + NS_IE_NSEI = 0x04, +}; + +/* TS 08.16, Section 10.3.2, Table 13 */ +enum ns_cause { + NS_CAUSE_TRANSIT_FAIL = 0x00, + NS_CAUSE_OM_INTERVENTION = 0x01, + NS_CAUSE_EQUIP_FAIL = 0x02, + NS_CAUSE_NSVC_BLOCKED = 0x03, + NS_CAUSE_NSVC_UNKNOWN = 0x04, + NS_CAUSE_BVCI_UNKNOWN = 0x05, + NS_CAUSE_SEM_INCORR_PDU = 0x08, + NS_CAUSE_PDU_INCOMP_PSTATE = 0x0a, + NS_CAUSE_PROTO_ERR_UNSPEC = 0x0b, + NS_CAUSE_INVAL_ESSENT_IE = 0x0c, + NS_CAUSE_MISSING_ESSENT_IE = 0x0d, +}; + +/* a layer 1 entity transporting NS frames */ +struct gprs_ns_link { + union { + struct { + int fd; + } ip; + }; +}; + + +int gprs_ns_rcvmsg(struct msgb *msg); + +int gprs_ns_sendmsg(struct gprs_ns_link *link, u_int16_t bvci, + struct msgb *msg); +#endif diff --git a/openbsc/src/gprs_bssgp.c b/openbsc/src/gprs_bssgp.c new file mode 100644 index 00000000..de57d25d --- /dev/null +++ b/openbsc/src/gprs_bssgp.c @@ -0,0 +1,397 @@ +/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* global pointer to the gsm network data structure */ +/* FIXME: this must go! */ +extern struct gsm_network *bsc_gsmnet; + +/* Chapter 11.3.9 / Table 11.10: Cause coding */ +static const char *bssgp_cause_strings[] = { + [BSSGP_CAUSE_PROC_OVERLOAD] = "Processor overload", + [BSSGP_CAUSE_EQUIP_FAIL] = "Equipment Failure", + [BSSGP_CAUSE_TRASIT_NET_FAIL] = "Transit netowkr service failure", + [BSSGP_CAUSE_CAPA_GREATER_0KPBS]= "Transmission capacity modified", + [BSSGP_CAUSE_UNKNOWN_MS] = "Unknown MS", + [BSSGP_CAUSE_UNKNOWN_BVCI] = "Unknown BVCI", + [BSSGP_CAUSE_CELL_TRAF_CONG] = "Cell traffic congestion", + [BSSGP_CAUSE_SGSN_CONG] = "SGSN congestion", + [BSSGP_CAUSE_OML_INTERV] = "O&M intervention", + [BSSGP_CAUSE_BVCI_BLOCKED] = "BVCI blocked", + [BSSGP_CAUSE_PFC_CREATE_FAIL] = "PFC create failure", + [BSSGP_CAUSE_SEM_INCORR_PDU] = "Semantically incorrect PDU", + [BSSGP_CAUSE_INV_MAND_INF] = "Invalid mandatory information", + [BSSGP_CAUSE_MISSING_MAND_IE] = "Missing mandatory IE", + [BSSGP_CAUSE_MISSING_COND_IE] = "Missing conditional IE", + [BSSGP_CAUSE_UNEXP_COND_IE] = "Unexpected conditional IE", + [BSSGP_CAUSE_COND_IE_ERR] = "Conditional IE error", + [BSSGP_CAUSE_PDU_INCOMP_STATE] = "PDU incompatible with protocol state", + [BSSGP_CAUSE_PROTO_ERR_UNSPEC] = "Protocol error - unspecified", + [BSSGP_CAUSE_PDU_INCOMP_FEAT] = "PDU not compatible with feature set", +}; + +static const char *bssgp_cause_str(enum gprs_bssgp_cause cause) +{ + if (cause >= ARRAY_SIZE(bssgp_cause_strings)) + return "undefined"; + + if (bssgp_cause_strings[cause]) + return bssgp_cause_strings[cause]; + + return "undefined"; +} + +static inline int bssgp_tlv_parse(struct tlv_parsed *tp, u_int8_t *buf, int len) +{ + return tlv_parse(tp, &tvlv_att_def, buf, len, 0, 0); +} + +static inline struct msgb *bssgp_msgb_alloc(void) +{ + return msgb_alloc_headroom(4096, 128, "BSSGP"); +} + +/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */ +static int bssgp_tx_simple_bvci(u_int8_t pdu_type, u_int16_t bvci, u_int16_t ns_bvci) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + u_int16_t _bvci; + + bgph->pdu_type = pdu_type; + _bvci = htons(bvci); + msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (u_int8_t *) &_bvci); + + return gprs_ns_sendmsg(NULL, ns_bvci, msg); +} + +/* Chapter 10.4.5: Flow Control BVC ACK */ +static int bssgp_tx_fc_bvc_ack(u_int8_t tag, u_int16_t ns_bvci) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + + bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK; + msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag); + + return gprs_ns_sendmsg(NULL, ns_bvci, msg); +} + +/* Chapter 10.4.14: Status */ +static int bssgp_tx_status(u_int8_t cause, u_int16_t *bvci, struct msgb *orig_msg) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + + DEBUGPC(DGPRS, "BSSGP: TX STATUS, cause=%s\n", bssgp_cause_str(cause)); + + bgph->pdu_type = BSSGP_PDUT_STATUS; + msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause); + if (bvci) { + u_int16_t _bvci = htons(*bvci); + msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (u_int8_t *) &_bvci); + } + if (orig_msg) + msgb_tvlv_put(msg, BSSGP_IE_PDU_IN_ERROR, + msgb_l3len(orig_msg), orig_msg->l3h); + + return gprs_ns_sendmsg(NULL, 0, msg); +} + +/* Uplink unit-data */ +static int bssgp_rx_ul_ud(struct msgb *msg, u_int16_t bvci) +{ + struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msg->l3h; + struct gsm_bts *bts; + int data_len = msgb_l3len(msg) - sizeof(*budh); + struct tlv_parsed tp; + int rc; + + DEBUGP(DGPRS, "BSSGP UL-UD\n"); + + msg->tlli = ntohl(budh->tlli); + rc = bssgp_tlv_parse(&tp, budh->data, data_len); + + /* Cell ID and LLC_PDU are the only mandatory IE */ + if (!TLVP_PRESENT(&tp, BSSGP_IE_CELL_ID) || + !TLVP_PRESENT(&tp, BSSGP_IE_LLC_PDU)) + return -EIO; + + /* Determine the BTS based on the Cell ID */ + bts = gsm48_bts_by_ra_id(bsc_gsmnet, + TLVP_VAL(&tp, BSSGP_IE_CELL_ID), + TLVP_LEN(&tp, BSSGP_IE_CELL_ID)); + if (bts) + msg->trx = bts->c0; + + msg->llch = TLVP_VAL(&tp, BSSGP_IE_LLC_PDU); + + return gprs_llc_rcvmsg(msg, &tp); +} + +static int bssgp_rx_suspend(struct msgb *msg, u_int16_t bvci) +{ + struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msg->l3h; + int data_len = msgb_l3len(msg) - sizeof(*bgph); + struct tlv_parsed tp; + int rc; + + DEBUGP(DGPRS, "BSSGP SUSPEND\n"); + + rc = bssgp_tlv_parse(&tp, bgph->data, data_len); + if (rc < 0) + return rc; + + if (!TLVP_PRESENT(&tp, BSSGP_IE_TLLI) || + !TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) + return -EIO; + + /* SEND SUSPEND_ACK or SUSPEND_NACK */ + /* FIXME */ +} + +static int bssgp_rx_resume(struct msgb *msg, u_int16_t bvci) +{ + struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msg->l3h; + int data_len = msgb_l3len(msg) - sizeof(*bgph); + struct tlv_parsed tp; + int rc; + + DEBUGP(DGPRS, "BSSGP RESUME\n"); + + rc = bssgp_tlv_parse(&tp, bgph->data, data_len); + if (rc < 0) + return rc; + + if (!TLVP_PRESENT(&tp, BSSGP_IE_TLLI) || + !TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA) || + !TLVP_PRESENT(&tp, BSSGP_IE_SUSPEND_REF_NR)) + return -EIO; + + /* SEND RESUME_ACK or RESUME_NACK */ + /* FIXME */ +} + +static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp, + u_int16_t ns_bvci) +{ + + DEBUGP(DGPRS, "BSSGP FC BVC\n"); + + if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) || + !TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) || + !TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) || + !TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) || + !TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS)) + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); + + /* Send FLOW_CONTROL_BVC_ACK */ + return bssgp_tx_fc_bvc_ack(*TLVP_VAL(tp, BSSGP_IE_TAG), ns_bvci); +} +/* We expect msg->l3h to point to the BSSGP header */ +int gprs_bssgp_rcvmsg(struct msgb *msg, u_int16_t ns_bvci) +{ + struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msg->l3h; + struct tlv_parsed tp; + u_int8_t pdu_type = bgph->pdu_type; + int data_len = msgb_l3len(msg) - sizeof(*bgph); + u_int16_t bvci; + int rc = 0; + + if (pdu_type != BSSGP_PDUT_UL_UNITDATA && + pdu_type != BSSGP_PDUT_DL_UNITDATA) + rc = bssgp_tlv_parse(&tp, bgph->data, data_len); + + switch (pdu_type) { + case BSSGP_PDUT_UL_UNITDATA: + /* some LLC data from the MS */ + rc = bssgp_rx_ul_ud(msg, ns_bvci); + break; + case BSSGP_PDUT_RA_CAPABILITY: + /* BSS requests RA capability or IMSI */ + DEBUGP(DGPRS, "BSSGP RA CAPABILITY UPDATE\n"); + /* FIXME: send RA_CAPA_UPDATE_ACK */ + break; + case BSSGP_PDUT_RADIO_STATUS: + DEBUGP(DGPRS, "BSSGP RADIO STATUS\n"); + /* BSS informs us of some exception */ + break; + case BSSGP_PDUT_SUSPEND: + /* MS wants to suspend */ + rc = bssgp_rx_suspend(msg, ns_bvci); + break; + case BSSGP_PDUT_RESUME: + /* MS wants to resume */ + rc = bssgp_rx_resume(msg, ns_bvci); + break; + case BSSGP_PDUT_FLUSH_LL: + /* BSS informs MS has moved to one cell to other cell */ + DEBUGP(DGPRS, "BSSGP FLUSH LL\n"); + /* Send FLUSH_LL_ACK */ + break; + case BSSGP_PDUT_LLC_DISCARD: + /* BSS informs that some LLC PDU's have been discarded */ + DEBUGP(DGPRS, "BSSGP LLC DISCARDED\n"); + break; + case BSSGP_PDUT_FLOW_CONTROL_BVC: + /* BSS informs us of available bandwidth in Gb interface */ + rc = bssgp_rx_fc_bvc(msg, &tp, ns_bvci); + break; + case BSSGP_PDUT_FLOW_CONTROL_MS: + /* BSS informs us of available bandwidth to one MS */ + DEBUGP(DGPRS, "BSSGP FC MS\n"); + /* Send FLOW_CONTROL_MS_ACK */ + break; + case BSSGP_PDUT_BVC_BLOCK: + /* BSS tells us that BVC shall be blocked */ + DEBUGP(DGPRS, "BSSGP BVC BLOCK "); + if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI) || + !TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) + goto err_mand_ie; + bvci = ntohs(*(u_int16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI)); + DEBUGPC(DGPRS, "BVCI=%u, cause=%s\n", bvci, + bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE))); + rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, + bvci, ns_bvci); + break; + case BSSGP_PDUT_BVC_UNBLOCK: + /* BSS tells us that BVC shall be unblocked */ + DEBUGP(DGPRS, "BSSGP BVC UNBLOCK "); + if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) + goto err_mand_ie; + bvci = ntohs(*(u_int16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI)); + DEBUGPC(DGPRS, "BVCI=%u\n", bvci); + rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, + bvci, ns_bvci); + break; + case BSSGP_PDUT_BVC_RESET: + /* BSS tells us that BVC init is required */ + DEBUGP(DGPRS, "BSSGP BVC RESET "); + if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI) || + !TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) + goto err_mand_ie; + bvci = ntohs(*(u_int16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI)); + DEBUGPC(DGPRS, "BVCI=%u, cause=%s\n", bvci, + bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE))); + rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, + bvci, ns_bvci); + break; + case BSSGP_PDUT_STATUS: + /* Some exception has occurred */ + case BSSGP_PDUT_DOWNLOAD_BSS_PFC: + case BSSGP_PDUT_CREATE_BSS_PFC_ACK: + case BSSGP_PDUT_CREATE_BSS_PFC_NACK: + case BSSGP_PDUT_MODIFY_BSS_PFC: + case BSSGP_PDUT_DELETE_BSS_PFC_ACK: + DEBUGP(DGPRS, "BSSGP PDU type 0x%02x not [yet] implemented\n", + pdu_type); + break; + /* those only exist in the SGSN -> BSS direction */ + case BSSGP_PDUT_DL_UNITDATA: + case BSSGP_PDUT_PAGING_PS: + case BSSGP_PDUT_PAGING_CS: + case BSSGP_PDUT_RA_CAPA_UPDATE_ACK: + case BSSGP_PDUT_SUSPEND_ACK: + case BSSGP_PDUT_SUSPEND_NACK: + case BSSGP_PDUT_RESUME_ACK: + case BSSGP_PDUT_RESUME_NACK: + case BSSGP_PDUT_FLUSH_LL_ACK: + case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK: + case BSSGP_PDUT_FLOW_CONTROL_MS_ACK: + case BSSGP_PDUT_BVC_BLOCK_ACK: + case BSSGP_PDUT_BVC_UNBLOCK_ACK: + case BSSGP_PDUT_SGSN_INVOKE_TRACE: + DEBUGP(DGPRS, "BSSGP PDU type 0x%02x only exists in DL\n", + pdu_type); + rc = -EINVAL; + break; + default: + DEBUGP(DGPRS, "BSSGP PDU type 0x%02x unknown\n", pdu_type); + break; + } + + return rc; +err_mand_ie: + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); +} + +int gprs_bssgp_tx_dl_ud(struct msgb *msg) +{ + struct gsm_bts *bts; + struct bssgp_ud_hdr *budh; + u_int8_t llc_pdu_tlv_hdr_len = 2; + u_int8_t *llc_pdu_tlv, *qos_profile; + u_int16_t pdu_lifetime = 1000; /* centi-seconds */ + u_int8_t qos_profile_default[3] = { 0x00, 0x00, 0x21 }; + u_int16_t msg_len = msg->len; + + if (!msg->trx) { + DEBUGP(DGPRS, "Cannot transmit DL-UD without TRX assigned\n"); + return -EINVAL; + } + + bts = msg->trx->bts; + + if (msg->len > TVLV_MAX_ONEBYTE) + llc_pdu_tlv_hdr_len += 1; + + /* prepend the tag and length of the LLC-PDU TLV */ + llc_pdu_tlv = msgb_push(msg, llc_pdu_tlv_hdr_len); + llc_pdu_tlv[0] = BSSGP_IE_LLC_PDU; + if (llc_pdu_tlv_hdr_len > 2) { + llc_pdu_tlv[1] = msg_len >> 8; + llc_pdu_tlv[2] = msg_len & 0xff; + } else { + llc_pdu_tlv[1] = msg_len & 0x3f; + llc_pdu_tlv[1] |= 0x80; + } + + /* FIXME: optional elements */ + + /* prepend the pdu lifetime */ + pdu_lifetime = htons(pdu_lifetime); + msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (u_int8_t *)&pdu_lifetime); + + /* prepend the QoS profile, TLLI and pdu type */ + budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh)); + memcpy(budh->qos_profile, qos_profile_default, sizeof(qos_profile_default)); + budh->tlli = htonl(msg->tlli); + budh->pdu_type = BSSGP_PDUT_DL_UNITDATA; + + return gprs_ns_sendmsg(NULL, bts->gprs.cell.bvci, msg); +} diff --git a/openbsc/src/gprs_ns.c b/openbsc/src/gprs_ns.c new file mode 100644 index 00000000..a686a22a --- /dev/null +++ b/openbsc/src/gprs_ns.c @@ -0,0 +1,348 @@ +/* GPRS Networks Service (NS) messages on the Gb interface + * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/* Some introduction into NS: NS is used typically on top of frame relay, + * but in the ip.access world it is encapsulated in UDP packets. It serves + * as an intermediate shim betwen BSSGP and the underlying medium. It doesn't + * do much, apart from providing congestion notification and status indication. + * + * Terms: + * NS Network Service + * NSVC NS Virtual Connection + * NSEI NS Entity Identifier + * NSVL NS Virtual Link + * NSVLI NS Virtual Link Identifier + * BVC BSSGP Virtual Connection + * BVCI BSSGP Virtual Connection Identifier + * NSVCG NS Virtual Connection Goup + * Blocked NS-VC cannot be used for user traffic + * Alive Ability of a NS-VC to provide communication + * + * There can be multiple BSSGP virtual connections over one (group of) NSVC's. BSSGP will + * therefore identify the BSSGP virtual connection by a BVCI passed down to NS. + * NS then has to firgure out which NSVC's are responsible for this BVCI. + * Those mappings are administratively configured. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define NS_ALLOC_SIZE 1024 + +static const struct tlv_definition ns_att_tlvdef = { + .def = { + [NS_IE_CAUSE] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_VCI] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_PDU] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_BVCI] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_NSEI] = { TLV_TYPE_TvLV, 0 }, + }, +}; + +#define NSE_S_BLOCKED 0x0001 +#define NSE_S_ALIVE 0x0002 + +struct gprs_nsvc { + struct llist_head list; + + u_int16_t nsei; /* end-to-end significance */ + u_int16_t nsvci; /* uniquely identifies NS-VC at SGSN */ + + u_int32_t state; + + struct timer_list alive_timer; + int timer_is_tns_alive; + int alive_retries; +}; + +/* FIXME: dynamically search for the matching NSVC */ +static struct gprs_nsvc dummy_nsvc = { .state = NSE_S_BLOCKED | NSE_S_ALIVE }; + +/* Section 10.3.2, Table 13 */ +static const char *ns_cause_str[] = { + [NS_CAUSE_TRANSIT_FAIL] = "Transit network failure", + [NS_CAUSE_OM_INTERVENTION] = "O&M intervention", + [NS_CAUSE_EQUIP_FAIL] = "Equipment failure", + [NS_CAUSE_NSVC_BLOCKED] = "NS-VC blocked", + [NS_CAUSE_NSVC_UNKNOWN] = "NS-VC unknown", + [NS_CAUSE_BVCI_UNKNOWN] = "BVCI unknown", + [NS_CAUSE_SEM_INCORR_PDU] = "Semantically incorrect PDU", + [NS_CAUSE_PDU_INCOMP_PSTATE] = "PDU not compatible with protocol state", + [NS_CAUSE_PROTO_ERR_UNSPEC] = "Protocol error, unspecified", + [NS_CAUSE_INVAL_ESSENT_IE] = "Invalid essential IE", + [NS_CAUSE_MISSING_ESSENT_IE] = "Missing essential IE", +}; + +static const char *gprs_ns_cause_str(enum ns_cause cause) +{ + if (cause >= ARRAY_SIZE(ns_cause_str)) + return "undefined"; + + if (ns_cause_str[cause]) + return ns_cause_str[cause]; + + return "undefined"; +} + +static int gprs_ns_tx(struct msgb *msg) +{ + return ipac_gprs_send(msg); +} + +static int gprs_ns_tx_simple(struct gprs_ns_link *link, u_int8_t pdu_type) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + + if (!msg) + return -ENOMEM; + + nsh = (struct gprs_ns_hdr *) msgb_put(msg, sizeof(*nsh)); + + nsh->pdu_type = pdu_type; + + return gprs_ns_tx(msg); +} + +#define NS_TIMER_ALIVE 3, 0 /* after 3 seconds without response, we retry */ +#define NS_TIMER_TEST 30, 0 /* every 10 seconds we check if the BTS is still alive */ +#define NS_ALIVE_RETRIES 10 /* after 3 failed retransmit we declare BTS as dead */ + +static void gprs_ns_alive_cb(void *data) +{ + struct gprs_nsvc *nsvc = data; + + if (nsvc->timer_is_tns_alive) { + /* Tns-alive case: we expired without response ! */ + nsvc->alive_retries++; + if (nsvc->alive_retries > NS_ALIVE_RETRIES) { + /* mark as dead and blocked */ + nsvc->state = NSE_S_BLOCKED; + DEBUGP(DGPRS, "Tns-alive more then %u retries, " + " blocking NS-VC\n", NS_ALIVE_RETRIES); + /* FIXME: inform higher layers */ + return; + } + } else { + /* Tns-test case: send NS-ALIVE PDU */ + gprs_ns_tx_simple(NULL, NS_PDUT_ALIVE); + /* start Tns-alive timer */ + nsvc->timer_is_tns_alive = 1; + } + bsc_schedule_timer(&nsvc->alive_timer, NS_TIMER_ALIVE); +} + +/* Section 9.2.6 */ +static int gprs_ns_tx_reset_ack(u_int16_t nsvci, u_int16_t nsei) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + + if (!msg) + return -ENOMEM; + + nsvci = htons(nsvci); + nsei = htons(nsei); + + nsh = (struct gprs_ns_hdr *) msgb_put(msg, sizeof(*nsh)); + + nsh->pdu_type = NS_PDUT_RESET_ACK; + + msgb_tvlv_put(msg, NS_IE_VCI, 2, (u_int8_t *)&nsvci); + msgb_tvlv_put(msg, NS_IE_NSEI, 2, (u_int8_t *)&nsei); + + return gprs_ns_tx(msg); +} + +/* Section 9.2.10: transmit side */ +int gprs_ns_sendmsg(struct gprs_ns_link *link, u_int16_t bvci, + struct msgb *msg) +{ + struct gprs_ns_hdr *nsh; + + nsh = (struct gprs_ns_hdr *) msgb_push(msg, sizeof(*nsh) + 3); + if (!nsh) { + DEBUGP(DGPRS, "Not enough headroom for NS header\n"); + return -EIO; + } + + nsh->pdu_type = NS_PDUT_UNITDATA; + /* spare octet in data[0] */ + nsh->data[1] = bvci >> 8; + nsh->data[2] = bvci & 0xff; + + return gprs_ns_tx(msg); +} + +/* Section 9.2.10: receive side */ +static int gprs_ns_rx_unitdata(struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h; + u_int16_t bvci; + + /* spare octet in data[0] */ + bvci = nsh->data[1] << 8 | nsh->data[2]; + msg->l3h = &nsh->data[3]; + + /* call upper layer (BSSGP) */ + return gprs_bssgp_rcvmsg(msg, bvci); +} + +/* Section 9.2.7 */ +static int gprs_ns_rx_status(struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = msg->l2h; + struct tlv_parsed tp; + u_int8_t cause; + int rc; + + DEBUGP(DGPRS, "NS STATUS "); + + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0); + + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE)) { + DEBUGPC(DGPRS, "missing cause IE\n"); + return -EINVAL; + } + + cause = *TLVP_VAL(&tp, NS_IE_CAUSE); + DEBUGPC(DGPRS, "cause=%s\n", gprs_ns_cause_str(cause)); + + return 0; +} + +/* Section 7.3 */ +static int gprs_ns_rx_reset(struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct gprs_nsvc *nsvc = &dummy_nsvc; + struct tlv_parsed tp; + u_int8_t *cause; + u_int16_t *nsvci, *nsei; + int rc; + + DEBUGP(DGPRS, "NS RESET "); + + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0); + + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || + !TLVP_PRESENT(&tp, NS_IE_VCI) || + !TLVP_PRESENT(&tp, NS_IE_NSEI)) { + /* FIXME: respond with NS_CAUSE_MISSING_ESSENT_IE */ + DEBUGPC(DGPRS, "Missing mandatory IE\n"); + return -EINVAL; + } + + cause = (u_int8_t *) TLVP_VAL(&tp, NS_IE_CAUSE); + nsvci = (u_int16_t *) TLVP_VAL(&tp, NS_IE_VCI); + nsei = (u_int16_t *) TLVP_VAL(&tp, NS_IE_NSEI); + + *nsvci = ntohs(*nsvci); + *nsei = ntohs(*nsei); + + DEBUGPC(DGPRS, "cause=%s, NSVCI=%u, NSEI=%u\n", + gprs_ns_cause_str(*cause), *nsvci, *nsei); + + /* mark the NS-VC as blocked and alive */ + nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; + nsvc->nsei = *nsei; + nsvc->nsvci = *nsvci; + + /* start the test procedure */ + nsvc->alive_timer.cb = gprs_ns_alive_cb; + nsvc->alive_timer.data = nsvc; + bsc_schedule_timer(&nsvc->alive_timer, NS_TIMER_ALIVE); + + return gprs_ns_tx_reset_ack(*nsvci, *nsei); +} + +/* main entry point, here incoming NS frames enter */ +int gprs_ns_rcvmsg(struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct gprs_nsvc *nsvc = &dummy_nsvc; + int rc = 0; + + switch (nsh->pdu_type) { + case NS_PDUT_ALIVE: + /* remote end inquires whether we're still alive, + * we need to respond with ALIVE_ACK */ + rc = gprs_ns_tx_simple(NULL, NS_PDUT_ALIVE_ACK); + break; + case NS_PDUT_ALIVE_ACK: + /* stop Tns-alive */ + bsc_del_timer(&nsvc->alive_timer); + /* start Tns-test */ + nsvc->timer_is_tns_alive = 0; + bsc_schedule_timer(&nsvc->alive_timer, NS_TIMER_TEST); + break; + case NS_PDUT_UNITDATA: + /* actual user data */ + rc = gprs_ns_rx_unitdata(msg); + break; + case NS_PDUT_STATUS: + rc = gprs_ns_rx_status(msg); + break; + case NS_PDUT_RESET: + rc = gprs_ns_rx_reset(msg); + break; + case NS_PDUT_RESET_ACK: + /* FIXME: mark remote NS-VC as blocked + active */ + break; + case NS_PDUT_UNBLOCK: + /* Section 7.2: unblocking procedure */ + DEBUGP(DGPRS, "NS UNBLOCK\n"); + nsvc->state &= ~NSE_S_BLOCKED; + rc = gprs_ns_tx_simple(NULL, NS_PDUT_UNBLOCK_ACK); + break; + case NS_PDUT_UNBLOCK_ACK: + /* FIXME: mark remote NS-VC as unblocked + active */ + break; + case NS_PDUT_BLOCK: + DEBUGP(DGPRS, "NS BLOCK\n"); + nsvc->state |= NSE_S_BLOCKED; + rc = gprs_ns_tx_simple(NULL, NS_PDUT_UNBLOCK_ACK); + break; + case NS_PDUT_BLOCK_ACK: + /* FIXME: mark remote NS-VC as blocked + active */ + break; + default: + DEBUGP(DGPRS, "Unknown NS PDU type 0x%02x\n", nsh->pdu_type); + rc = -EINVAL; + break; + } + return rc; +} + -- cgit v1.2.3 From c547848eadb3f285078a68d17240ba9280c2c117 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 18 Mar 2010 00:01:43 +0800 Subject: GPRS: remove hard-coded IP address for NSIP responses from SGSN->BTS --- openbsc/include/openbsc/gprs_ns.h | 2 +- openbsc/src/gprs_ns.c | 54 +++++++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/openbsc/include/openbsc/gprs_ns.h b/openbsc/include/openbsc/gprs_ns.h index 90f1adf3..9ea4d675 100644 --- a/openbsc/include/openbsc/gprs_ns.h +++ b/openbsc/include/openbsc/gprs_ns.h @@ -54,7 +54,7 @@ struct gprs_ns_link { }; -int gprs_ns_rcvmsg(struct msgb *msg); +int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr); int gprs_ns_sendmsg(struct gprs_ns_link *link, u_int16_t bvci, struct msgb *msg); diff --git a/openbsc/src/gprs_ns.c b/openbsc/src/gprs_ns.c index a686a22a..6fb7d1d9 100644 --- a/openbsc/src/gprs_ns.c +++ b/openbsc/src/gprs_ns.c @@ -85,6 +85,12 @@ struct gprs_nsvc { struct timer_list alive_timer; int timer_is_tns_alive; int alive_retries; + + union { + struct { + struct sockaddr_in bts_addr; + } ip; + }; }; /* FIXME: dynamically search for the matching NSVC */ @@ -116,12 +122,12 @@ static const char *gprs_ns_cause_str(enum ns_cause cause) return "undefined"; } -static int gprs_ns_tx(struct msgb *msg) +static int gprs_ns_tx(struct msgb *msg, struct gprs_nsvc *nsvc) { - return ipac_gprs_send(msg); + return ipac_gprs_send(msg, &nsvc->ip.bts_addr); } -static int gprs_ns_tx_simple(struct gprs_ns_link *link, u_int8_t pdu_type) +static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, u_int8_t pdu_type) { struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); struct gprs_ns_hdr *nsh; @@ -133,7 +139,7 @@ static int gprs_ns_tx_simple(struct gprs_ns_link *link, u_int8_t pdu_type) nsh->pdu_type = pdu_type; - return gprs_ns_tx(msg); + return gprs_ns_tx(msg, nsvc); } #define NS_TIMER_ALIVE 3, 0 /* after 3 seconds without response, we retry */ @@ -157,7 +163,7 @@ static void gprs_ns_alive_cb(void *data) } } else { /* Tns-test case: send NS-ALIVE PDU */ - gprs_ns_tx_simple(NULL, NS_PDUT_ALIVE); + gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); /* start Tns-alive timer */ nsvc->timer_is_tns_alive = 1; } @@ -165,25 +171,28 @@ static void gprs_ns_alive_cb(void *data) } /* Section 9.2.6 */ -static int gprs_ns_tx_reset_ack(u_int16_t nsvci, u_int16_t nsei) +static int gprs_ns_tx_reset_ack(struct gprs_nsvc *nsvc) { struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); struct gprs_ns_hdr *nsh; + u_int16_t nsvci, nsei; if (!msg) return -ENOMEM; - nsvci = htons(nsvci); - nsei = htons(nsei); + nsvci = htons(nsvc->nsvci); + nsei = htons(nsvc->nsei); nsh = (struct gprs_ns_hdr *) msgb_put(msg, sizeof(*nsh)); nsh->pdu_type = NS_PDUT_RESET_ACK; + DEBUGP(DGPRS, "nsvci=%u, nsei=%u\n", nsvc->nsvci, nsvc->nsei); + msgb_tvlv_put(msg, NS_IE_VCI, 2, (u_int8_t *)&nsvci); msgb_tvlv_put(msg, NS_IE_NSEI, 2, (u_int8_t *)&nsei); - return gprs_ns_tx(msg); + return gprs_ns_tx(msg, nsvc); } /* Section 9.2.10: transmit side */ @@ -191,6 +200,7 @@ int gprs_ns_sendmsg(struct gprs_ns_link *link, u_int16_t bvci, struct msgb *msg) { struct gprs_ns_hdr *nsh; + struct gprs_nsvc *nsvc = &dummy_nsvc; nsh = (struct gprs_ns_hdr *) msgb_push(msg, sizeof(*nsh) + 3); if (!nsh) { @@ -203,7 +213,7 @@ int gprs_ns_sendmsg(struct gprs_ns_link *link, u_int16_t bvci, nsh->data[1] = bvci >> 8; nsh->data[2] = bvci & 0xff; - return gprs_ns_tx(msg); + return gprs_ns_tx(msg, nsvc); } /* Section 9.2.10: receive side */ @@ -269,37 +279,37 @@ static int gprs_ns_rx_reset(struct msgb *msg) nsvci = (u_int16_t *) TLVP_VAL(&tp, NS_IE_VCI); nsei = (u_int16_t *) TLVP_VAL(&tp, NS_IE_NSEI); - *nsvci = ntohs(*nsvci); - *nsei = ntohs(*nsei); + nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; + nsvc->nsei = ntohs(*nsei); + nsvc->nsvci = ntohs(*nsvci); DEBUGPC(DGPRS, "cause=%s, NSVCI=%u, NSEI=%u\n", - gprs_ns_cause_str(*cause), *nsvci, *nsei); + gprs_ns_cause_str(*cause), nsvc->nsvci, nsvc->nsei); /* mark the NS-VC as blocked and alive */ - nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; - nsvc->nsei = *nsei; - nsvc->nsvci = *nsvci; - /* start the test procedure */ nsvc->alive_timer.cb = gprs_ns_alive_cb; nsvc->alive_timer.data = nsvc; bsc_schedule_timer(&nsvc->alive_timer, NS_TIMER_ALIVE); - return gprs_ns_tx_reset_ack(*nsvci, *nsei); + return gprs_ns_tx_reset_ack(nsvc); } /* main entry point, here incoming NS frames enter */ -int gprs_ns_rcvmsg(struct msgb *msg) +int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; struct gprs_nsvc *nsvc = &dummy_nsvc; int rc = 0; + /* FIXME: do this properly! */ + nsvc->ip.bts_addr = *saddr; + switch (nsh->pdu_type) { case NS_PDUT_ALIVE: /* remote end inquires whether we're still alive, * we need to respond with ALIVE_ACK */ - rc = gprs_ns_tx_simple(NULL, NS_PDUT_ALIVE_ACK); + rc = gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE_ACK); break; case NS_PDUT_ALIVE_ACK: /* stop Tns-alive */ @@ -325,7 +335,7 @@ int gprs_ns_rcvmsg(struct msgb *msg) /* Section 7.2: unblocking procedure */ DEBUGP(DGPRS, "NS UNBLOCK\n"); nsvc->state &= ~NSE_S_BLOCKED; - rc = gprs_ns_tx_simple(NULL, NS_PDUT_UNBLOCK_ACK); + rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); break; case NS_PDUT_UNBLOCK_ACK: /* FIXME: mark remote NS-VC as unblocked + active */ @@ -333,7 +343,7 @@ int gprs_ns_rcvmsg(struct msgb *msg) case NS_PDUT_BLOCK: DEBUGP(DGPRS, "NS BLOCK\n"); nsvc->state |= NSE_S_BLOCKED; - rc = gprs_ns_tx_simple(NULL, NS_PDUT_UNBLOCK_ACK); + rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); break; case NS_PDUT_BLOCK_ACK: /* FIXME: mark remote NS-VC as blocked + active */ -- cgit v1.2.3 From 510c3920c8e965d1bd36ece2a686d9e63f009d17 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 30 Apr 2010 16:33:12 +0200 Subject: gprs: Update gprs-sgsn branch to use new msgb->cb layout The explicit 'tlli, gmmh' members of struct msgb are gone from current libosmocore and have been replaced by the more generic 'control buffer' mechanism. --- openbsc/src/gprs_bssgp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openbsc/src/gprs_bssgp.c b/openbsc/src/gprs_bssgp.c index de57d25d..b4436294 100644 --- a/openbsc/src/gprs_bssgp.c +++ b/openbsc/src/gprs_bssgp.c @@ -144,7 +144,7 @@ static int bssgp_rx_ul_ud(struct msgb *msg, u_int16_t bvci) DEBUGP(DGPRS, "BSSGP UL-UD\n"); - msg->tlli = ntohl(budh->tlli); + msgb_tlli(msg) = ntohl(budh->tlli); rc = bssgp_tlv_parse(&tp, budh->data, data_len); /* Cell ID and LLC_PDU are the only mandatory IE */ @@ -159,7 +159,7 @@ static int bssgp_rx_ul_ud(struct msgb *msg, u_int16_t bvci) if (bts) msg->trx = bts->c0; - msg->llch = TLVP_VAL(&tp, BSSGP_IE_LLC_PDU); + msgb_llch(msg) = TLVP_VAL(&tp, BSSGP_IE_LLC_PDU); return gprs_llc_rcvmsg(msg, &tp); } @@ -390,7 +390,7 @@ int gprs_bssgp_tx_dl_ud(struct msgb *msg) /* prepend the QoS profile, TLLI and pdu type */ budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh)); memcpy(budh->qos_profile, qos_profile_default, sizeof(qos_profile_default)); - budh->tlli = htonl(msg->tlli); + budh->tlli = htonl(msgb_tlli(msg)); budh->pdu_type = BSSGP_PDUT_DL_UNITDATA; return gprs_ns_sendmsg(NULL, bts->gprs.cell.bvci, msg); -- cgit v1.2.3 From f030b210e8c13314d361a6b721a0cbcc72935219 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 26 Apr 2010 19:18:54 +0200 Subject: GPRS: Modularize the NS implementation * move UDP listener code for NSIP from input/ipaccess.c and into gprs_ns.c * add PDU type, IE and CAUSE values for later IP based 3GPP TS 48.016 * support multiple NS-VCs and their lookup based on NSVC and sockaddr_in * maintain the remote_state (blocked/alive) for each NSVC * introduce the concept of GPRS_NS instances, move all global vars to instance * remove hardcoded calls to gprs_bssgp_rcvmsg() and replace it by callback WARNING: This is not finished code. While it will compile, it will not work yet, as BSSGP needs to be converted to properly indicate the NSVC to which it needs to send data. --- openbsc/include/openbsc/gprs_ns.h | 62 ++++++++-- openbsc/src/gprs_ns.c | 253 ++++++++++++++++++++++++++++++++++---- 2 files changed, 284 insertions(+), 31 deletions(-) diff --git a/openbsc/include/openbsc/gprs_ns.h b/openbsc/include/openbsc/gprs_ns.h index 9ea4d675..98b31f8d 100644 --- a/openbsc/include/openbsc/gprs_ns.h +++ b/openbsc/include/openbsc/gprs_ns.h @@ -1,6 +1,10 @@ #ifndef _GPRS_NS_H #define _GPRS_NS_H +/* GPRS Networks Service (NS) messages on the Gb interface + * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) + * 3GPP TS 48.016 version 6.5.0 Release 6 / ETSI TS 148 016 V6.5.0 (2005-11) */ + struct gprs_ns_hdr { u_int8_t pdu_type; u_int8_t data[0]; @@ -18,6 +22,15 @@ enum ns_pdu_type { NS_PDUT_STATUS = 0x08, NS_PDUT_ALIVE = 0x0a, NS_PDUT_ALIVE_ACK = 0x0b, + /* TS 48.016 Section 10.3.7, Table 10.3.7.1 */ + SNS_PDUT_ACK = 0x0c, + SNS_PDUT_ADD = 0x0d, + SNS_PDUT_CHANGE_WEIGHT = 0x0e, + SNS_PDUT_CONFIG = 0x0f, + SNS_PDUT_CONFIG_ACK = 0x10, + SNS_PDUT_DELETE = 0x11, + SNS_PDUT_SIZE = 0x12, + SNS_PDUT_SIZE_ACK = 0x13, }; /* TS 08.16, Section 10.3, Table 12 */ @@ -27,6 +40,14 @@ enum ns_ctrl_ie { NS_IE_PDU = 0x02, NS_IE_BVCI = 0x03, NS_IE_NSEI = 0x04, + /* TS 48.016 Section 10.3, Table 10.3.1 */ + NS_IE_IPv4_LIST = 0x05, + NS_IE_IPv6_LIST = 0x06, + NS_IE_MAX_NR_NSVC = 0x07, + NS_IE_IPv4_EP_NR = 0x08, + NS_IE_IPv6_EP_NR = 0x09, + NS_IE_RESET_FLAG = 0x0a, + NS_IE_IP_ADDR = 0x0b, }; /* TS 08.16, Section 10.3.2, Table 13 */ @@ -42,20 +63,43 @@ enum ns_cause { NS_CAUSE_PROTO_ERR_UNSPEC = 0x0b, NS_CAUSE_INVAL_ESSENT_IE = 0x0c, NS_CAUSE_MISSING_ESSENT_IE = 0x0d, + /* TS 48.016 Section 10.3.2, Table 10.3.2.1 */ + NS_CAUSE_INVAL_NR_IPv4_EP = 0x0e, + NS_CAUSE_INVAL_NR_IPv6_EP = 0x0f, + NS_CAUSE_INVAL_NR_NS_VC = 0x10, + NS_CAUSE_INVAL_WEIGH = 0x11, + NS_CAUSE_UNKN_IP_EP = 0x12, + NS_CAUSE_UNKN_IP_ADDR = 0x13, + NS_CAUSE_UNKN_IP_TEST_FAILED = 0x14, }; -/* a layer 1 entity transporting NS frames */ -struct gprs_ns_link { - union { - struct { - int fd; - } ip; - }; +struct gprs_nsvc; +struct gprs_ns_inst; + +enum gprs_ns_evt { + GPRS_NS_EVT_UNIT_DATA, }; +typedef int gprs_ns_cb_t(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, + struct msgb *msg, u_int16_t bvci); + +/* Create a new NS protocol instance */ +struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb); + +/* Destroy a NS protocol instance */ +void gprs_ns_destroy(struct gprs_ns_inst *nsi); -int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr); +/* Listen for incoming GPRS packets */ +int nsip_listen(struct gprs_ns_inst *nsi, uint16_t udp_port); -int gprs_ns_sendmsg(struct gprs_ns_link *link, u_int16_t bvci, +struct sockaddr_in; + +/* main entry point, here incoming NS frames enter */ +int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, + struct sockaddr_in *saddr); + +/* main function for higher layers (BSSGP) to send NS messages */ +int gprs_ns_sendmsg(struct gprs_nsvc *nsvc, u_int16_t bvci, struct msgb *msg); + #endif diff --git a/openbsc/src/gprs_ns.c b/openbsc/src/gprs_ns.c index 6fb7d1d9..c5166fa5 100644 --- a/openbsc/src/gprs_ns.c +++ b/openbsc/src/gprs_ns.c @@ -1,7 +1,7 @@ /* GPRS Networks Service (NS) messages on the Gb interface * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */ -/* (C) 2009 by Harald Welte +/* (C) 2009-2010 by Harald Welte * * All Rights Reserved * @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -76,11 +77,13 @@ static const struct tlv_definition ns_att_tlvdef = { struct gprs_nsvc { struct llist_head list; + struct gprs_ns_inst *nsi; u_int16_t nsei; /* end-to-end significance */ u_int16_t nsvci; /* uniquely identifies NS-VC at SGSN */ u_int32_t state; + u_int32_t remote_state; struct timer_list alive_timer; int timer_is_tns_alive; @@ -93,8 +96,67 @@ struct gprs_nsvc { }; }; -/* FIXME: dynamically search for the matching NSVC */ -static struct gprs_nsvc dummy_nsvc = { .state = NSE_S_BLOCKED | NSE_S_ALIVE }; +enum gprs_ns_ll { + GPRS_NS_LL_UDP, + GPRS_NS_LL_E1, +}; + +/* An instance of the NS protocol stack */ +struct gprs_ns_inst { + /* callback to the user for incoming UNIT DATA IND */ + gprs_ns_cb_t *cb; + + /* linked lists of all NSVC in this instance */ + struct llist_head gprs_nsvcs; + + /* which link-layer are we based on? */ + enum gprs_ns_ll ll; + + union { + /* NS-over-IP specific bits */ + struct { + struct bsc_fd fd; + } nsip; + }; +}; + +/* Lookup struct gprs_nsvc based on NSVCI */ +static struct gprs_nsvc *nsvc_by_nsvci(struct gprs_ns_inst *nsi, + u_int16_t nsvci) +{ + struct gprs_nsvc *nsvc; + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (nsvc->nsvci == nsvci) + return nsvc; + } + return NULL; +} + +/* Lookup struct gprs_nsvc based on remote peer socket addr */ +static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi, + struct sockaddr_in *sin) +{ + struct gprs_nsvc *nsvc; + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (!memcmp(&nsvc->ip.bts_addr, sin, sizeof(*sin))) + return nsvc; + } + return NULL; +} + +static struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, u_int16_t nsvci) +{ + struct gprs_nsvc *nsvc; + + nsvc = talloc_zero(nsi, struct gprs_nsvc); + nsvc->nsvci = nsvci; + /* before RESET procedure: BLOCKED and DEAD */ + nsvc->state = NSE_S_BLOCKED; + nsvc->nsi = nsi; + llist_add(&nsvc->list, &nsi->gprs_nsvcs); + + return nsvc; +} /* Section 10.3.2, Table 13 */ static const char *ns_cause_str[] = { @@ -122,9 +184,23 @@ static const char *gprs_ns_cause_str(enum ns_cause cause) return "undefined"; } +static int nsip_sendmsg(struct msgb *msg, struct gprs_nsvc *nsvc); + static int gprs_ns_tx(struct msgb *msg, struct gprs_nsvc *nsvc) { - return ipac_gprs_send(msg, &nsvc->ip.bts_addr); + int ret; + + switch (nsvc->nsi->ll) { + case GPRS_NS_LL_UDP: + ret = nsip_sendmsg(msg, nsvc); + break; + default: + LOGP(DGPRS, LOGL_ERROR, "unsupported NS linklayer %u\n", nsvc->nsi->ll); + msgb_free(msg); + ret = -EIO; + break; + } + return ret; } static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, u_int8_t pdu_type) @@ -196,11 +272,10 @@ static int gprs_ns_tx_reset_ack(struct gprs_nsvc *nsvc) } /* Section 9.2.10: transmit side */ -int gprs_ns_sendmsg(struct gprs_ns_link *link, u_int16_t bvci, +int gprs_ns_sendmsg(struct gprs_nsvc *nsvc, u_int16_t bvci, struct msgb *msg) { struct gprs_ns_hdr *nsh; - struct gprs_nsvc *nsvc = &dummy_nsvc; nsh = (struct gprs_ns_hdr *) msgb_push(msg, sizeof(*nsh) + 3); if (!nsh) { @@ -217,7 +292,7 @@ int gprs_ns_sendmsg(struct gprs_ns_link *link, u_int16_t bvci, } /* Section 9.2.10: receive side */ -static int gprs_ns_rx_unitdata(struct msgb *msg) +static int gprs_ns_rx_unitdata(struct msgb *msg, struct gprs_nsvc *nsvc) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h; u_int16_t bvci; @@ -227,13 +302,13 @@ static int gprs_ns_rx_unitdata(struct msgb *msg) msg->l3h = &nsh->data[3]; /* call upper layer (BSSGP) */ - return gprs_bssgp_rcvmsg(msg, bvci); + return nsvc->nsi->cb(GPRS_NS_EVT_UNIT_DATA, nsvc, msg, bvci); } /* Section 9.2.7 */ -static int gprs_ns_rx_status(struct msgb *msg) +static int gprs_ns_rx_status(struct msgb *msg, struct gprs_nsvc *nsvc) { - struct gprs_ns_hdr *nsh = msg->l2h; + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; struct tlv_parsed tp; u_int8_t cause; int rc; @@ -254,10 +329,9 @@ static int gprs_ns_rx_status(struct msgb *msg) } /* Section 7.3 */ -static int gprs_ns_rx_reset(struct msgb *msg) +static int gprs_ns_rx_reset(struct msgb *msg, struct gprs_nsvc *nsvc) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; - struct gprs_nsvc *nsvc = &dummy_nsvc; struct tlv_parsed tp; u_int8_t *cause; u_int16_t *nsvci, *nsei; @@ -296,14 +370,24 @@ static int gprs_ns_rx_reset(struct msgb *msg) } /* main entry point, here incoming NS frames enter */ -int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr) +int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, + struct sockaddr_in *saddr) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; - struct gprs_nsvc *nsvc = &dummy_nsvc; + struct gprs_nsvc *nsvc; int rc = 0; - /* FIXME: do this properly! */ - nsvc->ip.bts_addr = *saddr; + /* look up the NSVC based on source address */ + nsvc = nsvc_by_rem_addr(nsi, saddr); + if (!nsvc) { + /* Only the RESET procedure creates a new NSVC */ + if (nsh->pdu_type != NS_PDUT_RESET) + return -EIO; + nsvc = nsvc_create(nsi, 0xffff); + nsvc->ip.bts_addr = *saddr; + rc = gprs_ns_rx_reset(msg, nsvc); + return rc; + } switch (nsh->pdu_type) { case NS_PDUT_ALIVE: @@ -320,16 +404,18 @@ int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr) break; case NS_PDUT_UNITDATA: /* actual user data */ - rc = gprs_ns_rx_unitdata(msg); + rc = gprs_ns_rx_unitdata(msg, nsvc); break; case NS_PDUT_STATUS: - rc = gprs_ns_rx_status(msg); + rc = gprs_ns_rx_status(msg, nsvc); break; case NS_PDUT_RESET: - rc = gprs_ns_rx_reset(msg); + rc = gprs_ns_rx_reset(msg, nsvc); break; case NS_PDUT_RESET_ACK: - /* FIXME: mark remote NS-VC as blocked + active */ + DEBUGP(DGPRS, "NS RESET ACK\n"); + /* mark remote NS-VC as blocked + active */ + nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; break; case NS_PDUT_UNBLOCK: /* Section 7.2: unblocking procedure */ @@ -338,7 +424,9 @@ int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr) rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); break; case NS_PDUT_UNBLOCK_ACK: - /* FIXME: mark remote NS-VC as unblocked + active */ + DEBUGP(DGPRS, "NS UNBLOCK ACK\n"); + /* mark remote NS-VC as unblocked + active */ + nsvc->remote_state = NSE_S_ALIVE; break; case NS_PDUT_BLOCK: DEBUGP(DGPRS, "NS BLOCK\n"); @@ -346,7 +434,9 @@ int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr) rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); break; case NS_PDUT_BLOCK_ACK: - /* FIXME: mark remote NS-VC as blocked + active */ + DEBUGP(DGPRS, "NS BLOCK ACK\n"); + /* mark remote NS-VC as blocked + active */ + nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; break; default: DEBUGP(DGPRS, "Unknown NS PDU type 0x%02x\n", nsh->pdu_type); @@ -356,3 +446,122 @@ int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr) return rc; } +struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb) +{ + struct gprs_ns_inst *nsi = talloc_zero(tall_bsc_ctx, struct gprs_ns_inst); + + nsi->cb = cb; + INIT_LLIST_HEAD(&nsi->gprs_nsvcs); + + return NULL; +} + +void gprs_ns_destroy(struct gprs_ns_inst *nsi) +{ + /* FIXME: clear all timers */ + + /* recursively free the NSI and all its NSVCs */ + talloc_free(nsi); +} + + +/* NS-over-IP code, according to 3GPP TS 48.016 Chapter 6.2 + * We don't support Size Procedure, Configuration Procedure, ChangeWeight Procedure */ + +/* Read a single NS-over-IP message */ +static struct msgb *read_nsip_msg(struct bsc_fd *bfd, int *error, + struct sockaddr_in *saddr) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Abis/IP/GPRS-NS"); + int ret = 0; + socklen_t saddr_len = sizeof(*saddr); + + if (!msg) { + *error = -ENOMEM; + return NULL; + } + + ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, + (struct sockaddr *)saddr, &saddr_len); + if (ret < 0) { + fprintf(stderr, "recv error %s\n", strerror(errno)); + msgb_free(msg); + *error = ret; + return NULL; + } else if (ret == 0) { + msgb_free(msg); + *error = ret; + return NULL; + } + + msg->l2h = msg->data; + msgb_put(msg, ret); + + return msg; +} + +static int handle_nsip_read(struct bsc_fd *bfd) +{ + int error; + struct sockaddr_in saddr; + struct gprs_ns_inst *nsi = bfd->data; + struct msgb *msg = read_nsip_msg(bfd, &error, &saddr); + + if (!msg) + return error; + + return gprs_ns_rcvmsg(nsi, msg, &saddr); +} + +static int handle_nsip_write(struct bsc_fd *bfd) +{ + /* FIXME: actually send the data here instead of nsip_sendmsg() */ + return -EIO; +} + +int nsip_sendmsg(struct msgb *msg, struct gprs_nsvc *nsvc) +{ + int rc; + struct gprs_ns_inst *nsi = nsvc->nsi; + struct sockaddr_in *daddr = &nsvc->ip.bts_addr; + + rc = sendto(nsi->nsip.fd.fd, msg->data, msg->len, 0, + (struct sockaddr *)daddr, sizeof(*daddr)); + + talloc_free(msg); + + return rc; +} + +/* UDP Port 23000 carries the LLC-in-BSSGP-in-NS protocol stack */ +static int nsip_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int rc = 0; + + if (what & BSC_FD_READ) + rc = handle_nsip_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_nsip_write(bfd); + + return rc; +} + + +/* FIXME: this is currently in input/ipaccess.c */ +extern int make_sock(struct bsc_fd *bfd, int proto, u_int16_t port, + int (*cb)(struct bsc_fd *fd, unsigned int what)); + +/* Listen for incoming GPRS packets */ +int nsip_listen(struct gprs_ns_inst *nsi, uint16_t udp_port) +{ + int ret; + + ret = make_sock(&nsi->nsip.fd, IPPROTO_UDP, udp_port, nsip_fd_cb); + if (ret < 0) + return ret; + + nsi->ll = GPRS_NS_LL_UDP; + nsi->nsip.fd.data = nsi; + + return ret; +} -- cgit v1.2.3 From 645609ddc8e3e4fe4508af5f3fa3edd073185fef Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 30 Apr 2010 16:43:39 +0200 Subject: gprs: Use new msgb->cb[] for storing a pointer to the NS-VC through which it was received --- openbsc/src/gprs_ns.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/openbsc/src/gprs_ns.c b/openbsc/src/gprs_ns.c index c5166fa5..b57de1d7 100644 --- a/openbsc/src/gprs_ns.c +++ b/openbsc/src/gprs_ns.c @@ -292,9 +292,10 @@ int gprs_ns_sendmsg(struct gprs_nsvc *nsvc, u_int16_t bvci, } /* Section 9.2.10: receive side */ -static int gprs_ns_rx_unitdata(struct msgb *msg, struct gprs_nsvc *nsvc) +static int gprs_ns_rx_unitdata(struct msgb *msg) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h; + struct gprs_nsvc *nsvc = msgb_nsvc(msg); u_int16_t bvci; /* spare octet in data[0] */ @@ -306,9 +307,10 @@ static int gprs_ns_rx_unitdata(struct msgb *msg, struct gprs_nsvc *nsvc) } /* Section 9.2.7 */ -static int gprs_ns_rx_status(struct msgb *msg, struct gprs_nsvc *nsvc) +static int gprs_ns_rx_status(struct msgb *msg) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct gprs_nsvc *nsvc = msgb_nsvc(msg); struct tlv_parsed tp; u_int8_t cause; int rc; @@ -329,9 +331,10 @@ static int gprs_ns_rx_status(struct msgb *msg, struct gprs_nsvc *nsvc) } /* Section 7.3 */ -static int gprs_ns_rx_reset(struct msgb *msg, struct gprs_nsvc *nsvc) +static int gprs_ns_rx_reset(struct msgb *msg) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct gprs_nsvc *nsvc = msgb_nsvc(msg); struct tlv_parsed tp; u_int8_t *cause; u_int16_t *nsvci, *nsei; @@ -385,9 +388,10 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, return -EIO; nsvc = nsvc_create(nsi, 0xffff); nsvc->ip.bts_addr = *saddr; - rc = gprs_ns_rx_reset(msg, nsvc); + rc = gprs_ns_rx_reset(msg); return rc; } + msgb_nsvc(msg) = nsvc; switch (nsh->pdu_type) { case NS_PDUT_ALIVE: @@ -404,13 +408,13 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, break; case NS_PDUT_UNITDATA: /* actual user data */ - rc = gprs_ns_rx_unitdata(msg, nsvc); + rc = gprs_ns_rx_unitdata(msg); break; case NS_PDUT_STATUS: - rc = gprs_ns_rx_status(msg, nsvc); + rc = gprs_ns_rx_status(msg); break; case NS_PDUT_RESET: - rc = gprs_ns_rx_reset(msg, nsvc); + rc = gprs_ns_rx_reset(msg); break; case NS_PDUT_RESET_ACK: DEBUGP(DGPRS, "NS RESET ACK\n"); -- cgit v1.2.3 From 24a655f1406f53ce54802e72f72b6bf19394672e Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 30 Apr 2010 19:54:29 +0200 Subject: gprs: remove msgb->nsvc pointer and replace it with NSEI and BVCI According to TS 08.16, the BSSGP layer needs to specify NSEI and BVCI when executing the NS UNITDATA REQUEST primitive of the underlying NS layer. Rather than passing around a pointer to the 'struct gprs_nsvc', we now have NSEI and BVCI as members of 'struct obsc_msgb_cb' and set them when BSSGP hands a message down to NS. NS then does a lookup of the 'gprs_nsvc' based on the NSEI parameter. --- openbsc/include/openbsc/gprs_ns.h | 3 +- openbsc/src/gprs_bssgp.c | 34 +++++++++++++++------- openbsc/src/gprs_ns.c | 61 +++++++++++++++++++++++++-------------- 3 files changed, 64 insertions(+), 34 deletions(-) diff --git a/openbsc/include/openbsc/gprs_ns.h b/openbsc/include/openbsc/gprs_ns.h index 98b31f8d..34a3e581 100644 --- a/openbsc/include/openbsc/gprs_ns.h +++ b/openbsc/include/openbsc/gprs_ns.h @@ -99,7 +99,6 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, struct sockaddr_in *saddr); /* main function for higher layers (BSSGP) to send NS messages */ -int gprs_ns_sendmsg(struct gprs_nsvc *nsvc, u_int16_t bvci, - struct msgb *msg); +int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg); #endif diff --git a/openbsc/src/gprs_bssgp.c b/openbsc/src/gprs_bssgp.c index b4436294..650d7d45 100644 --- a/openbsc/src/gprs_bssgp.c +++ b/openbsc/src/gprs_bssgp.c @@ -37,6 +37,7 @@ /* global pointer to the gsm network data structure */ /* FIXME: this must go! */ extern struct gsm_network *bsc_gsmnet; +struct gprs_ns_inst *bssgp_nsi; /* Chapter 11.3.9 / Table 11.10: Cause coding */ static const char *bssgp_cause_strings[] = { @@ -84,31 +85,38 @@ static inline struct msgb *bssgp_msgb_alloc(void) } /* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */ -static int bssgp_tx_simple_bvci(u_int8_t pdu_type, u_int16_t bvci, u_int16_t ns_bvci) +static int bssgp_tx_simple_bvci(u_int8_t pdu_type, u_int16_t nsei, + u_int16_t bvci, u_int16_t ns_bvci) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); u_int16_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, (u_int8_t *) &_bvci); - return gprs_ns_sendmsg(NULL, ns_bvci, msg); + return gprs_ns_sendmsg(bssgp_nsi, msg); } /* Chapter 10.4.5: Flow Control BVC ACK */ -static int bssgp_tx_fc_bvc_ack(u_int8_t tag, u_int16_t ns_bvci) +static int bssgp_tx_fc_bvc_ack(u_int16_t nsei, u_int8_t tag, u_int16_t ns_bvci) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + msgb_nsei(msg) = nsei; + msgb_bvci(msg) = ns_bvci; + bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK; msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag); - return gprs_ns_sendmsg(NULL, ns_bvci, msg); + return gprs_ns_sendmsg(bssgp_nsi, msg); } /* Chapter 10.4.14: Status */ @@ -119,6 +127,8 @@ static int bssgp_tx_status(u_int8_t cause, u_int16_t *bvci, struct msgb *orig_ms (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); DEBUGPC(DGPRS, "BSSGP: TX STATUS, cause=%s\n", bssgp_cause_str(cause)); + msgb_nsei(msg) = msgb_nsei(orig_msg); + msgb_bvci(msg) = 0; bgph->pdu_type = BSSGP_PDUT_STATUS; msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause); @@ -130,7 +140,7 @@ static int bssgp_tx_status(u_int8_t cause, u_int16_t *bvci, struct msgb *orig_ms msgb_tvlv_put(msg, BSSGP_IE_PDU_IN_ERROR, msgb_l3len(orig_msg), orig_msg->l3h); - return gprs_ns_sendmsg(NULL, 0, msg); + return gprs_ns_sendmsg(bssgp_nsi, msg); } /* Uplink unit-data */ @@ -221,7 +231,8 @@ static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp, return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); /* Send FLOW_CONTROL_BVC_ACK */ - return bssgp_tx_fc_bvc_ack(*TLVP_VAL(tp, BSSGP_IE_TAG), ns_bvci); + return bssgp_tx_fc_bvc_ack(msgb_nsei(msg), *TLVP_VAL(tp, BSSGP_IE_TAG), + ns_bvci); } /* We expect msg->l3h to point to the BSSGP header */ int gprs_bssgp_rcvmsg(struct msgb *msg, u_int16_t ns_bvci) @@ -287,7 +298,7 @@ int gprs_bssgp_rcvmsg(struct msgb *msg, u_int16_t ns_bvci) DEBUGPC(DGPRS, "BVCI=%u, cause=%s\n", bvci, bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE))); rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, - bvci, ns_bvci); + msgb_nsei(msg), bvci, ns_bvci); break; case BSSGP_PDUT_BVC_UNBLOCK: /* BSS tells us that BVC shall be unblocked */ @@ -297,7 +308,7 @@ int gprs_bssgp_rcvmsg(struct msgb *msg, u_int16_t ns_bvci) bvci = ntohs(*(u_int16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI)); DEBUGPC(DGPRS, "BVCI=%u\n", bvci); rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, - bvci, ns_bvci); + msgb_nsei(msg), bvci, ns_bvci); break; case BSSGP_PDUT_BVC_RESET: /* BSS tells us that BVC init is required */ @@ -309,7 +320,7 @@ int gprs_bssgp_rcvmsg(struct msgb *msg, u_int16_t ns_bvci) DEBUGPC(DGPRS, "BVCI=%u, cause=%s\n", bvci, bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE))); rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, - bvci, ns_bvci); + msgb_nsei(msg), bvci, ns_bvci); break; case BSSGP_PDUT_STATUS: /* Some exception has occurred */ @@ -393,5 +404,8 @@ int gprs_bssgp_tx_dl_ud(struct msgb *msg) budh->tlli = htonl(msgb_tlli(msg)); budh->pdu_type = BSSGP_PDUT_DL_UNITDATA; - return gprs_ns_sendmsg(NULL, bts->gprs.cell.bvci, msg); + msgb_nsei(msg) = bts->gprs.nse.nsei; + msgb_bvci(msg) = bts->gprs.cell.bvci; + + return gprs_ns_sendmsg(bssgp_nsi, msg); } diff --git a/openbsc/src/gprs_ns.c b/openbsc/src/gprs_ns.c index b57de1d7..6c495b01 100644 --- a/openbsc/src/gprs_ns.c +++ b/openbsc/src/gprs_ns.c @@ -1,4 +1,4 @@ -/* GPRS Networks Service (NS) messages on the Gb interface +/* GPRS Networks Service (NS) messages on the Gb interfacebvci = msgb_bvci(msg); * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */ /* (C) 2009-2010 by Harald Welte @@ -132,6 +132,19 @@ static struct gprs_nsvc *nsvc_by_nsvci(struct gprs_ns_inst *nsi, return NULL; } +/* Lookup struct gprs_nsvc based on NSVCI */ +static struct gprs_nsvc *nsvc_by_nsei(struct gprs_ns_inst *nsi, + u_int16_t nsei) +{ + struct gprs_nsvc *nsvc; + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (nsvc->nsei == nsei) + return nsvc; + } + return NULL; +} + + /* Lookup struct gprs_nsvc based on remote peer socket addr */ static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi, struct sockaddr_in *sin) @@ -184,15 +197,15 @@ static const char *gprs_ns_cause_str(enum ns_cause cause) return "undefined"; } -static int nsip_sendmsg(struct msgb *msg, struct gprs_nsvc *nsvc); +static int nsip_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg); -static int gprs_ns_tx(struct msgb *msg, struct gprs_nsvc *nsvc) +static int gprs_ns_tx(struct gprs_nsvc *nsvc, struct msgb *msg) { int ret; switch (nsvc->nsi->ll) { case GPRS_NS_LL_UDP: - ret = nsip_sendmsg(msg, nsvc); + ret = nsip_sendmsg(nsvc, msg); break; default: LOGP(DGPRS, LOGL_ERROR, "unsupported NS linklayer %u\n", nsvc->nsi->ll); @@ -215,7 +228,7 @@ static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, u_int8_t pdu_type) nsh->pdu_type = pdu_type; - return gprs_ns_tx(msg, nsvc); + return gprs_ns_tx(nsvc, msg); } #define NS_TIMER_ALIVE 3, 0 /* after 3 seconds without response, we retry */ @@ -268,14 +281,21 @@ static int gprs_ns_tx_reset_ack(struct gprs_nsvc *nsvc) msgb_tvlv_put(msg, NS_IE_VCI, 2, (u_int8_t *)&nsvci); msgb_tvlv_put(msg, NS_IE_NSEI, 2, (u_int8_t *)&nsei); - return gprs_ns_tx(msg, nsvc); + return gprs_ns_tx(nsvc, msg); } -/* Section 9.2.10: transmit side */ -int gprs_ns_sendmsg(struct gprs_nsvc *nsvc, u_int16_t bvci, - struct msgb *msg) +/* Section 9.2.10: transmit side / NS-UNITDATA-REQUEST primitive */ +int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg) { + struct gprs_nsvc *nsvc; struct gprs_ns_hdr *nsh; + u_int16_t bvci = msgb_bvci(msg); + + nsvc = nsvc_by_nsei(nsi, msgb_nsei(msg)); + if (!nsvc) { + DEBUGP(DGPRS, "Unable to resolve NSEI %u to NS-VC!\n", msgb_nsei(msg)); + return -EINVAL; + } nsh = (struct gprs_ns_hdr *) msgb_push(msg, sizeof(*nsh) + 3); if (!nsh) { @@ -288,14 +308,13 @@ int gprs_ns_sendmsg(struct gprs_nsvc *nsvc, u_int16_t bvci, nsh->data[1] = bvci >> 8; nsh->data[2] = bvci & 0xff; - return gprs_ns_tx(msg, nsvc); + return gprs_ns_tx(nsvc, msg); } /* Section 9.2.10: receive side */ -static int gprs_ns_rx_unitdata(struct msgb *msg) +static int gprs_ns_rx_unitdata(struct gprs_nsvc *nsvc, struct msgb *msg) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h; - struct gprs_nsvc *nsvc = msgb_nsvc(msg); u_int16_t bvci; /* spare octet in data[0] */ @@ -307,10 +326,9 @@ static int gprs_ns_rx_unitdata(struct msgb *msg) } /* Section 9.2.7 */ -static int gprs_ns_rx_status(struct msgb *msg) +static int gprs_ns_rx_status(struct gprs_nsvc *nsvc, struct msgb *msg) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; - struct gprs_nsvc *nsvc = msgb_nsvc(msg); struct tlv_parsed tp; u_int8_t cause; int rc; @@ -331,10 +349,9 @@ static int gprs_ns_rx_status(struct msgb *msg) } /* Section 7.3 */ -static int gprs_ns_rx_reset(struct msgb *msg) +static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; - struct gprs_nsvc *nsvc = msgb_nsvc(msg); struct tlv_parsed tp; u_int8_t *cause; u_int16_t *nsvci, *nsei; @@ -388,10 +405,10 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, return -EIO; nsvc = nsvc_create(nsi, 0xffff); nsvc->ip.bts_addr = *saddr; - rc = gprs_ns_rx_reset(msg); + rc = gprs_ns_rx_reset(nsvc, msg); return rc; } - msgb_nsvc(msg) = nsvc; + msgb_nsei(msg) = nsvc->nsei; switch (nsh->pdu_type) { case NS_PDUT_ALIVE: @@ -408,13 +425,13 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, br