Compare commits

...

19 Commits

Author SHA1 Message Date
Harald Welte
edcba4e307 use new external libosmogb (part of libosmocore.git)
This removes the libgb (GPRS NS/BSSGP) code from the openbsc.git
repository and uses the new version from libosmocore.git instead.
2012-06-17 23:34:34 +08:00
Harald Welte
b46d566559 index on master: d24566a libgb: separate header files related to spec and implementation 2012-06-17 13:13:52 +08:00
Harald Welte
d24566ac6e libgb: separate header files related to spec and implementation
like in libosmogsm, we separate between header files that are just
reflecting information in the respective specs, and header files that
related to our specific implementation.
2012-06-17 13:12:51 +08:00
Harald Welte
ce95b27b68 libgb: make sure all BSSGP functions have bssgp_ prefix
We change the minority of functions employing the gprs_bssgp_ prefix to
match with the majority without gprs_ in front.
2012-06-17 13:05:48 +08:00
Harald Welte
11461a6457 libgb/gprs: don't use log_info from libcommon anymore 2012-06-17 12:58:46 +08:00
Harald Welte
e6599ee180 libgb: prefix all NS related functions with gprs_ 2012-06-17 12:38:07 +08:00
Harald Welte
b1fd9022eb libgb: don't call directly into GMM / LLC layer
Instead of direct function calls to individual functions, we now
generate primitives (osmo_prim) and send them to one
application-provided function "bssgp_prim_cb()"
2012-06-17 12:17:22 +08:00
Harald Welte
faa70ff2c6 libgb: remove dependency of BSSGP to include sgsn/gmm internal structs 2012-06-17 09:31:16 +08:00
Harald Welte
0d5e8e0b56 update .gitignore 2012-06-17 09:05:43 +08:00
Harald Welte
68d85d5610 libgb: Remove dependency to openbsc/debug.h 2012-06-16 17:53:38 +08:00
Harald Welte
fdd8b3b40d libgb: remove dependencies to openbsc/vty.h and openbsc/gsm_data.h
Rather than using openbsc internal data/functions, we now use only
internal and libosmocore-provided ones.
2012-06-16 16:59:50 +08:00
Harald Welte
a6a20b4ff6 libgb: Use library SS_L_NS instead lf local SS_NS
which removes some further dependencies of libgb to openbsc internal
code and data.
2012-06-16 16:40:42 +08:00
Harald Welte
8645e101da libgb: move GPRS specific msgb CB definitions to separate header 2012-06-16 16:09:52 +08:00
Harald Welte
ea34a4e3a7 split libgb into a separate library for outside use
This also removes the dependency to osmo_sock() inside libcommon and
replaces it with osmo_sock_* from libosmocore
2012-06-16 15:14:00 +08:00
Harald Welte
704cb8680b gsm_data_shared: add handover related bits for osmo-bts 2012-06-16 15:02:13 +08:00
Harald Welte
67161f27d1 Allow VTY-based configuration of T3105
This timer will only be forwarded to BS11 and Ericsson Abis so far,
not to Nokia and ip.access BTS yet.
2012-06-03 16:58:18 +02:00
Harald Welte
66d360661a update year in copyright string 2012-06-03 16:58:09 +02:00
Harald Welte
f25b55ec71 abis_rsl_rcvmsg(): Fix msgb memory leak in error path
Thanks to Andreas Eversberg for spotting this one.
2012-05-31 20:22:34 +02:00
Harald Welte
c513ded578 vty / cfg_bts_tsc: TSC range is 0..7, not 0..255 2012-05-31 10:57:40 +02:00
42 changed files with 234 additions and 4185 deletions

6
openbsc/.gitignore vendored
View File

@@ -1,4 +1,5 @@
*.o
*.lo
*.a
.deps
Makefile
@@ -11,17 +12,22 @@ src/osmo-bsc_mgcp/osmo-bsc_mgcp
src/osmo-bsc/osmo-bsc
*.*~
*.sw?
.libs
#configure
aclocal.m4
autom4te.cache/
config.log
config.status
config.guess
config.sub
configure
depcomp
install-sh
missing
stamp-h1
libtool
ltmain.sh
# git-version-gen magic
.tarball-version

View File

@@ -45,6 +45,7 @@ PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.2)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.1.0)
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.1)
found_libgtp=yes
PKG_CHECK_MODULES(LIBGTP, libgtp, , found_libgtp=no)
@@ -128,7 +129,6 @@ AC_OUTPUT(
src/osmo-bsc_mgcp/Makefile
src/ipaccess/Makefile
src/utils/Makefile
src/libgb/Makefile
src/gprs/Makefile
tests/Makefile
tests/atlocal

View File

@@ -7,9 +7,9 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \
system_information.h handover.h mgcp_internal.h \
vty.h socket.h e1_config.h trau_upqueue.h token_auth.h \
handover_decision.h rrlp.h control_if.h \
crc24.h gprs_bssgp.h gprs_llc.h gprs_ns.h gprs_gmm.h \
crc24.h gprs_llc.h gprs_gmm.h \
gb_proxy.h gprs_sgsn.h gsm_04_08_gprs.h sgsn.h \
gprs_ns_frgre.h auth.h osmo_msc.h bsc_msc.h bsc_nat.h \
auth.h osmo_msc.h bsc_msc.h bsc_nat.h \
osmo_bsc_rf.h osmo_bsc.h network_listen.h bsc_nat_sccp.h \
osmo_msc_data.h osmo_bsc_grace.h sms_queue.h abis_om2000.h \
bss.h gsm_data_shared.h control_cmd.h ipaccess.h mncc_int.h

View File

@@ -40,8 +40,6 @@ enum {
#define BSC_CTX_SUBSCR 1
#define BSC_CTX_BTS 2
#define BSC_CTX_SCCP 3
#define BSC_CTX_NSVC 4
#define BSC_CTX_BVC 5
/* target */

View File

@@ -4,7 +4,7 @@
#include <osmocom/core/msgb.h>
#include <openbsc/gprs_ns.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/vty/command.h>
struct gbproxy_config {

View File

@@ -1,250 +0,0 @@
#ifndef _GPRS_BSSGP_H
#define _GPRS_BSSGP_H
#include <stdint.h>
/*! \brief Fixed BVCI definitions (Section 5.4.1) */
#define BVCI_SIGNALLING 0x0000
#define BVCI_PTM 0x0001
/*! \brief BSSGP PDU types (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,
};
/*! \brief BSSGP User-Data header (Section 10.2.1 and 10.2.2) */
struct bssgp_ud_hdr {
uint8_t pdu_type; /*!< BSSGP PDU type */
uint32_t tlli; /*!< Temporary Link-Local Identifier */
uint8_t qos_profile[3]; /*!< QoS profile */
uint8_t data[0]; /* optional/conditional IEs as TLVs */
} __attribute__((packed));
/*! \brief BSSGP normal header */
struct bssgp_normal_hdr {
uint8_t pdu_type; /*!< BSSGP PDU type */
uint8_t data[0]; /*!< optional/conditional IEs as TLVs */
};
/*! \brief BSSGP Information Element Identifiers */
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,
};
/*! \brief Cause coding (Section 11.3.8 / Table 11.10) */
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,
};
/* Our implementation */
/* gprs_bssgp_util.c */
extern struct gprs_ns_inst *bssgp_nsi;
struct msgb *bssgp_msgb_alloc(void);
const char *bssgp_cause_str(enum gprs_bssgp_cause cause);
/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */
int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei,
uint16_t bvci, uint16_t ns_bvci);
/* Chapter 10.4.14: Status */
int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg);
/* gprs_bssgp.c */
#define BVC_S_BLOCKED 0x0001
/* The per-BTS context that we keep on the SGSN side of the BSSGP link */
struct bssgp_bvc_ctx {
struct llist_head list;
struct gprs_ra_id ra_id; /*!< parsed RA ID of the remote BTS */
uint16_t cell_id; /*!< Cell ID of the remote BTS */
/* NSEI and BVCI of underlying Gb link. Together they
* uniquely identify a link to a BTS (5.4.4) */
uint16_t bvci;
uint16_t nsei;
uint32_t state;
struct rate_ctr_group *ctrg;
/* we might want to add this as a shortcut later, avoiding the NSVC
* lookup for every packet, similar to a routing cache */
//struct gprs_nsvc *nsvc;
};
extern struct llist_head bssgp_bvc_ctxts;
/* Find a BTS Context based on parsed RA ID and Cell ID */
struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid);
/* Find a BTS context based on BVCI+NSEI tuple */
struct bssgp_bvc_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei);
#define BVC_F_BLOCKED 0x0001
enum bssgp_ctr {
BSSGP_CTR_PKTS_IN,
BSSGP_CTR_PKTS_OUT,
BSSGP_CTR_BYTES_IN,
BSSGP_CTR_BYTES_OUT,
BSSGP_CTR_BLOCKED,
BSSGP_CTR_DISCARDED,
};
#include <osmocom/gsm/tlv.h>
/* BSSGP-UL-UNITDATA.ind */
int gprs_bssgp_rcvmsg(struct msgb *msg);
/* BSSGP-DL-UNITDATA.req */
struct sgsn_mm_ctx;
int gprs_bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx);
uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf);
int bssgp_create_cell_id(uint8_t *buf, const struct gprs_ra_id *raid,
uint16_t cid);
/* Wrapper around TLV parser to parse BSSGP IEs */
static inline int bssgp_tlv_parse(struct tlv_parsed *tp, uint8_t *buf, int len)
{
return tlv_parse(tp, &tvlv_att_def, buf, len, 0, 0);
}
/*! \brief BSSGP Paging mode */
enum bssgp_paging_mode {
BSSGP_PAGING_PS,
BSSGP_PAGING_CS,
};
/*! \brief BSSGP Paging scope */
enum bssgp_paging_scope {
BSSGP_PAGING_BSS_AREA, /*!< all cells in BSS */
BSSGP_PAGING_LOCATION_AREA, /*!< all cells in LA */
BSSGP_PAGING_ROUTEING_AREA, /*!< all cells in RA */
BSSGP_PAGING_BVCI, /*!< one cell */
};
/*! \brief BSSGP paging information */
struct bssgp_paging_info {
enum bssgp_paging_mode mode; /*!< CS or PS paging */
enum bssgp_paging_scope scope; /*!< bssgp_paging_scope */
struct gprs_ra_id raid; /*!< RA Identifier */
uint16_t bvci; /*!< BVCI */
char *imsi; /*!< IMSI, if any */
uint32_t *ptmsi; /*!< P-TMSI, if any */
uint16_t drx_params; /*!< DRX parameters */
uint8_t qos[3]; /*!< QoS parameters */
};
/* Send a single GMM-PAGING.req to a given NSEI/NS-BVCI */
int gprs_bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci,
struct bssgp_paging_info *pinfo);
/* gprs_bssgp_vty.c */
int gprs_bssgp_vty_init(void);
#endif /* _GPRS_BSSGP_H */

View File

@@ -1,250 +0,0 @@
#ifndef _GPRS_NS_H
#define _GPRS_NS_H
#include <stdint.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) */
/*! \addtogroup libgb
* @{
*/
/*! \file gprs_ns.h */
/*! \brief Common header of GPRS NS */
struct gprs_ns_hdr {
uint8_t pdu_type; /*!< NS PDU type */
uint8_t data[0]; /*!< variable-length payload */
} __attribute__((packed));
/*! \brief NS PDU Type (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 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,
};
/*! \brief NS Control IE (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 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,
};
/*! \brief NS Cause (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,
/* 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,
};
/* Our Implementation */
#include <netinet/in.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/select.h>
#define NS_TIMERS_COUNT 7
#define NS_TIMERS "(tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries)"
#define NS_TIMERS_HELP \
"(un)blocking Timer (Tns-block) timeout\n" \
"(un)blocking Timer (Tns-block) number of retries\n" \
"Reset Timer (Tns-reset) timeout\n" \
"Reset Timer (Tns-reset) number of retries\n" \
"Test Timer (Tns-test) timeout\n" \
"Alive Timer (Tns-alive) timeout\n" \
"Alive Timer (Tns-alive) number of retries\n"
enum ns_timeout {
NS_TOUT_TNS_BLOCK,
NS_TOUT_TNS_BLOCK_RETRIES,
NS_TOUT_TNS_RESET,
NS_TOUT_TNS_RESET_RETRIES,
NS_TOUT_TNS_TEST,
NS_TOUT_TNS_ALIVE,
NS_TOUT_TNS_ALIVE_RETRIES,
};
#define NSE_S_BLOCKED 0x0001
#define NSE_S_ALIVE 0x0002
/*! \brief Osmocom NS link layer types */
enum gprs_ns_ll {
GPRS_NS_LL_UDP, /*!< NS/UDP/IP */
GPRS_NS_LL_E1, /*!< NS/E1 */
GPRS_NS_LL_FR_GRE, /*!< NS/FR/GRE/IP */
};
/*! \brief Osmoco NS events */
enum gprs_ns_evt {
GPRS_NS_EVT_UNIT_DATA,
};
struct gprs_nsvc;
/*! \brief Osmocom GPRS callback function type */
typedef int gprs_ns_cb_t(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
struct msgb *msg, uint16_t bvci);
/*! \brief An instance of the NS protocol stack */
struct gprs_ns_inst {
/*! \brief callback to the user for incoming UNIT DATA IND */
gprs_ns_cb_t *cb;
/*! \brief linked lists of all NSVC in this instance */
struct llist_head gprs_nsvcs;
/*! \brief a NSVC object that's needed to deal with packets for
* unknown NSVC */
struct gprs_nsvc *unknown_nsvc;
uint16_t timeout[NS_TIMERS_COUNT];
/*! \brief NS-over-IP specific bits */
struct {
struct osmo_fd fd;
uint32_t local_ip;
uint16_t local_port;
} nsip;
/*! \brief NS-over-FR-over-GRE-over-IP specific bits */
struct {
struct osmo_fd fd;
uint32_t local_ip;
unsigned int enabled:1;
} frgre;
};
enum nsvc_timer_mode {
/* standard timers */
NSVC_TIMER_TNS_TEST,
NSVC_TIMER_TNS_ALIVE,
NSVC_TIMER_TNS_RESET,
_NSVC_TIMER_NR,
};
/*! \brief Structure representing a single NS-VC */
struct gprs_nsvc {
/*! \brief list of NS-VCs within NS Instance */
struct llist_head list;
/*! \brief pointer to NS Instance */
struct gprs_ns_inst *nsi;
uint16_t nsei; /*! \brief end-to-end significance */
uint16_t nsvci; /*! \brief uniquely identifies NS-VC at SGSN */
uint32_t state;
uint32_t remote_state;
struct osmo_timer_list timer;
enum nsvc_timer_mode timer_mode;
int alive_retries;
unsigned int remote_end_is_sgsn:1;
unsigned int persistent:1;
struct rate_ctr_group *ctrg;
/*! \brief which link-layer are we based on? */
enum gprs_ns_ll ll;
union {
struct {
struct sockaddr_in bts_addr;
} ip;
struct {
struct sockaddr_in bts_addr;
} frgre;
};
};
/* 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);
/* Listen for incoming GPRS packets via NS/UDP */
int gprs_ns_nsip_listen(struct gprs_ns_inst *nsi);
struct sockaddr_in;
/* main function for higher layers (BSSGP) to send NS messages */
int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg);
int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause);
int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause);
int gprs_ns_tx_unblock(struct gprs_nsvc *nsvc);
/* Listen for incoming GPRS packets via NS/FR/GRE */
int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi);
/* Establish a connection (from the BSS) to the SGSN */
struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi,
struct sockaddr_in *dest, uint16_t nsei,
uint16_t nsvci);
struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci);
void nsvc_delete(struct gprs_nsvc *nsvc);
struct gprs_nsvc *nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei);
struct gprs_nsvc *nsvc_by_nsvci(struct gprs_ns_inst *nsi, uint16_t nsvci);
/* Initiate a RESET procedure (including timer start, ...)*/
void gprs_nsvc_reset(struct gprs_nsvc *nsvc, uint8_t cause);
/* Add NS-specific VTY stuff */
int gprs_ns_vty_init(struct gprs_ns_inst *nsi);
#define NS_ALLOC_SIZE 2048
#define NS_ALLOC_HEADROOM 20
static inline struct msgb *gprs_ns_msgb_alloc(void)
{
return msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM, "GPRS/NS");
}
/*! }@ */
#endif

View File

@@ -1,6 +0,0 @@
#ifndef _GPRS_NS_FRGRE_H
#define _GPRS_NS_FRGRE_H
int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg);
#endif

View File

@@ -12,31 +12,6 @@
struct mncc_sock_state;
/* the data structure stored in msgb->cb for openbsc apps */
struct openbsc_msgb_cb {
unsigned char *bssgph;
unsigned char *llch;
/* Cell Identifier */
unsigned char *bssgp_cell_id;
/* Identifiers of a BTS, equal to 'struct bssgp_bts_ctx' */
uint16_t nsei;
uint16_t bvci;
/* Identifier of a MS (inside BTS), equal to 'struct sgsn_mm_ctx' */
uint32_t tlli;
} __attribute__((packed));
#define OBSC_MSGB_CB(__msgb) ((struct openbsc_msgb_cb *)&((__msgb)->cb[0]))
#define msgb_tlli(__x) OBSC_MSGB_CB(__x)->tlli
#define msgb_nsei(__x) OBSC_MSGB_CB(__x)->nsei
#define msgb_bvci(__x) OBSC_MSGB_CB(__x)->bvci
#define msgb_gmmh(__x) (__x)->l3h
#define msgb_bssgph(__x) OBSC_MSGB_CB(__x)->bssgph
#define msgb_bssgp_len(__x) ((__x)->tail - (uint8_t *)msgb_bssgph(__x))
#define msgb_bcid(__x) OBSC_MSGB_CB(__x)->bssgp_cell_id
#define msgb_llch(__x) OBSC_MSGB_CB(__x)->llch
#define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3]
enum gsm_security_event {
@@ -215,6 +190,7 @@ enum gsm_auth_policy {
};
#define GSM_T3101_DEFAULT 10
#define GSM_T3105_DEFAULT 40
#define GSM_T3113_DEFAULT 60
struct gsm_network {

View File

@@ -255,6 +255,14 @@ struct gsm_lchan {
/* BTS-side ciphering state (rx only, bi-directional, ...) */
uint8_t ciph_state;
uint8_t loopback;
struct {
uint8_t active;
uint8_t ref;
/* T3105: PHYS INF retransmission */
struct osmo_timer_list t3105;
/* counts up to Ny1 */
unsigned int phys_info_count;
} ho;
#endif
};

View File

@@ -4,7 +4,7 @@
#include <osmocom/core/msgb.h>
#include <openbsc/gprs_ns.h>
#include <osmocom/gprs/gprs_ns.h>
#include <openbsc/gprs_sgsn.h>
struct sgsn_config {

View File

@@ -41,7 +41,6 @@ enum signal_subsystems {
SS_SUBSCR,
SS_SCALL,
SS_CHALLOC,
SS_NS,
SS_IPAC_NWL,
SS_RF,
SS_MSC,
@@ -215,18 +214,6 @@ struct lchan_signal_data {
struct gsm_meas_rep *mr;
};
enum signal_ns {
S_NS_RESET,
S_NS_BLOCK,
S_NS_UNBLOCK,
S_NS_ALIVE_EXP, /* Tns-alive expired more than N times */
};
struct ns_signal_data {
struct gprs_nsvc *nsvc;
uint8_t cause;
};
/* MSC signals */
enum signal_msc {
S_MSC_LOST,

View File

@@ -26,8 +26,6 @@ enum bsc_vty_node {
MGCP_NODE,
GBPROXY_NODE,
SGSN_NODE,
NS_NODE,
BSSGP_NODE,
OML_NODE,
NAT_NODE,
NAT_BSC_NODE,

View File

@@ -2,7 +2,7 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS)
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS)
SUBDIRS = libcommon libmgcp libbsc libmsc libtrau libctrl osmo-nitb osmo-bsc_mgcp utils ipaccess libgb gprs
SUBDIRS = libcommon libmgcp libbsc libmsc libtrau libctrl osmo-nitb osmo-bsc_mgcp utils ipaccess gprs
# Conditional modules
if BUILD_NAT

View File

@@ -1,8 +1,9 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS)
OSMO_LIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS)
$(LIBOSMOABIS_CFLAGS) $(LIBOSMOGB_CFLAGS) $(COVERAGE_CFLAGS)
OSMO_LIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \
$(LIBOSMOGB_LIBS)
noinst_HEADERS = gprs_sndcp.h
@@ -13,13 +14,11 @@ bin_PROGRAMS = osmo-gbproxy
endif
osmo_gbproxy_SOURCES = gb_proxy.c gb_proxy_main.c gb_proxy_vty.c
osmo_gbproxy_LDADD = $(top_builddir)/src/libgb/libgb.a \
$(top_builddir)/src/libcommon/libcommon.a \
osmo_gbproxy_LDADD = $(top_builddir)/src/libcommon/libcommon.a \
$(OSMO_LIBS)
osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c gprs_sndcp.c gprs_sndcp_vty.c \
sgsn_main.c sgsn_vty.c sgsn_libgtp.c \
gprs_llc.c gprs_llc_vty.c crc24.c
osmo_sgsn_LDADD = $(top_builddir)/src/libgb/libgb.a \
$(top_builddir)/src/libcommon/libcommon.a \
osmo_sgsn_LDADD = $(top_builddir)/src/libcommon/libcommon.a \
-lgtp $(OSMO_LIBS)

View File

@@ -32,10 +32,11 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <openbsc/signal.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_ns.h>
#include <openbsc/gprs_bssgp.h>
#include <openbsc/gb_proxy.h>
struct gbprox_peer {
@@ -121,7 +122,7 @@ static void peer_free(struct gbprox_peer *peer)
/* FIXME: this needs to go to libosmocore/msgb.c */
static struct msgb *msgb_copy(const struct msgb *msg, const char *name)
{
struct openbsc_msgb_cb *old_cb, *new_cb;
struct libgb_msgb_cb *old_cb, *new_cb;
struct msgb *new_msg;
new_msg = msgb_alloc(msg->data_len, name);
@@ -143,8 +144,8 @@ static struct msgb *msgb_copy(const struct msgb *msg, const char *name)
new_msg->l4h = new_msg->_data + (msg->l4h - msg->_data);
/* copy GB specific data */
old_cb = OBSC_MSGB_CB(msg);
new_cb = OBSC_MSGB_CB(new_msg);
old_cb = LIBGB_MSGB_CB(msg);
new_cb = LIBGB_MSGB_CB(new_msg);
new_cb->bssgph = new_msg->_data + (old_cb->bssgph - msg->_data);
new_cb->llch = new_msg->_data + (old_cb->llch - msg->_data);
@@ -245,6 +246,11 @@ static int gbprox_relay2bvci(struct msgb *msg, uint16_t ptp_bvci,
return gbprox_relay2peer(msg, peer, ns_bvci);
}
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
return 0;
}
/* Receive an incoming signalling message from a BSS-side NS-VC */
static int gbprox_rx_sig_from_bss(struct msgb *msg, struct gprs_nsvc *nsvc,
uint16_t ns_bvci)
@@ -595,7 +601,7 @@ int gbprox_signal(unsigned int subsys, unsigned int signal,
struct gprs_nsvc *nsvc = nssd->nsvc;
struct gbprox_peer *peer;
if (subsys != SS_NS)
if (subsys != SS_L_NS)
return 0;
if (signal == S_NS_RESET && nsvc->nsei == gbcfg.nsip_sgsn_nsei) {
@@ -609,8 +615,8 @@ int gbprox_signal(unsigned int subsys, unsigned int signal,
if (signal == S_NS_ALIVE_EXP && nsvc->remote_end_is_sgsn) {
LOGP(DGPRS, LOGL_NOTICE, "Tns alive expired too often, "
"re-starting RESET procedure\n");
nsip_connect(nsvc->nsi, &nsvc->ip.bts_addr, nsvc->nsei,
nsvc->nsvci);
gprs_ns_nsip_connect(nsvc->nsi, &nsvc->ip.bts_addr,
nsvc->nsei, nsvc->nsvci);
}
if (!nsvc->remote_end_is_sgsn) {

View File

@@ -37,10 +37,11 @@
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <openbsc/signal.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_ns.h>
#include <openbsc/gprs_bssgp.h>
#include <openbsc/vty.h>
#include <openbsc/gb_proxy.h>
@@ -199,6 +200,31 @@ static struct vty_app_info vty_info = {
.is_config_node = bsc_vty_is_config_node,
};
/* default categories */
static struct log_info_cat gprs_categories[] = {
[DGPRS] = {
.name = "DGPRS",
.description = "GPRS Packet Service",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DNS] = {
.name = "DNS",
.description = "GPRS Network Service (NS)",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DBSSGP] = {
.name = "DBSSGP",
.description = "GPRS BSS Gateway Protocol (BSSGP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
};
static const struct log_info gprs_log_info = {
.filter_fn = gprs_log_filter_fn,
.cat = gprs_categories,
.num_cat = ARRAY_SIZE(gprs_categories),
};
int main(int argc, char **argv)
{
struct gsm_network dummy_network;
@@ -213,11 +239,11 @@ int main(int argc, char **argv)
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
osmo_init_logging(&log_info);
osmo_init_logging(&gprs_log_info);
vty_info.copyright = openbsc_copyright;
vty_init(&vty_info);
logging_vty_add_cmds(&log_info);
logging_vty_add_cmds(&gprs_log_info);
gbproxy_vty_init();
handle_options(argc, argv);
@@ -228,14 +254,16 @@ int main(int argc, char **argv)
if (rc < 0)
exit(1);
bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb);
bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb, tall_bsc_ctx);
if (!bssgp_nsi) {
LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
exit(1);
}
gbcfg.nsi = bssgp_nsi;
gprs_ns_vty_init(bssgp_nsi);
osmo_signal_register_handler(SS_NS, &gbprox_signal, NULL);
gprs_ns_set_log_ss(DNS);
bssgp_set_log_ss(DBSSGP);
osmo_signal_register_handler(SS_L_NS, &gbprox_signal, NULL);
rc = gbproxy_parse_config(config_file, &gbcfg);
if (rc < 0) {
@@ -243,7 +271,7 @@ int main(int argc, char **argv)
exit(2);
}
if (!nsvc_by_nsei(gbcfg.nsi, gbcfg.nsip_sgsn_nsei)) {
if (!gprs_nsvc_by_nsei(gbcfg.nsi, gbcfg.nsip_sgsn_nsei)) {
LOGP(DGPRS, LOGL_FATAL, "You cannot proxy to NSEI %u "
"without creating that NSEI before\n",
gbcfg.nsip_sgsn_nsei);

View File

@@ -24,9 +24,10 @@
#include <osmocom/core/talloc.h>
#include <osmocom/gprs/gprs_ns.h>
#include <openbsc/debug.h>
#include <openbsc/gb_proxy.h>
#include <openbsc/gprs_ns.h>
#include <openbsc/vty.h>
#include <osmocom/vty/command.h>

View File

@@ -38,6 +38,8 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_subscriber.h>
@@ -45,7 +47,6 @@
#include <openbsc/gsm_04_08_gprs.h>
#include <openbsc/paging.h>
#include <openbsc/transaction.h>
#include <openbsc/gprs_bssgp.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>

View File

@@ -26,16 +26,35 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <openbsc/gsm_data.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/gprs_bssgp.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/crc24.h>
#include <openbsc/sgsn.h>
/* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU
* to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */
static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx)
{
struct bssgp_dl_ud_par dup;
const uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x20 };
dup.tlli = NULL;
dup.imsi = mmctx->imsi;
dup.drx_parms = mmctx->drx_parms;
dup.ms_ra_cap.len = mmctx->ms_radio_access_capa.len;
dup.ms_ra_cap.v = mmctx->ms_radio_access_capa.buf;
memcpy(&dup.qos_profile, qos_profile_default,
sizeof(qos_profile_default));
return bssgp_tx_dl_ud(msg, 1000, &dup);
}
/* Section 8.9.9 LLC layer parameter default values */
static const struct gprs_llc_params llc_default_params[] = {
[1] = {
@@ -308,7 +327,7 @@ int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command,
/* Identifiers passed down: (BVCI, NSEI) */
/* Send BSSGP-DL-UNITDATA.req */
return gprs_bssgp_tx_dl_ud(msg, NULL);
return _bssgp_tx_dl_ud(msg, NULL);
}
/* Send XID response to LLE */
@@ -420,7 +439,7 @@ int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command,
/* Identifiers passed down: (BVCI, NSEI) */
/* Send BSSGP-DL-UNITDATA.req */
return gprs_bssgp_tx_dl_ud(msg, mmctx);
return _bssgp_tx_dl_ud(msg, mmctx);
}
/* According to 6.4.1.6 / Figure 11 */

View File

@@ -25,11 +25,12 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_ns.h>
#include <openbsc/gprs_bssgp.h>
#include <openbsc/sgsn.h>
#include <openbsc/gsm_04_08_gprs.h>
#include <openbsc/gprs_gmm.h>

View File

@@ -27,10 +27,10 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <openbsc/gsm_data.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_bssgp.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/sgsn.h>

View File

@@ -36,13 +36,13 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <openbsc/gsm_04_08_gprs.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <openbsc/gsm_04_08_gprs.h>
#include <openbsc/signal.h>
#include <openbsc/debug.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/gprs_bssgp.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
@@ -446,7 +446,7 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
pinfo.ptmsi = &mm->p_tmsi;
pinfo.drx_params = mm->drx_parms;
pinfo.qos[0] = 0; // FIXME
rc = gprs_bssgp_tx_paging(mm->nsei, 0, &pinfo);
rc = bssgp_tx_paging(mm->nsei, 0, &pinfo);
rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PAGING_PS]);
/* FIXME: queue the packet we received from GTP */
break;

View File

@@ -38,6 +38,9 @@
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/logging.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
@@ -45,9 +48,8 @@
#include <openbsc/debug.h>
#include <openbsc/vty.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_ns.h>
#include <openbsc/gprs_bssgp.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/gprs_gmm.h>
#include <gtp.h>
@@ -86,7 +88,7 @@ static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
switch (event) {
case GPRS_NS_EVT_UNIT_DATA:
/* hand the message into the BSSGP implementation */
rc = gprs_bssgp_rcvmsg(msg);
rc = bssgp_rcvmsg(msg);
break;
default:
LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
@@ -98,6 +100,34 @@ static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
return rc;
}
/* call-back function for the BSSGP protocol */
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
struct osmo_bssgp_prim *bp;
bp = container_of(oph, struct osmo_bssgp_prim, oph);
switch (oph->sap) {
case SAP_BSSGP_LL:
switch (oph->primitive) {
case PRIM_BSSGP_UL_UD:
return gprs_llc_rcvmsg(oph->msg, bp->tp);
}
break;
case SAP_BSSGP_GMM:
switch (oph->primitive) {
case PRIM_BSSGP_GMM_SUSPEND:
return gprs_gmm_rx_suspend(bp->ra_id, bp->tlli);
case PRIM_BSSGP_GMM_RESUME:
return gprs_gmm_rx_resume(bp->ra_id, bp->tlli,
*bp->u.resume.suspend_ref);
}
break;
case SAP_BSSGP_NM:
break;
}
return 0;
}
static void signal_handler(int signal)
{
fprintf(stdout, "signal %u received\n", signal);
@@ -197,6 +227,70 @@ static void handle_options(int argc, char **argv)
}
}
/* default categories */
static struct log_info_cat gprs_categories[] = {
[DMM] = {
.name = "DMM",
.description = "Layer3 Mobility Management (MM)",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DSMS] = {
.name = "DSMS",
.description = "Layer3 Short Message Service (SMS)",
.color = "\033[1;37m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DPAG] = {
.name = "DPAG",
.description = "Paging Subsystem",
.color = "\033[1;38m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DMEAS] = {
.name = "DMEAS",
.description = "Radio Measurement Processing",
.enabled = 0, .loglevel = LOGL_NOTICE,
},
[DREF] = {
.name = "DREF",
.description = "Reference Counting",
.enabled = 0, .loglevel = LOGL_NOTICE,
},
[DGPRS] = {
.name = "DGPRS",
.description = "GPRS Packet Service",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DNS] = {
.name = "DNS",
.description = "GPRS Network Service (NS)",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DBSSGP] = {
.name = "DBSSGP",
.description = "GPRS BSS Gateway Protocol (BSSGP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DLLC] = {
.name = "DLLC",
.description = "GPRS Logical Link Control Protocol (LLC)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DSNDCP] = {
.name = "DSNDCP",
.description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
};
static const struct log_info gprs_log_info = {
.filter_fn = gprs_log_filter_fn,
.cat = gprs_categories,
.num_cat = ARRAY_SIZE(gprs_categories),
};
int main(int argc, char **argv)
{
struct gsm_network dummy_network;
@@ -211,11 +305,11 @@ int main(int argc, char **argv)
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
osmo_init_logging(&log_info);
osmo_init_logging(&gprs_log_info);
vty_info.copyright = openbsc_copyright;
vty_init(&vty_info);
logging_vty_add_cmds(&log_info);
logging_vty_add_cmds(&gprs_log_info);
sgsn_vty_init();
handle_options(argc, argv);
@@ -225,7 +319,10 @@ int main(int argc, char **argv)
if (rc < 0)
exit(1);
sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb);
gprs_ns_set_log_ss(DNS);
bssgp_set_log_ss(DBSSGP);
sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_bsc_ctx);
if (!sgsn_nsi) {
LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
exit(1);
@@ -235,10 +332,10 @@ int main(int argc, char **argv)
gprs_llc_init("/usr/local/lib/osmocom/crypt/");
gprs_ns_vty_init(bssgp_nsi);
gprs_bssgp_vty_init();
bssgp_vty_init();
gprs_llc_vty_init();
gprs_sndcp_vty_init();
/* FIXME: register signal handler for SS_NS */
/* FIXME: register signal handler for SS_L_NS */
rc = sgsn_parse_config(sgsn_inst.config_file, &sgsn_inst.cfg);
if (rc < 0) {

View File

@@ -28,7 +28,7 @@
#include <openbsc/debug.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_ns.h>
#include <osmocom/gprs/gprs_ns.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/vty.h>
#include <openbsc/gsm_04_08_gprs.h>

View File

@@ -1186,7 +1186,7 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts)
msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01);
break;
case GSM_PCHAN_CCCH_SDCCH4:
msgb_tv_put(msg, OM2K_DEI_T3105, 0x04);
msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10);
msgb_tv_put(msg, OM2K_DEI_NY1, 35);
msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06);
msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0);
@@ -1200,7 +1200,7 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts)
sizeof(icm_bound_params), icm_bound_params);
break;
case GSM_PCHAN_SDCCH8_SACCH8C:
msgb_tv_put(msg, OM2K_DEI_T3105, 0x04);
msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10);
msgb_tv_put(msg, OM2K_DEI_NY1, 35);
msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0);
msgb_tv_put(msg, OM2K_DEI_TSC, ts->trx->bts->tsc);
@@ -1210,7 +1210,7 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts)
sizeof(icm_bound_params), icm_bound_params);
break;
default:
msgb_tv_put(msg, OM2K_DEI_T3105, 0x04);
msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10);
msgb_tv_put(msg, OM2K_DEI_NY1, 35);
msgb_tv_put(msg, OM2K_DEI_TSC, ts->trx->bts->tsc);
/* Disable RF RESOURCE INDICATION on idle channels */

View File

@@ -1907,6 +1907,7 @@ int abis_rsl_rcvmsg(struct msgb *msg)
if (msgb_l2len(msg) < sizeof(*rslh)) {
DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg));
msgb_free(msg);
return -1;
}
@@ -1935,7 +1936,7 @@ int abis_rsl_rcvmsg(struct msgb *msg)
default:
LOGP(DRSL, LOGL_NOTICE, "unknown RSL message discriminator "
"0x%02x\n", rslh->msg_discr);
return -EINVAL;
rc = -EINVAL;
}
msgb_free(msg);
return rc;

View File

@@ -41,7 +41,7 @@
#include <openbsc/db.h>
#include <osmocom/core/talloc.h>
#include <openbsc/vty.h>
#include <openbsc/gprs_ns.h>
#include <osmocom/gprs/gprs_ns.h>
#include <openbsc/system_information.h>
#include <openbsc/debug.h>
#include <openbsc/paging.h>
@@ -1355,7 +1355,7 @@ DEFUN(cfg_net_pag_any_tch,
DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT.\n")
DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.\n")
DECLARE_TIMER(3105, "Currently not used.\n")
DECLARE_TIMER(3105, "Set the timer for repetition of PHYSICAL INFORMATION.\n")
DECLARE_TIMER(3107, "Currently not used.\n")
DECLARE_TIMER(3109, "Currently not used.\n")
DECLARE_TIMER(3111, "Set the RSL timeout to wait before releasing the RF Channel.\n")
@@ -1503,17 +1503,12 @@ DEFUN(cfg_bts_lac,
DEFUN(cfg_bts_tsc,
cfg_bts_tsc_cmd,
"training_sequence_code <0-255>",
"training_sequence_code <0-7>",
"Set the Training Sequence Code (TSC) of this BTS\n")
{
struct gsm_bts *bts = vty->index;
int tsc = atoi(argv[0]);
if (tsc < 0 || tsc > 0xff) {
vty_out(vty, "%% TSC %d is not in the valid range (0-255)%s",
tsc, VTY_NEWLINE);
return CMD_WARNING;
}
bts->tsc = tsc;
return CMD_SUCCESS;

View File

@@ -541,13 +541,13 @@ static uint8_t fu_config_template[] = {
/* ID = 0x02 (T3105_F) */
/* length = 2 */
/* [16] */
0x00, 0x28,
0x00, 0x28, /* FIXME: use net->T3105 */
0x50, 0x02,
/* ID = 0x10 (T3105_D) */
/* length = 2 */
/* [20] */
0x00, 0x28,
0x00, 0x28, /* FIXME: use net->T3105 */
0x43, 0x05,
/* ID = 0x03 (Interference band limits) */

View File

@@ -357,6 +357,9 @@ static void patch_nm_tables(struct gsm_bts *bts)
uint8_t arfcn_low = bts->c0->arfcn & 0xff;
uint8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f;
/* T3105 attribute in units of 10ms */
bs11_attr_bts[2] = bts->network->T3105 / 10;
/* patch ARFCN into BTS Attributes */
bs11_attr_bts[69] &= 0xf0;
bs11_attr_bts[69] |= arfcn_high;

View File

@@ -20,7 +20,7 @@
#include "../../bscconfig.h"
const char *openbsc_copyright =
"Copyright (C) 2008-2010 Harald Welte, Holger Freyther\r\n"
"Copyright (C) 2008-2012 Harald Welte, Holger Freyther\r\n"
"Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n"
"Dieter Spaar, Andreas Eversberg, Sylvain Munaut\r\n\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"

View File

@@ -147,8 +147,6 @@ gDEFUN(ournode_exit,
case MGCP_NODE:
case GBPROXY_NODE:
case SGSN_NODE:
case NS_NODE:
case BSSGP_NODE:
case NAT_NODE:
vty->node = CONFIG_NODE;
vty->index = NULL;
@@ -191,7 +189,6 @@ gDEFUN(ournode_end,
case TRUNK_NODE:
case GBPROXY_NODE:
case SGSN_NODE:
case NS_NODE:
case VTY_NODE:
case NAT_NODE:
case NAT_BSC_NODE:

View File

@@ -30,6 +30,7 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/gprs/gprs_msgb.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/debug.h>
@@ -173,8 +174,8 @@ static int filter_fn(const struct log_context *ctx,
struct log_target *tar)
{
struct gsm_subscriber *subscr = ctx->ctx[BSC_CTX_SUBSCR];
const struct gprs_nsvc *nsvc = ctx->ctx[BSC_CTX_NSVC];
const struct gprs_nsvc *bvc = ctx->ctx[BSC_CTX_BVC];
const struct gprs_nsvc *nsvc = ctx->ctx[GPRS_CTX_NSVC];
const struct gprs_nsvc *bvc = ctx->ctx[GPRS_CTX_BVC];
if ((tar->filter_map & (1 << FLT_IMSI)) != 0
&& subscr && strcmp(subscr->imsi, tar->filter_data[FLT_IMSI]) == 0)
@@ -210,25 +211,3 @@ void log_set_imsi_filter(struct log_target *target, const char *imsi)
target->filter_data[FLT_IMSI] = NULL;
}
}
void log_set_nsvc_filter(struct log_target *target, struct gprs_nsvc *nsvc)
{
if (nsvc) {
target->filter_map |= (1 << FLT_NSVC);
target->filter_data[FLT_NSVC] = nsvc;
} else if (target->filter_data[FLT_NSVC]) {
target->filter_map = ~(1 << FLT_NSVC);
target->filter_data[FLT_NSVC] = NULL;
}
}
void log_set_bvc_filter(struct log_target *target, struct bssgp_bvc_ctx *bctx)
{
if (bctx) {
target->filter_map |= (1 << FLT_BVC);
target->filter_data[FLT_BVC] = bctx;
} else if (target->filter_data[FLT_NSVC]) {
target->filter_map = ~(1 << FLT_BVC);
target->filter_data[FLT_BVC] = NULL;
}
}

View File

@@ -94,6 +94,7 @@ struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_cod
net->num_bts = 0;
net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED;
net->T3101 = GSM_T3101_DEFAULT;
net->T3105 = GSM_T3105_DEFAULT;
net->T3113 = GSM_T3113_DEFAULT;
/* FIXME: initialize all other timers! */

View File

@@ -5,5 +5,5 @@ AM_CFLAGS=-Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) \
noinst_LIBRARIES = libgb.a
libgb_a_SOURCES = gprs_ns.c gprs_ns_frgre.c gprs_ns_vty.c \
gprs_bssgp.c gprs_bssgp_util.c gprs_bssgp_vty.c gprs_bssgp_bss.c
#gprs_llc.c gprs_llc_vty.c crc24.c
gprs_bssgp.c gprs_bssgp_util.c gprs_bssgp_vty.c \
gprs_bssgp_bss.c common_vty.c

View File

@@ -1,857 +0,0 @@
/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* TODO:
* o properly count incoming BVC-RESET packets in counter group
* o set log context as early as possible for outgoing packets
*/
#include <errno.h>
#include <stdint.h>
#include <netinet/in.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/rate_ctr.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_04_08_gprs.h>
#include <openbsc/gprs_bssgp.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/gprs_ns.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
void *bssgp_tall_ctx = NULL;
static const struct rate_ctr_desc bssgp_ctr_description[] = {
{ "packets.in", "Packets at BSSGP Level ( In)" },
{ "packets.out","Packets at BSSGP Level (Out)" },
{ "bytes.in", "Bytes at BSSGP Level ( In)" },
{ "bytes.out", "Bytes at BSSGP Level (Out)" },
{ "blocked", "BVC Blocking count" },
{ "discarded", "BVC LLC Discarded count" },
};
static const struct rate_ctr_group_desc bssgp_ctrg_desc = {
.group_name_prefix = "bssgp.bss_ctx",
.group_description = "BSSGP Peer Statistics",
.num_ctr = ARRAY_SIZE(bssgp_ctr_description),
.ctr_desc = bssgp_ctr_description,
};
LLIST_HEAD(bssgp_bvc_ctxts);
/* Find a BTS Context based on parsed RA ID and Cell ID */
struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid)
{
struct bssgp_bvc_ctx *bctx;
llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) {
if (!memcmp(&bctx->ra_id, raid, sizeof(bctx->ra_id)) &&
bctx->cell_id == cid)
return bctx;
}
return NULL;
}
/* Find a BTS context based on BVCI+NSEI tuple */
struct bssgp_bvc_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei)
{
struct bssgp_bvc_ctx *bctx;
llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) {
if (bctx->nsei == nsei && bctx->bvci == bvci)
return bctx;
}
return NULL;
}
struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei)
{
struct bssgp_bvc_ctx *ctx;
ctx = talloc_zero(bssgp_tall_ctx, struct bssgp_bvc_ctx);
if (!ctx)
return NULL;
ctx->bvci = bvci;
ctx->nsei = nsei;
/* FIXME: BVCI is not unique, only BVCI+NSEI ?!? */
ctx->ctrg = rate_ctr_group_alloc(ctx, &bssgp_ctrg_desc, bvci);
llist_add(&ctx->list, &bssgp_bvc_ctxts);
return ctx;
}
/* Chapter 10.4.5: Flow Control BVC ACK */
static int bssgp_tx_fc_bvc_ack(uint16_t nsei, uint8_t tag, uint16_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(bssgp_nsi, msg);
}
/* 10.3.7 SUSPEND-ACK PDU */
int bssgp_tx_suspend_ack(uint16_t nsei, uint32_t tlli,
const struct gprs_ra_id *ra_id, uint8_t suspend_ref)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint32_t _tlli;
uint8_t ra[6];
msgb_nsei(msg) = nsei;
msgb_bvci(msg) = 0; /* Signalling */
bgph->pdu_type = BSSGP_PDUT_SUSPEND_ACK;
_tlli = htonl(tlli);
msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
gsm48_construct_ra(ra, ra_id);
msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/* 10.3.8 SUSPEND-NACK PDU */
int bssgp_tx_suspend_nack(uint16_t nsei, uint32_t tlli,
const struct gprs_ra_id *ra_id,
uint8_t *cause)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint32_t _tlli;
uint8_t ra[6];
msgb_nsei(msg) = nsei;
msgb_bvci(msg) = 0; /* Signalling */
bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK;
_tlli = htonl(tlli);
msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
gsm48_construct_ra(ra, ra_id);
msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
if (cause)
msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/* 10.3.10 RESUME-ACK PDU */
int bssgp_tx_resume_ack(uint16_t nsei, uint32_t tlli,
const struct gprs_ra_id *ra_id)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint32_t _tlli;
uint8_t ra[6];
msgb_nsei(msg) = nsei;
msgb_bvci(msg) = 0; /* Signalling */
bgph->pdu_type = BSSGP_PDUT_RESUME_ACK;
_tlli = htonl(tlli);
msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
gsm48_construct_ra(ra, ra_id);
msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/* 10.3.11 RESUME-NACK PDU */
int bssgp_tx_resume_nack(uint16_t nsei, uint32_t tlli,
const struct gprs_ra_id *ra_id, uint8_t *cause)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint32_t _tlli;
uint8_t ra[6];
msgb_nsei(msg) = nsei;
msgb_bvci(msg) = 0; /* Signalling */
bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK;
_tlli = htonl(tlli);
msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
gsm48_construct_ra(ra, ra_id);
msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
if (cause)
msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf)
{
/* 6 octets RAC */
gsm48_parse_ra(raid, buf);
/* 2 octets CID */
return ntohs(*(uint16_t *) (buf+6));
}
int bssgp_create_cell_id(uint8_t *buf, const struct gprs_ra_id *raid,
uint16_t cid)
{
uint16_t *out_cid = (uint16_t *) (buf + 6);
/* 6 octets RAC */
gsm48_construct_ra(buf, raid);
/* 2 octets CID */
*out_cid = htons(cid);
return 8;
}
/* Chapter 8.4 BVC-Reset Procedure */
static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
uint16_t ns_bvci)
{
struct bssgp_bvc_ctx *bctx;
uint16_t nsei = msgb_nsei(msg);
uint16_t bvci;
int rc;
bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RESET cause=%s\n", bvci,
bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE)));
/* look-up or create the BTS context for this BVC */
bctx = btsctx_by_bvci_nsei(bvci, nsei);
if (!bctx)
bctx = btsctx_alloc(bvci, nsei);
/* As opposed to NS-VCs, BVCs are NOT blocked after RESET */
bctx->state &= ~BVC_S_BLOCKED;
/* When we receive a BVC-RESET PDU (at least of a PTP BVCI), the BSS
* informs us about its RAC + Cell ID, so we can create a mapping */
if (bvci != 0 && bvci != 1) {
if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESET "
"missing mandatory IE\n", bvci);
return -EINVAL;
}
/* actually extract RAC / CID */
bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id,
TLVP_VAL(tp, BSSGP_IE_CELL_ID));
LOGP(DBSSGP, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n",
bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac,
bctx->ra_id.rac, bctx->cell_id, bvci);
}
/* Acknowledge the RESET to the BTS */
rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK,
nsei, bvci, ns_bvci);
return 0;
}
static int bssgp_rx_bvc_block(struct msgb *msg, struct tlv_parsed *tp)
{
uint16_t bvci;
struct bssgp_bvc_ctx *ptp_ctx;
bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
if (bvci == BVCI_SIGNALLING) {
/* 8.3.2: Signalling BVC shall never be blocked */
LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
"received block for signalling BVC!?!\n",
msgb_nsei(msg), msgb_bvci(msg));
return 0;
}
LOGP(DBSSGP, LOGL_INFO, "BSSGP Rx BVCI=%u BVC-BLOCK\n", bvci);
ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
if (!ptp_ctx)
return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
ptp_ctx->state |= BVC_S_BLOCKED;
rate_ctr_inc(&ptp_ctx->ctrg->ctr[BSSGP_CTR_BLOCKED]);
/* FIXME: Send NM_BVC_BLOCK.ind to NM */
/* We always acknowledge the BLOCKing */
return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, msgb_nsei(msg),
bvci, msgb_bvci(msg));
};
static int bssgp_rx_bvc_unblock(struct msgb *msg, struct tlv_parsed *tp)
{
uint16_t bvci;
struct bssgp_bvc_ctx *ptp_ctx;
bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
if (bvci == BVCI_SIGNALLING) {
/* 8.3.2: Signalling BVC shall never be blocked */
LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
"received unblock for signalling BVC!?!\n",
msgb_nsei(msg), msgb_bvci(msg));
return 0;
}
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC-UNBLOCK\n", bvci);
ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
if (!ptp_ctx)
return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
ptp_ctx->state &= ~BVC_S_BLOCKED;
/* FIXME: Send NM_BVC_UNBLOCK.ind to NM */
/* We always acknowledge the unBLOCKing */
return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, msgb_nsei(msg),
bvci, msgb_bvci(msg));
};
/* Uplink unit-data */
static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp,
struct bssgp_bvc_ctx *ctx)
{
struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
/* extract TLLI and parse TLV IEs */
msgb_tlli(msg) = ntohl(budh->tlli);
DEBUGP(DBSSGP, "BSSGP TLLI=0x%08x Rx UPLINK-UNITDATA\n", msgb_tlli(msg));
/* 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)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD "
"missing mandatory IE\n", msgb_tlli(msg));
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
/* store pointer to LLC header and CELL ID in msgb->cb */
msgb_llch(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
msgb_bcid(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_CELL_ID);
return gprs_llc_rcvmsg(msg, tp);
}
static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp,
struct bssgp_bvc_ctx *ctx)
{
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_bssgph(msg);
struct gprs_ra_id raid;
uint32_t tlli;
int rc;
if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
!TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND "
"missing mandatory IE\n", ctx->bvci);
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n",
ctx->bvci, tlli);
gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
/* Inform GMM about the SUSPEND request */
rc = gprs_gmm_rx_suspend(&raid, tlli);
if (rc < 0)
return bssgp_tx_suspend_nack(msgb_nsei(msg), tlli, &raid, NULL);
bssgp_tx_suspend_ack(msgb_nsei(msg), tlli, &raid, 0);
return 0;
}
static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp,
struct bssgp_bvc_ctx *ctx)
{
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_bssgph(msg);
struct gprs_ra_id raid;
uint32_t tlli;
uint8_t suspend_ref;
int rc;
if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
!TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) ||
!TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME "
"missing mandatory IE\n", ctx->bvci);
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
suspend_ref = *TLVP_VAL(tp, BSSGP_IE_SUSPEND_REF_NR);
DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx RESUME\n", ctx->bvci, tlli);
gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
/* Inform GMM about the RESUME request */
rc = gprs_gmm_rx_resume(&raid, tlli, suspend_ref);
if (rc < 0)
return bssgp_tx_resume_nack(msgb_nsei(msg), tlli, &raid,
NULL);
bssgp_tx_resume_ack(msgb_nsei(msg), tlli, &raid);
return 0;
}
static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp,
struct bssgp_bvc_ctx *ctx)
{
uint32_t tlli = 0;
if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
!TLVP_PRESENT(tp, BSSGP_IE_LLC_FRAMES_DISCARDED) ||
!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
!TLVP_PRESENT(tp, BSSGP_IE_NUM_OCT_AFF)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx LLC DISCARDED "
"missing mandatory IE\n", ctx->bvci);
}
if (TLVP_PRESENT(tp, BSSGP_IE_TLLI))
tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=%08x Rx LLC DISCARDED\n",
ctx->bvci, tlli);
rate_ctr_inc(&ctx->ctrg->ctr[BSSGP_CTR_DISCARDED]);
/* FIXME: send NM_LLC_DISCARDED to NM */
return 0;
}
static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp,
struct bssgp_bvc_ctx *bctx)
{
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n",
bctx->bvci);
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)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx FC BVC "
"missing mandatory IE\n", bctx->bvci);
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
/* FIXME: actually implement flow control */
/* Send FLOW_CONTROL_BVC_ACK */
return bssgp_tx_fc_bvc_ack(msgb_nsei(msg), *TLVP_VAL(tp, BSSGP_IE_TAG),
msgb_bvci(msg));
}
/* Receive a BSSGP PDU from a BSS on a PTP BVCI */
static int gprs_bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
struct bssgp_bvc_ctx *bctx)
{
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_bssgph(msg);
uint8_t pdu_type = bgph->pdu_type;
int rc = 0;
/* If traffic is received on a BVC that is marked as blocked, the
* received PDU shall not be accepted and a STATUS PDU (Cause value:
* BVC Blocked) shall be sent to the peer entity on the signalling BVC */
if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS) {
uint16_t bvci = msgb_bvci(msg);
return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg);
}
switch (pdu_type) {
case BSSGP_PDUT_UL_UNITDATA:
/* some LLC data from the MS */
rc = bssgp_rx_ul_ud(msg, tp, bctx);
break;
case BSSGP_PDUT_RA_CAPABILITY:
/* BSS requests RA capability or IMSI */
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RA CAPABILITY UPDATE\n",
bctx->bvci);
/* FIXME: send GMM_RA_CAPABILITY_UPDATE.ind to GMM */
/* FIXME: send RA_CAPA_UPDATE_ACK */
break;
case BSSGP_PDUT_RADIO_STATUS:
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RADIO STATUS\n", bctx->bvci);
/* BSS informs us of some exception */
/* FIXME: send GMM_RADIO_STATUS.ind to GMM */
break;
case BSSGP_PDUT_FLOW_CONTROL_BVC:
/* BSS informs us of available bandwidth in Gb interface */
rc = bssgp_rx_fc_bvc(msg, tp, bctx);
break;
case BSSGP_PDUT_FLOW_CONTROL_MS:
/* BSS informs us of available bandwidth to one MS */
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control MS\n",
bctx->bvci);
/* FIXME: actually implement flow control */
/* FIXME: Send FLOW_CONTROL_MS_ACK */
break;
case BSSGP_PDUT_STATUS:
/* Some exception has occurred */
/* FIXME: send NM_STATUS.ind to NM */
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(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x not [yet] "
"implemented\n", bctx->bvci, pdu_type);
rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
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_FLOW_CONTROL_BVC_ACK:
case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x only exists "
"in DL\n", bctx->bvci, pdu_type);
bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
rc = -EINVAL;
break;
default:
DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x unknown\n",
bctx->bvci, pdu_type);
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
break;
}
return rc;
}
/* Receive a BSSGP PDU from a BSS on a SIGNALLING BVCI */
static int gprs_bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
struct bssgp_bvc_ctx *bctx)
{
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_bssgph(msg);
uint8_t pdu_type = bgph->pdu_type;
int rc = 0;
uint16_t ns_bvci = msgb_bvci(msg);
uint16_t bvci;
switch (bgph->pdu_type) {
case BSSGP_PDUT_SUSPEND:
/* MS wants to suspend */
rc = bssgp_rx_suspend(msg, tp, bctx);
break;
case BSSGP_PDUT_RESUME:
/* MS wants to resume */
rc = bssgp_rx_resume(msg, tp, bctx);
break;
case BSSGP_PDUT_FLUSH_LL_ACK:
/* BSS informs us it has performed LL FLUSH */
DEBUGP(DBSSGP, "BSSGP Rx BVCI=%u FLUSH LL ACK\n", bctx->bvci);
/* FIXME: send NM_FLUSH_LL.res to NM */
break;
case BSSGP_PDUT_LLC_DISCARD:
/* BSS informs that some LLC PDU's have been discarded */
rc = bssgp_rx_llc_disc(msg, tp, bctx);
break;
case BSSGP_PDUT_BVC_BLOCK:
/* BSS tells us that BVC shall be blocked */
if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
!TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-BLOCK "
"missing mandatory IE\n");
goto err_mand_ie;
}
rc = bssgp_rx_bvc_block(msg, tp);
break;
case BSSGP_PDUT_BVC_UNBLOCK:
/* BSS tells us that BVC shall be unblocked */
if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-UNBLOCK "
"missing mandatory IE\n");
goto err_mand_ie;
}
rc = bssgp_rx_bvc_unblock(msg, tp);
break;
case BSSGP_PDUT_BVC_RESET:
/* BSS tells us that BVC init is required */
if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
!TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-RESET "
"missing mandatory IE\n");
goto err_mand_ie;
}
rc = bssgp_rx_bvc_reset(msg, tp, ns_bvci);
break;
case BSSGP_PDUT_STATUS:
/* Some exception has occurred */
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci);
/* FIXME: send NM_STATUS.ind to NM */
break;
/* those only exist in the SGSN -> BSS direction */
case BSSGP_PDUT_PAGING_PS:
case BSSGP_PDUT_PAGING_CS:
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:
case BSSGP_PDUT_BVC_BLOCK_ACK:
case BSSGP_PDUT_BVC_UNBLOCK_ACK:
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x only exists "
"in DL\n", bctx->bvci, pdu_type);
bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
rc = -EINVAL;
break;
default:
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n",
bctx->bvci, pdu_type);
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
break;
}
return rc;
err_mand_ie:
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
/* We expect msgb_bssgph() to point to the BSSGP header */
int gprs_bssgp_rcvmsg(struct msgb *msg)
{
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_bssgph(msg);
struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
struct tlv_parsed tp;
struct bssgp_bvc_ctx *bctx;
uint8_t pdu_type = bgph->pdu_type;
uint16_t ns_bvci = msgb_bvci(msg);
int data_len;
int rc = 0;
/* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
/* UNITDATA BSSGP headers have TLLI in front */
if (pdu_type != BSSGP_PDUT_UL_UNITDATA &&
pdu_type != BSSGP_PDUT_DL_UNITDATA) {
data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
} else {
data_len = msgb_bssgp_len(msg) - sizeof(*budh);
rc = bssgp_tlv_parse(&tp, budh->data, data_len);
}
/* look-up or create the BTS context for this BVC */
bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
/* Only a RESET PDU can create a new BVC context */
if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET) {
LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
"type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
pdu_type);
return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
}
if (bctx) {
log_set_context(BSC_CTX_BVC, bctx);
rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN],
msgb_bssgp_len(msg));
}
if (ns_bvci == BVCI_SIGNALLING)
rc = gprs_bssgp_rx_sign(msg, &tp, bctx);
else if (ns_bvci == BVCI_PTM)
rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
else
rc = gprs_bssgp_rx_ptp(msg, &tp, bctx);
return rc;
}
/* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU
* to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */
int gprs_bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx)
{
struct bssgp_bvc_ctx *bctx;
struct bssgp_ud_hdr *budh;
uint8_t llc_pdu_tlv_hdr_len = 2;
uint8_t *llc_pdu_tlv, *qos_profile;
uint16_t pdu_lifetime = 1000; /* centi-seconds */
uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x20 };
uint16_t msg_len = msg->len;
uint16_t bvci = msgb_bvci(msg);
uint16_t nsei = msgb_nsei(msg);
uint16_t drx_params;
/* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */
if (bvci <= BVCI_PTM ) {
LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n",
bvci);
return -EINVAL;
}
bctx = btsctx_by_bvci_nsei(bvci, nsei);
if (!bctx) {
/* FIXME: don't simply create missing context, but reject message */
bctx = btsctx_alloc(bvci, nsei);
}
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 & 0x7f;
llc_pdu_tlv[1] |= 0x80;
}
/* FIXME: optional elements: Alignment, UTRAN CCO, LSA, PFI */
if (mmctx) {
/* Old TLLI to help BSS map from old->new */
#if 0
if (mmctx->tlli_old)
msgb_tvlv_push(msg, BSSGP_IE_TLLI, 4, htonl(*tlli_old));
#endif
/* IMSI */
if (strlen(mmctx->imsi)) {
uint8_t mi[10];
int imsi_len = gsm48_generate_mid_from_imsi(mi, mmctx->imsi);
if (imsi_len > 2)
msgb_tvlv_push(msg, BSSGP_IE_IMSI,
imsi_len-2, mi+2);
}
/* DRX parameters */
drx_params = htons(mmctx->drx_parms);
msgb_tvlv_push(msg, BSSGP_IE_DRX_PARAMS, 2,
(uint8_t *) &drx_params);
/* FIXME: Priority */
/* MS Radio Access Capability */
if (mmctx->ms_radio_access_capa.len)
msgb_tvlv_push(msg, BSSGP_IE_MS_RADIO_ACCESS_CAP,
mmctx->ms_radio_access_capa.len,
mmctx->ms_radio_access_capa.buf);
}
/* prepend the pdu lifetime */
pdu_lifetime = htons(pdu_lifetime);
msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (uint8_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(msgb_tlli(msg));
budh->pdu_type = BSSGP_PDUT_DL_UNITDATA;
rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]);
rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len);
/* Identifiers down: BVCI, NSEI (in msgb->cb) */
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/* Send a single GMM-PAGING.req to a given NSEI/NS-BVCI */
int gprs_bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci,
struct bssgp_paging_info *pinfo)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint16_t drx_params = htons(pinfo->drx_params);
uint8_t mi[10];
int imsi_len = gsm48_generate_mid_from_imsi(mi, pinfo->imsi);
uint8_t ra[6];
if (imsi_len < 2)
return -EINVAL;
msgb_nsei(msg) = nsei;
msgb_bvci(msg) = ns_bvci;
if (pinfo->mode == BSSGP_PAGING_PS)
bgph->pdu_type = BSSGP_PDUT_PAGING_PS;
else
bgph->pdu_type = BSSGP_PDUT_PAGING_CS;
/* IMSI */
msgb_tvlv_put(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2);
/* DRX Parameters */
msgb_tvlv_put(msg, BSSGP_IE_DRX_PARAMS, 2,
(uint8_t *) &drx_params);
/* Scope */
switch (pinfo->scope) {
case BSSGP_PAGING_BSS_AREA:
{
uint8_t null = 0;
msgb_tvlv_put(msg, BSSGP_IE_BSS_AREA_ID, 1, &null);
}
break;
case BSSGP_PAGING_LOCATION_AREA:
gsm48_construct_ra(ra, &pinfo->raid);
msgb_tvlv_put(msg, BSSGP_IE_LOCATION_AREA, 4, ra);
break;
case BSSGP_PAGING_ROUTEING_AREA:
gsm48_construct_ra(ra, &pinfo->raid);
msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
break;
case BSSGP_PAGING_BVCI:
{
uint16_t bvci = htons(pinfo->bvci);
msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *)&bvci);
}
break;
}
/* QoS profile mandatory for PS */
if (pinfo->mode == BSSGP_PAGING_PS)
msgb_tvlv_put(msg, BSSGP_IE_QOS_PROFILE, 3, pinfo->qos);
/* Optional (P-)TMSI */
if (pinfo->ptmsi) {
uint32_t ptmsi = htonl(*pinfo->ptmsi);
msgb_tvlv_put(msg, BSSGP_IE_TMSI, 4, (uint8_t *) &ptmsi);
}
return gprs_ns_sendmsg(bssgp_nsi, msg);
}

View File

@@ -1,426 +0,0 @@
/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
/* (C) 2009-2011 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <errno.h>
#include <stdint.h>
#include <netinet/in.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/talloc.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gprs_bssgp.h>
#include <openbsc/gprs_ns.h>
uint8_t *bssgp_msgb_tlli_put(struct msgb *msg, uint32_t tlli)
{
uint32_t _tlli = htonl(tlli);
return msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
}
/*! \brief GMM-SUSPEND.req (Chapter 10.3.6) */
int bssgp_tx_suspend(uint16_t nsei, uint32_t tlli,
const struct gprs_ra_id *ra_id)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint8_t ra[6];
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=0) Tx SUSPEND (TLLI=0x%04x)\n",
tlli);
msgb_nsei(msg) = nsei;
msgb_bvci(msg) = 0; /* Signalling */
bgph->pdu_type = BSSGP_PDUT_SUSPEND;
bssgp_msgb_tlli_put(msg, tlli);
gsm48_construct_ra(ra, ra_id);
msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/*! \brief GMM-RESUME.req (Chapter 10.3.9) */
int bssgp_tx_resume(uint16_t nsei, uint32_t tlli,
const struct gprs_ra_id *ra_id, uint8_t suspend_ref)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint8_t ra[6];
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=0) Tx RESUME (TLLI=0x%04x)\n",
tlli);
msgb_nsei(msg) = nsei;
msgb_bvci(msg) = 0; /* Signalling */
bgph->pdu_type = BSSGP_PDUT_RESUME;
bssgp_msgb_tlli_put(msg, tlli);
gsm48_construct_ra(ra, ra_id);
msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/*! \brief Transmit RA-CAPABILITY-UPDATE (10.3.3) */
int bssgp_tx_ra_capa_upd(struct bssgp_bvc_ctx *bctx, uint32_t tlli, uint8_t tag)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx RA-CAPA-UPD (TLLI=0x%04x)\n",
bctx->bvci, tlli);
/* set NSEI and BVCI in msgb cb */
msgb_nsei(msg) = bctx->nsei;
msgb_bvci(msg) = bctx->bvci;
bgph->pdu_type = BSSGP_PDUT_RA_CAPA_UDPATE;
bssgp_msgb_tlli_put(msg, tlli);
msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/* first common part of RADIO-STATUS */
static struct msgb *common_tx_radio_status(struct bssgp_bvc_ctx *bctx)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx RADIO-STATUS ",
bctx->bvci);
/* set NSEI and BVCI in msgb cb */
msgb_nsei(msg) = bctx->nsei;
msgb_bvci(msg) = bctx->bvci;
bgph->pdu_type = BSSGP_PDUT_RADIO_STATUS;
return msg;
}
/* second common part of RADIO-STATUS */
static int common_tx_radio_status2(struct msgb *msg, uint8_t cause)
{
msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause);
LOGPC(DBSSGP, LOGL_NOTICE, "CAUSE=%u\n", cause);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/*! \brief Transmit RADIO-STATUS for TLLI (10.3.5) */
int bssgp_tx_radio_status_tlli(struct bssgp_bvc_ctx *bctx, uint8_t cause,
uint32_t tlli)
{
struct msgb *msg = common_tx_radio_status(bctx);
if (!msg)
return -ENOMEM;
bssgp_msgb_tlli_put(msg, tlli);
LOGPC(DBSSGP, LOGL_NOTICE, "TLLI=0x%08x ", tlli);
return common_tx_radio_status2(msg, cause);
}
/*! \brief Transmit RADIO-STATUS for TMSI (10.3.5) */
int bssgp_tx_radio_status_tmsi(struct bssgp_bvc_ctx *bctx, uint8_t cause,
uint32_t tmsi)
{
struct msgb *msg = common_tx_radio_status(bctx);
uint32_t _tmsi = htonl(tmsi);
if (!msg)
return -ENOMEM;
msgb_tvlv_put(msg, BSSGP_IE_TMSI, 4, (uint8_t *)&_tmsi);
LOGPC(DBSSGP, LOGL_NOTICE, "TMSI=0x%08x ", tmsi);
return common_tx_radio_status2(msg, cause);
}
/*! \brief Transmit RADIO-STATUS for IMSI (10.3.5) */
int bssgp_tx_radio_status_imsi(struct bssgp_bvc_ctx *bctx, uint8_t cause,
const char *imsi)
{
struct msgb *msg = common_tx_radio_status(bctx);
uint8_t mi[10];
int imsi_len = gsm48_generate_mid_from_imsi(mi, imsi);
if (!msg)
return -ENOMEM;
/* strip the MI type and length values (2 bytes) */
if (imsi_len > 2)
msgb_tvlv_put(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2);
LOGPC(DBSSGP, LOGL_NOTICE, "IMSI=%s ", imsi);
return common_tx_radio_status2(msg, cause);
}
/*! \brief Transmit FLUSH-LL-ACK (Chapter 10.4.2) */
int bssgp_tx_flush_ll_ack(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
uint8_t action, uint16_t bvci_new,
uint32_t num_octets)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint16_t _bvci_new = htons(bvci_new);
uint32_t _oct_aff = htonl(num_octets & 0xFFFFFF);
msgb_nsei(msg) = bctx->nsei;
msgb_bvci(msg) = 0; /* Signalling */
bgph->pdu_type = BSSGP_PDUT_FLUSH_LL_ACK;
bssgp_msgb_tlli_put(msg, tlli);
msgb_tvlv_put(msg, BSSGP_IE_FLUSH_ACTION, 1, &action);
if (action == 1) /* transferred */
msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci_new);
msgb_tvlv_put(msg, BSSGP_IE_NUM_OCT_AFF, 3, (uint8_t *) &_oct_aff);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/*! \brief Transmit LLC-DISCARDED (Chapter 10.4.3) */
int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
uint8_t num_frames, uint32_t num_octets)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint16_t _bvci = htons(bctx->bvci);
uint32_t _oct_aff = htonl(num_octets & 0xFFFFFF);
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx LLC-DISCARDED "
"TLLI=0x%04x, FRAMES=%u, OCTETS=%u\n", bctx->bvci, tlli,
num_frames, num_octets);
msgb_nsei(msg) = bctx->nsei;
msgb_bvci(msg) = 0; /* Signalling */
bgph->pdu_type = BSSGP_PDUT_LLC_DISCARD;
bssgp_msgb_tlli_put(msg, tlli);
msgb_tvlv_put(msg, BSSGP_IE_LLC_FRAMES_DISCARDED, 1, &num_frames);
msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
msgb_tvlv_put(msg, BSSGP_IE_NUM_OCT_AFF, 3, (uint8_t *) &_oct_aff);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/*! \brief Transmit a BVC-BLOCK message (Chapter 10.4.8) */
int bssgp_tx_bvc_block(struct bssgp_bvc_ctx *bctx, uint8_t cause)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint16_t _bvci = htons(bctx->bvci);
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-BLOCK "
"CAUSE=%u\n", bctx->bvci, cause);
msgb_nsei(msg) = bctx->nsei;
msgb_bvci(msg) = 0; /* Signalling */
bgph->pdu_type = BSSGP_PDUT_BVC_BLOCK;
msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/*! \brief Transmit a BVC-UNBLOCK message (Chapter 10.4.10) */
int bssgp_tx_bvc_unblock(struct bssgp_bvc_ctx *bctx)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint16_t _bvci = htons(bctx->bvci);
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-BLOCK\n", bctx->bvci);
msgb_nsei(msg) = bctx->nsei;
msgb_bvci(msg) = 0; /* Signalling */
bgph->pdu_type = BSSGP_PDUT_BVC_UNBLOCK;
msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/*! \brief Transmit a BVC-RESET message (Chapter 10.4.12) */
int bssgp_tx_bvc_reset(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint16_t _bvci = htons(bvci);
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-RESET "
"CAUSE=%u\n", bvci, cause);
msgb_nsei(msg) = bctx->nsei;
msgb_bvci(msg) = 0; /* Signalling */
bgph->pdu_type = BSSGP_PDUT_BVC_RESET;
msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause);
if (bvci != BVCI_PTM) {
uint8_t bssgp_cid[8];
bssgp_create_cell_id(bssgp_cid, &bctx->ra_id, bctx->cell_id);
msgb_tvlv_put(msg, BSSGP_IE_CELL_ID, sizeof(bssgp_cid), bssgp_cid);
}
/* Optional: Feature Bitmap */
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/*! \brief RL-UL-UNITDATA.req (Chapter 10.2.2) */
int bssgp_tx_ul_ud(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
const uint8_t *qos_profile, struct msgb *llc_pdu)
{
struct msgb *msg = llc_pdu;
uint8_t bssgp_cid[8];
struct bssgp_ud_hdr *budh;
/* FIXME: First push alignment octets, if rqd */
/* FIXME: Optional LSA Identifier List, PFI */
/* Cell Identifier */
bssgp_create_cell_id(bssgp_cid, &bctx->ra_id, bctx->cell_id);
msgb_tvlv_push(msg, BSSGP_IE_CELL_ID, sizeof(bssgp_cid), bssgp_cid);
/* User Data Header */
budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh));
budh->tlli = htonl(tlli);
memcpy(budh->qos_profile, qos_profile, 3);
budh->pdu_type = BSSGP_PDUT_UL_UNITDATA;
/* set NSEI and BVCI in msgb cb */
msgb_nsei(msg) = bctx->nsei;
msgb_bvci(msg) = bctx->bvci;
rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]);
rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/* Parse a single GMM-PAGING.req to a given NSEI/NS-BVCI */
int gprs_bssgp_rx_paging(struct bssgp_paging_info *pinfo,
struct msgb *msg)
{
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_bssgph(msg);
struct tlv_parsed tp;
uint8_t ra[6];
int rc, data_len;
memset(ra, 0, sizeof(ra));
data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
if (rc < 0)
goto err_mand_ie;
switch (bgph->pdu_type) {
case BSSGP_PDUT_PAGING_PS:
pinfo->mode = BSSGP_PAGING_PS;
break;
case BSSGP_PDUT_PAGING_CS:
pinfo->mode = BSSGP_PAGING_CS;
break;
default:
return -EINVAL;
}
/* IMSI */
if (!TLVP_PRESENT(&tp, BSSGP_IE_IMSI))
goto err_mand_ie;
if (!pinfo->imsi)
pinfo->imsi = talloc_zero_size(pinfo, 16);
gsm48_mi_to_string(pinfo->imsi, sizeof(pinfo->imsi),
TLVP_VAL(&tp, BSSGP_IE_IMSI),
TLVP_LEN(&tp, BSSGP_IE_IMSI));
/* DRX Parameters */
if (!TLVP_PRESENT(&tp, BSSGP_IE_DRX_PARAMS))
goto err_mand_ie;
pinfo->drx_params = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_DRX_PARAMS));
/* Scope */
if (TLVP_PRESENT(&tp, BSSGP_IE_BSS_AREA_ID)) {
pinfo->scope = BSSGP_PAGING_BSS_AREA;
} else if (TLVP_PRESENT(&tp, BSSGP_IE_LOCATION_AREA)) {
pinfo->scope = BSSGP_PAGING_LOCATION_AREA;
memcpy(ra, TLVP_VAL(&tp, BSSGP_IE_LOCATION_AREA),
TLVP_LEN(&tp, BSSGP_IE_LOCATION_AREA));
gsm48_parse_ra(&pinfo->raid, ra);
} else if (TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) {
pinfo->scope = BSSGP_PAGING_ROUTEING_AREA;
memcpy(ra, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA),
TLVP_LEN(&tp, BSSGP_IE_ROUTEING_AREA));
gsm48_parse_ra(&pinfo->raid, ra);
} else if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) {
pinfo->scope = BSSGP_PAGING_BVCI;
pinfo->bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI));
} else
return -EINVAL;
/* QoS profile mandatory for PS */
if (pinfo->mode == BSSGP_PAGING_PS) {
if (!TLVP_PRESENT(&tp, BSSGP_IE_QOS_PROFILE))
goto err_cond_ie;
if (TLVP_LEN(&tp, BSSGP_IE_QOS_PROFILE) < 3)
goto err;
memcpy(&pinfo->qos, TLVP_VAL(&tp, BSSGP_IE_QOS_PROFILE),
3);
}
/* Optional (P-)TMSI */
if (TLVP_PRESENT(&tp, BSSGP_IE_TMSI) &&
TLVP_LEN(&tp, BSSGP_IE_TMSI) >= 4)
if (!pinfo->ptmsi)
pinfo->ptmsi = talloc_zero_size(pinfo, sizeof(uint32_t));
*(pinfo->ptmsi) = ntohl(*(uint32_t *)
TLVP_VAL(&tp, BSSGP_IE_TMSI));
return 0;
err_mand_ie:
err_cond_ie:
err:
/* FIXME */
return 0;
}

View File

@@ -1,118 +0,0 @@
/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <errno.h>
#include <stdint.h>
#include <netinet/in.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/talloc.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gprs_bssgp.h>
#include <openbsc/gprs_ns.h>
struct gprs_ns_inst *bssgp_nsi;
/* BSSGP Protocol specific, not implementation specific */
/* FIXME: This needs to go into libosmocore after finished */
/* Chapter 11.3.9 / Table 11.10: Cause coding */
static const struct value_string 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" },
{ 0, NULL },
};
const char *bssgp_cause_str(enum gprs_bssgp_cause cause)
{
return get_value_string(bssgp_cause_strings, cause);
}
struct msgb *bssgp_msgb_alloc(void)
{
return msgb_alloc_headroom(4096, 128, "BSSGP");
}
/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */
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(bssgp_nsi, msg);
}
/* Chapter 10.4.14: Status */
int bssgp_tx_status(uint8_t cause, uint16_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));
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Tx STATUS, cause=%s\n",
bvci ? *bvci : 0, 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);
if (bvci) {
uint16_t _bvci = htons(*bvci);
msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
}
msgb_tvlv_put(msg, BSSGP_IE_PDU_IN_ERROR,
msgb_bssgp_len(orig_msg), msgb_bssgph(orig_msg));
return gprs_ns_sendmsg(bssgp_nsi, msg);
}

View File

@@ -1,177 +0,0 @@
/* VTY interface for our GPRS BSS Gateway Protocol (BSSGP) implementation */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <openbsc/gsm_data.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <openbsc/debug.h>
#include <openbsc/signal.h>
#include <openbsc/gprs_ns.h>
#include <openbsc/gprs_bssgp.h>
#include <openbsc/vty.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/misc.h>
/* FIXME: this should go to some common file as it is copied
* in vty_interface.c of the BSC */
static const struct value_string gprs_bssgp_timer_strs[] = {
{ 0, NULL }
};
static struct cmd_node bssgp_node = {
BSSGP_NODE,
"%s(bssgp)#",
1,
};
static int config_write_bssgp(struct vty *vty)
{
vty_out(vty, "bssgp%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN(cfg_bssgp, cfg_bssgp_cmd,
"bssgp",
"Configure the GPRS BSS Gateway Protocol")
{
vty->node = BSSGP_NODE;
return CMD_SUCCESS;
}
static void dump_bvc(struct vty *vty, struct bssgp_bvc_ctx *bvc, int stats)
{
vty_out(vty, "NSEI %5u, BVCI %5u, RA-ID: %u-%u-%u-%u, CID: %u, "
"STATE: %s%s", bvc->nsei, bvc->bvci, bvc->ra_id.mcc,
bvc->ra_id.mnc, bvc->ra_id.lac, bvc->ra_id.rac, bvc->cell_id,
bvc->state & BVC_S_BLOCKED ? "BLOCKED" : "UNBLOCKED",
VTY_NEWLINE);
if (stats)
vty_out_rate_ctr_group(vty, " ", bvc->ctrg);
}
static void dump_bssgp(struct vty *vty, int stats)
{
struct bssgp_bvc_ctx *bvc;
llist_for_each_entry(bvc, &bssgp_bvc_ctxts, list) {
dump_bvc(vty, bvc, stats);
}
}
#define BSSGP_STR "Show information about the BSSGP protocol\n"
DEFUN(show_bssgp, show_bssgp_cmd, "show bssgp",
SHOW_STR BSSGP_STR)
{
dump_bssgp(vty, 0);
return CMD_SUCCESS;
}
DEFUN(show_bssgp_stats, show_bssgp_stats_cmd, "show bssgp stats",
SHOW_STR BSSGP_STR
"Include statistics\n")
{
dump_bssgp(vty, 1);
return CMD_SUCCESS;
}
DEFUN(show_bvc, show_bvc_cmd, "show bssgp nsei <0-65535> [stats]",
SHOW_STR BSSGP_STR
"Show all BVCs on one NSE\n"
"The NSEI\n" "Include Statistics\n")
{
struct bssgp_bvc_ctx *bvc;
uint16_t nsei = atoi(argv[1]);
int show_stats = 0;
if (argc >= 2)
show_stats = 1;
llist_for_each_entry(bvc, &bssgp_bvc_ctxts, list) {
if (bvc->nsei != nsei)
continue;
dump_bvc(vty, bvc, show_stats);
}
return CMD_SUCCESS;
}
DEFUN(logging_fltr_bvc,
logging_fltr_bvc_cmd,
"logging filter bvc nsei <0-65535> bvci <0-65535>",
LOGGING_STR FILTER_STR
"Filter based on BSSGP Virtual Connection\n"
"NSEI of the BVC to be filtered\n"
"Network Service Entity Identifier (NSEI)\n"
"BVCI of the BVC to be filtered\n"
"BSSGP Virtual Connection Identifier (BVCI)\n")
{
struct log_target *tgt = osmo_log_vty2tgt(vty);
struct bssgp_bvc_ctx *bvc;
uint16_t nsei = atoi(argv[0]);
uint16_t bvci = atoi(argv[1]);
if (!tgt)
return CMD_WARNING;
bvc = btsctx_by_bvci_nsei(bvci, nsei);
if (!bvc) {
vty_out(vty, "No BVC by that identifier%s", VTY_NEWLINE);
return CMD_WARNING;
}
log_set_bvc_filter(tgt, bvc);
return CMD_SUCCESS;
}
int gprs_bssgp_vty_init(void)
{
install_element_ve(&show_bssgp_cmd);
install_element_ve(&show_bssgp_stats_cmd);
install_element_ve(&show_bvc_cmd);
install_element_ve(&logging_fltr_bvc_cmd);
install_element(CFG_LOG_NODE, &logging_fltr_bvc_cmd);
install_element(CONFIG_NODE, &cfg_bssgp_cmd);
install_node(&bssgp_node, config_write_bssgp);
install_default(BSSGP_NODE);
install_element(BSSGP_NODE, &ournode_exit_cmd);
install_element(BSSGP_NODE, &ournode_end_cmd);
//install_element(BSSGP_NODE, &cfg_bssgp_timer_cmd);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,304 +0,0 @@
/* 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) */
/* NS-over-FR-over-GRE implementation */
/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <osmocom/core/select.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <openbsc/socket.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_ns.h>
#define GRE_PTYPE_FR 0x6559
#define GRE_PTYPE_IPv4 0x0800
#define GRE_PTYPE_KAR 0x0000 /* keepalive response */
struct gre_hdr {
uint16_t flags;
uint16_t ptype;
} __attribute__ ((packed));
/* IPv4 messages inside the GRE tunnel might be GRE keepalives */
static int handle_rx_gre_ipv4(struct osmo_fd *bfd, struct msgb *msg,
struct iphdr *iph, struct gre_hdr *greh)
{
struct gprs_ns_inst *nsi = bfd->data;
int gre_payload_len;
struct iphdr *inner_iph;
struct gre_hdr *inner_greh;
struct sockaddr_in daddr;
struct in_addr ia;
gre_payload_len = msg->len - (iph->ihl*4 + sizeof(*greh));
inner_iph = (struct iphdr *) ((uint8_t *)greh + sizeof(*greh));
if (gre_payload_len < inner_iph->ihl*4 + sizeof(*inner_greh)) {
LOGP(DNS, LOGL_ERROR, "GRE keepalive too short\n");
return -EIO;
}
if (inner_iph->saddr != iph->daddr ||
inner_iph->daddr != iph->saddr) {
LOGP(DNS, LOGL_ERROR,
"GRE keepalive with wrong tunnel addresses\n");
return -EIO;
}
if (inner_iph->protocol != IPPROTO_GRE) {
LOGP(DNS, LOGL_ERROR, "GRE keepalive with wrong protocol\n");
return -EIO;
}
inner_greh = (struct gre_hdr *) ((uint8_t *)inner_iph + iph->ihl*4);
if (inner_greh->ptype != htons(GRE_PTYPE_KAR)) {
LOGP(DNS, LOGL_ERROR, "GRE keepalive inner GRE type != 0\n");
return -EIO;
}
/* Actually send the response back */
daddr.sin_family = AF_INET;
daddr.sin_addr.s_addr = inner_iph->daddr;
daddr.sin_port = IPPROTO_GRE;
ia.s_addr = iph->saddr;
LOGP(DNS, LOGL_DEBUG, "GRE keepalive from %s, responding\n",
inet_ntoa(ia));
return sendto(nsi->frgre.fd.fd, inner_greh,
gre_payload_len - inner_iph->ihl*4, 0,
(struct sockaddr *)&daddr, sizeof(daddr));
}
static struct msgb *read_nsfrgre_msg(struct osmo_fd *bfd, int *error,
struct sockaddr_in *saddr)
{
struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
int ret = 0;
socklen_t saddr_len = sizeof(*saddr);
struct iphdr *iph;
struct gre_hdr *greh;
uint8_t *frh;
uint16_t dlci;
if (!msg) {
*error = -ENOMEM;
return NULL;
}
ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0,
(struct sockaddr *)saddr, &saddr_len);
if (ret < 0) {
LOGP(DNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",
strerror(errno));
*error = ret;
goto out_err;
} else if (ret == 0) {
*error = ret;
goto out_err;
}
msgb_put(msg, ret);
if (msg->len < sizeof(*iph) + sizeof(*greh) + 2) {
LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
*error = -EIO;
goto out_err;
}
iph = (struct iphdr *) msg->data;
if (msg->len < (iph->ihl*4 + sizeof(*greh) + 2)) {
LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
*error = -EIO;
goto out_err;
}
greh = (struct gre_hdr *) (msg->data + iph->ihl*4);
if (greh->flags) {
LOGP(DNS, LOGL_NOTICE, "Unknown GRE flags 0x%04x\n",
ntohs(greh->flags));
}
switch (ntohs(greh->ptype)) {
case GRE_PTYPE_IPv4:
/* IPv4 messages might be GRE keepalives */
*error = handle_rx_gre_ipv4(bfd, msg, iph, greh);
goto out_err;
break;
case GRE_PTYPE_FR:
/* continue as usual */
break;
default:
LOGP(DNS, LOGL_NOTICE, "Unknown GRE protocol 0x%04x != FR\n",
ntohs(greh->ptype));
*error = -EIO;
goto out_err;
break;
}
if (msg->len < sizeof(*greh) + 2) {
LOGP(DNS, LOGL_ERROR, "Short FR header: %u bytes\n", msg->len);
*error = -EIO;
goto out_err;
}
frh = (uint8_t *)greh + sizeof(*greh);
if (frh[0] & 0x01) {
LOGP(DNS, LOGL_NOTICE, "Unsupported single-byte FR address\n");
*error = -EIO;
goto out_err;
}
dlci = ((frh[0] & 0xfc) << 2);
if ((frh[1] & 0x0f) != 0x01) {
LOGP(DNS, LOGL_NOTICE, "Unknown second FR octet 0x%02x\n",
frh[1]);
*error = -EIO;
goto out_err;
}
dlci |= (frh[1] >> 4);
msg->l2h = frh+2;
/* Store DLCI in NETWORK BYTEORDER in sockaddr port member */
saddr->sin_port = htons(dlci);
return msg;
out_err:
msgb_free(msg);
return NULL;
}
int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
struct sockaddr_in *saddr, enum gprs_ns_ll ll);
static int handle_nsfrgre_read(struct osmo_fd *bfd)
{
int rc;
struct sockaddr_in saddr;
struct gprs_ns_inst *nsi = bfd->data;
struct msgb *msg;
uint16_t dlci;
msg = read_nsfrgre_msg(bfd, &rc, &saddr);
if (!msg)
return rc;
dlci = ntohs(saddr.sin_port);
if (dlci == 0 || dlci == 1023) {
LOGP(DNS, LOGL_INFO, "Received FR on LMI DLCI %u - ignoring\n",
dlci);
rc = 0;
goto out;
}
rc = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_FR_GRE);
out:
msgb_free(msg);
return rc;
}
static int handle_nsfrgre_write(struct osmo_fd *bfd)
{
/* FIXME: actually send the data here instead of nsip_sendmsg() */
return -EIO;
}
int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg)
{
int rc;
struct gprs_ns_inst *nsi = nsvc->nsi;
struct sockaddr_in daddr;
uint16_t dlci = ntohs(nsvc->frgre.bts_addr.sin_port);
uint8_t *frh;
struct gre_hdr *greh;
/* Build socket address for the packet destionation */
daddr.sin_family = AF_INET;
daddr.sin_addr = nsvc->frgre.bts_addr.sin_addr;
daddr.sin_port = IPPROTO_GRE;
/* Prepend the FR header */
frh = msgb_push(msg, 2);
frh[0] = (dlci >> 2) & 0xfc;
frh[1] = ((dlci & 0xf)<<4) | 0x01;
/* Prepend the GRE header */
greh = (struct gre_hdr *) msgb_push(msg, sizeof(*greh));
greh->flags = 0;
greh->ptype = htons(GRE_PTYPE_FR);
rc = sendto(nsi->frgre.fd.fd, msg->data, msg->len, 0,
(struct sockaddr *)&daddr, sizeof(daddr));
msgb_free(msg);
return rc;
}
static int nsfrgre_fd_cb(struct osmo_fd *bfd, unsigned int what)
{
int rc = 0;
if (what & BSC_FD_READ)
rc = handle_nsfrgre_read(bfd);
if (what & BSC_FD_WRITE)
rc = handle_nsfrgre_write(bfd);
return rc;
}
int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi)
{
int rc;
/* Make sure we close any existing socket before changing it */
if (nsi->frgre.fd.fd)
close(nsi->frgre.fd.fd);
if (!nsi->frgre.enabled)
return 0;
rc = make_sock(&nsi->frgre.fd, IPPROTO_GRE, nsi->frgre.local_ip,
0, 0, nsfrgre_fd_cb, NULL);
if (rc < 0) {
LOGP(DNS, LOGL_ERROR, "Error creating GRE socket (%s)\n",
strerror(errno));
return rc;
}
nsi->frgre.fd.data = nsi;
return rc;
}

View File

@@ -1,571 +0,0 @@
/* VTY interface for our GPRS Networks Service (NS) implementation */
/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <openbsc/gsm_data.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <openbsc/debug.h>
#include <openbsc/signal.h>
#include <openbsc/gprs_ns.h>
#include <openbsc/gprs_bssgp.h>
#include <openbsc/vty.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/misc.h>
static struct gprs_ns_inst *vty_nsi = NULL;
/* FIXME: this should go to some common file as it is copied
* in vty_interface.c of the BSC */
static const struct value_string gprs_ns_timer_strs[] = {
{ 0, "tns-block" },
{ 1, "tns-block-retries" },
{ 2, "tns-reset" },
{ 3, "tns-reset-retries" },
{ 4, "tns-test" },
{ 5, "tns-alive" },
{ 6, "tns-alive-retries" },
{ 0, NULL }
};
static struct cmd_node ns_node = {
NS_NODE,
"%s(ns)#",
1,
};
static int config_write_ns(struct vty *vty)
{
struct gprs_nsvc *nsvc;
unsigned int i;
struct in_addr ia;
vty_out(vty, "ns%s", VTY_NEWLINE);
llist_for_each_entry(nsvc, &vty_nsi->gprs_nsvcs, list) {
if (!nsvc->persistent)
continue;
vty_out(vty, " nse %u nsvci %u%s",
nsvc->nsei, nsvc->nsvci, VTY_NEWLINE);
vty_out(vty, " nse %u remote-role %s%s",
nsvc->nsei, nsvc->remote_end_is_sgsn ? "sgsn" : "bss",
VTY_NEWLINE);
switch (nsvc->ll) {
case GPRS_NS_LL_UDP:
vty_out(vty, " nse %u encapsulation udp%s", nsvc->nsei,
VTY_NEWLINE);
vty_out(vty, " nse %u remote-ip %s%s",
nsvc->nsei,
inet_ntoa(nsvc->ip.bts_addr.sin_addr),
VTY_NEWLINE);
vty_out(vty, " nse %u remote-port %u%s",
nsvc->nsei, ntohs(nsvc->ip.bts_addr.sin_port),
VTY_NEWLINE);
break;
case GPRS_NS_LL_FR_GRE:
vty_out(vty, " nse %u encapsulation framerelay-gre%s",
nsvc->nsei, VTY_NEWLINE);
vty_out(vty, " nse %u remote-ip %s%s",
nsvc->nsei,
inet_ntoa(nsvc->frgre.bts_addr.sin_addr),
VTY_NEWLINE);
vty_out(vty, " nse %u fr-dlci %u%s",
nsvc->nsei, ntohs(nsvc->frgre.bts_addr.sin_port),
VTY_NEWLINE);
default:
break;
}
}
for (i = 0; i < ARRAY_SIZE(vty_nsi->timeout); i++)
vty_out(vty, " timer %s %u%s",
get_value_string(gprs_ns_timer_strs, i),
vty_nsi->timeout[i], VTY_NEWLINE);
if (vty_nsi->nsip.local_ip) {
ia.s_addr = htonl(vty_nsi->nsip.local_ip);
vty_out(vty, " encapsulation udp local-ip %s%s",
inet_ntoa(ia), VTY_NEWLINE);
}
if (vty_nsi->nsip.local_port)
vty_out(vty, " encapsulation udp local-port %u%s",
vty_nsi->nsip.local_port, VTY_NEWLINE);
vty_out(vty, " encapsulation framerelay-gre enabled %u%s",
vty_nsi->frgre.enabled ? 1 : 0, VTY_NEWLINE);
if (vty_nsi->frgre.local_ip) {
ia.s_addr = htonl(vty_nsi->frgre.local_ip);
vty_out(vty, " encapsulation framerelay-gre local-ip %s%s",
inet_ntoa(ia), VTY_NEWLINE);
}
return CMD_SUCCESS;
}
DEFUN(cfg_ns, cfg_ns_cmd,
"ns",
"Configure the GPRS Network Service")
{
vty->node = NS_NODE;
return CMD_SUCCESS;
}
static void dump_nse(struct vty *vty, struct gprs_nsvc *nsvc, int stats)
{
vty_out(vty, "NSEI %5u, NS-VC %5u, Remote: %-4s, %5s %9s",
nsvc->nsei, nsvc->nsvci,
nsvc->remote_end_is_sgsn ? "SGSN" : "BSS",
nsvc->state & NSE_S_ALIVE ? "ALIVE" : "DEAD",
nsvc->state & NSE_S_BLOCKED ? "BLOCKED" : "UNBLOCKED");
if (nsvc->ll == GPRS_NS_LL_UDP || nsvc->ll == GPRS_NS_LL_FR_GRE)
vty_out(vty, ", %s %15s:%u",
nsvc->ll == GPRS_NS_LL_UDP ? "UDP " : "FR-GRE",
inet_ntoa(nsvc->ip.bts_addr.sin_addr),
ntohs(nsvc->ip.bts_addr.sin_port));
vty_out(vty, "%s", VTY_NEWLINE);
if (stats)
vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
}
static void dump_ns(struct vty *vty, struct gprs_ns_inst *nsi, int stats)
{
struct gprs_nsvc *nsvc;
struct in_addr ia;
ia.s_addr = htonl(vty_nsi->nsip.local_ip);
vty_out(vty, "Encapsulation NS-UDP-IP Local IP: %s, UDP Port: %u%s",
inet_ntoa(ia), vty_nsi->nsip.local_port, VTY_NEWLINE);
ia.s_addr = htonl(vty_nsi->frgre.local_ip);
vty_out(vty, "Encapsulation NS-FR-GRE-IP Local IP: %s%s",
inet_ntoa(ia), VTY_NEWLINE);
llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
if (nsvc == nsi->unknown_nsvc)
continue;
dump_nse(vty, nsvc, stats);
}
}
DEFUN(show_ns, show_ns_cmd, "show ns",
SHOW_STR "Display information about the NS protocol")
{
struct gprs_ns_inst *nsi = vty_nsi;
dump_ns(vty, nsi, 0);
return CMD_SUCCESS;
}
DEFUN(show_ns_stats, show_ns_stats_cmd, "show ns stats",
SHOW_STR
"Display information about the NS protocol\n"
"Include statistics\n")
{
struct gprs_ns_inst *nsi = vty_nsi;
dump_ns(vty, nsi, 1);
return CMD_SUCCESS;
}
DEFUN(show_nse, show_nse_cmd, "show ns (nsei|nsvc) <0-65535> [stats]",
SHOW_STR "Display information about the NS protocol\n"
"Select one NSE by its NSE Identifier\n"
"Select one NSE by its NS-VC Identifier\n"
"The Identifier of selected type\n"
"Include Statistics\n")
{
struct gprs_ns_inst *nsi = vty_nsi;
struct gprs_nsvc *nsvc;
uint16_t id = atoi(argv[1]);
int show_stats = 0;
if (!strcmp(argv[0], "nsei"))
nsvc = nsvc_by_nsei(nsi, id);
else
nsvc = nsvc_by_nsvci(nsi, id);
if (!nsvc) {
vty_out(vty, "No such NS Entity%s", VTY_NEWLINE);
return CMD_WARNING;
}
if (argc >= 3)
show_stats = 1;
dump_nse(vty, nsvc, show_stats);
return CMD_SUCCESS;
}
#define NSE_CMD_STR "Persistent NS Entity\n" "NS Entity ID (NSEI)\n"
DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd,
"nse <0-65535> nsvci <0-65534>",
NSE_CMD_STR
"NS Virtual Connection\n"
"NS Virtual Connection ID (NSVCI)\n"
)
{
uint16_t nsei = atoi(argv[0]);
uint16_t nsvci = atoi(argv[1]);
struct gprs_nsvc *nsvc;
nsvc = nsvc_by_nsei(vty_nsi, nsei);
if (!nsvc) {
nsvc = nsvc_create(vty_nsi, nsvci);
nsvc->nsei = nsei;
}
nsvc->nsvci = nsvci;
/* All NSVCs that are explicitly configured by VTY are
* marked as persistent so we can write them to the config
* file at some later point */
nsvc->persistent = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_nse_remoteip, cfg_nse_remoteip_cmd,
"nse <0-65535> remote-ip A.B.C.D",
NSE_CMD_STR
"Remote IP Address\n"
"Remote IP Address\n")
{
uint16_t nsei = atoi(argv[0]);
struct gprs_nsvc *nsvc;
nsvc = nsvc_by_nsei(vty_nsi, nsei);
if (!nsvc) {
vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
return CMD_WARNING;
}
inet_aton(argv[1], &nsvc->ip.bts_addr.sin_addr);
return CMD_SUCCESS;
}
DEFUN(cfg_nse_remoteport, cfg_nse_remoteport_cmd,
"nse <0-65535> remote-port <0-65535>",
NSE_CMD_STR
"Remote UDP Port\n"
"Remote UDP Port Number\n")
{
uint16_t nsei = atoi(argv[0]);
uint16_t port = atoi(argv[1]);
struct gprs_nsvc *nsvc;
nsvc = nsvc_by_nsei(vty_nsi, nsei);
if (!nsvc) {
vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
return CMD_WARNING;
}
if (nsvc->ll != GPRS_NS_LL_UDP) {
vty_out(vty, "Cannot set UDP Port on non-UDP NSE%s",
VTY_NEWLINE);
return CMD_WARNING;
}
nsvc->ip.bts_addr.sin_port = htons(port);
return CMD_SUCCESS;
}
DEFUN(cfg_nse_fr_dlci, cfg_nse_fr_dlci_cmd,
"nse <0-65535> fr-dlci <16-1007>",
NSE_CMD_STR
"Frame Relay DLCI\n"
"Frame Relay DLCI Number\n")
{
uint16_t nsei = atoi(argv[0]);
uint16_t dlci = atoi(argv[1]);
struct gprs_nsvc *nsvc;
nsvc = nsvc_by_nsei(vty_nsi, nsei);
if (!nsvc) {
vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
return CMD_WARNING;
}
if (nsvc->ll != GPRS_NS_LL_FR_GRE) {
vty_out(vty, "Cannot set FR DLCI on non-FR NSE%s",
VTY_NEWLINE);
return CMD_WARNING;
}
nsvc->frgre.bts_addr.sin_port = htons(dlci);
return CMD_SUCCESS;
}
DEFUN(cfg_nse_encaps, cfg_nse_encaps_cmd,
"nse <0-65535> encapsulation (udp|framerelay-gre)",
NSE_CMD_STR
"Encapsulation for NS\n"
"UDP/IP Encapsulation\n" "Frame-Relay/GRE/IP Encapsulation\n")
{
uint16_t nsei = atoi(argv[0]);
struct gprs_nsvc *nsvc;
nsvc = nsvc_by_nsei(vty_nsi, nsei);
if (!nsvc) {
vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
return CMD_WARNING;
}
if (!strcmp(argv[1], "udp"))
nsvc->ll = GPRS_NS_LL_UDP;
else
nsvc->ll = GPRS_NS_LL_FR_GRE;
return CMD_SUCCESS;
}
DEFUN(cfg_nse_remoterole, cfg_nse_remoterole_cmd,
"nse <0-65535> remote-role (sgsn|bss)",
NSE_CMD_STR
"Remote NSE Role\n"
"Remote Peer is SGSN\n"
"Remote Peer is BSS\n")
{
uint16_t nsei = atoi(argv[0]);
struct gprs_nsvc *nsvc;
nsvc = nsvc_by_nsei(vty_nsi, nsei);
if (!nsvc) {
vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
return CMD_WARNING;
}
if (!strcmp(argv[1], "sgsn"))
nsvc->remote_end_is_sgsn = 1;
else
nsvc->remote_end_is_sgsn = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_no_nse, cfg_no_nse_cmd,
"no nse <0-65535>",
"Delete Persistent NS Entity\n"
"Delete " NSE_CMD_STR)
{
uint16_t nsei = atoi(argv[0]);
struct gprs_nsvc *nsvc;
nsvc = nsvc_by_nsei(vty_nsi, nsei);
if (!nsvc) {
vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
return CMD_WARNING;
}
if (!nsvc->persistent) {
vty_out(vty, "NSEI %u is not a persistent NSE%s",
nsei, VTY_NEWLINE);
return CMD_WARNING;
}
nsvc->persistent = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_ns_timer, cfg_ns_timer_cmd,
"timer " NS_TIMERS " <0-65535>",
"Network Service Timer\n"
NS_TIMERS_HELP "Timer Value\n")
{
int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
int val = atoi(argv[1]);
if (idx < 0 || idx >= ARRAY_SIZE(vty_nsi->timeout))
return CMD_WARNING;
vty_nsi->timeout[idx] = val;
return CMD_SUCCESS;
}
#define ENCAPS_STR "NS encapsulation options\n"
DEFUN(cfg_nsip_local_ip, cfg_nsip_local_ip_cmd,
"encapsulation udp local-ip A.B.C.D",
ENCAPS_STR "NS over UDP Encapsulation\n"
"Set the IP address on which we listen for NS/UDP\n"
"IP Address\n")
{
struct in_addr ia;
inet_aton(argv[0], &ia);
vty_nsi->nsip.local_ip = ntohl(ia.s_addr);
return CMD_SUCCESS;
}
DEFUN(cfg_nsip_local_port, cfg_nsip_local_port_cmd,
"encapsulation udp local-port <0-65535>",
ENCAPS_STR "NS over UDP Encapsulation\n"
"Set the UDP port on which we listen for NS/UDP\n"
"UDP port number\n")
{
unsigned int port = atoi(argv[0]);
vty_nsi->nsip.local_port = port;
return CMD_SUCCESS;
}
DEFUN(cfg_frgre_local_ip, cfg_frgre_local_ip_cmd,
"encapsulation framerelay-gre local-ip A.B.C.D",
ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
"Set the IP address on which we listen for NS/FR/GRE\n"
"IP Address\n")
{
struct in_addr ia;
if (!vty_nsi->frgre.enabled) {
vty_out(vty, "FR/GRE is not enabled%s", VTY_NEWLINE);
return CMD_WARNING;
}
inet_aton(argv[0], &ia);
vty_nsi->frgre.local_ip = ntohl(ia.s_addr);
return CMD_SUCCESS;
}
DEFUN(cfg_frgre_enable, cfg_frgre_enable_cmd,
"encapsulation framerelay-gre enabled (1|0)",
ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
"Enable or disable Frame Relay over GRE\n"
"Enable\n" "Disable\n")
{
int enabled = atoi(argv[0]);
vty_nsi->frgre.enabled = enabled;
return CMD_SUCCESS;
}
DEFUN(nsvc_nsei, nsvc_nsei_cmd,
"nsvc nsei <0-65535> (block|unblock|reset)",
"Perform an operation on a NSVC\n"
"NSEI to identify NS-VC Identifier (NS-VCI)\n"
"The NSEI\n"
"Initiate BLOCK procedure\n"
"Initiate UNBLOCK procedure\n"
"Initiate RESET procedure\n")
{
uint16_t nsvci = atoi(argv[0]);
const char *operation = argv[1];
struct gprs_nsvc *nsvc;
nsvc = nsvc_by_nsei(vty_nsi, nsvci);
if (!nsvc) {
vty_out(vty, "No such NSVCI (%u)%s", nsvci, VTY_NEWLINE);
return CMD_WARNING;
}
if (!strcmp(operation, "block"))
gprs_ns_tx_block(nsvc, NS_CAUSE_OM_INTERVENTION);
else if (!strcmp(operation, "unblock"))
gprs_ns_tx_unblock(nsvc);
else if (!strcmp(operation, "reset"))
gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
else
return CMD_WARNING;
return CMD_SUCCESS;
}
DEFUN(logging_fltr_nsvc,
logging_fltr_nsvc_cmd,
"logging filter nsvc (nsei|nsvci) <0-65535>",
LOGGING_STR FILTER_STR
"Filter based on NS Virtual Connection\n"
"Identify NS-VC by NSEI\n"
"Identify NS-VC by NSVCI\n"
"Numeric identifier\n")
{
struct log_target *tgt = osmo_log_vty2tgt(vty);
struct gprs_nsvc *nsvc;
uint16_t id = atoi(argv[1]);
if (!tgt)
return CMD_WARNING;
if (!strcmp(argv[0], "nsei"))
nsvc = nsvc_by_nsei(vty_nsi, id);
else
nsvc = nsvc_by_nsvci(vty_nsi, id);
if (!nsvc) {
vty_out(vty, "No NS-VC by that identifier%s", VTY_NEWLINE);
return CMD_WARNING;
}
log_set_nsvc_filter(tgt, nsvc);
return CMD_SUCCESS;
}
int gprs_ns_vty_init(struct gprs_ns_inst *nsi)
{
vty_nsi = nsi;
install_element_ve(&show_ns_cmd);
install_element_ve(&show_ns_stats_cmd);
install_element_ve(&show_nse_cmd);
install_element_ve(&logging_fltr_nsvc_cmd);
install_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
install_element(CONFIG_NODE, &cfg_ns_cmd);
install_node(&ns_node, config_write_ns);
install_default(NS_NODE);
install_element(NS_NODE, &ournode_exit_cmd);
install_element(NS_NODE, &ournode_end_cmd);
install_element(NS_NODE, &cfg_nse_nsvci_cmd);
install_element(NS_NODE, &cfg_nse_remoteip_cmd);
install_element(NS_NODE, &cfg_nse_remoteport_cmd);
install_element(NS_NODE, &cfg_nse_fr_dlci_cmd);
install_element(NS_NODE, &cfg_nse_encaps_cmd);
install_element(NS_NODE, &cfg_nse_remoterole_cmd);
install_element(NS_NODE, &cfg_no_nse_cmd);
install_element(NS_NODE, &cfg_ns_timer_cmd);
install_element(NS_NODE, &cfg_nsip_local_ip_cmd);
install_element(NS_NODE, &cfg_nsip_local_port_cmd);
install_element(NS_NODE, &cfg_frgre_enable_cmd);
install_element(NS_NODE, &cfg_frgre_local_ip_cmd);
install_element(ENABLE_NODE, &nsvc_nsei_cmd);
return 0;
}