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.
This commit is contained in:
Harald Welte 2012-06-17 23:34:34 +08:00
parent b46d566559
commit edcba4e307
23 changed files with 8 additions and 4453 deletions

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)
@ -114,9 +115,6 @@ AM_CONFIG_HEADER(bscconfig.h)
AC_OUTPUT(
openbsc.pc
include/openbsc/Makefile
include/osmocom/Makefile
include/osmocom/gprs/Makefile
include/osmocom/gprs/protocol/Makefile
include/Makefile
src/Makefile
src/libtrau/Makefile
@ -131,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

@ -1,3 +1,3 @@
SUBDIRS = openbsc osmocom
SUBDIRS = openbsc
noinst_HEADERS = mISDNif.h compat_af_isdn.h

View File

@ -1 +0,0 @@
SUBDIRS = gprs

View File

@ -1,5 +0,0 @@
libgb_HEADERS = gprs_bssgp.h gprs_ns.h gprs_ns_frgre.h gprs_msgb.h
libgbdir = $(includedir)/osmocom/gprs
SUBDIRS = protocol

View File

@ -1,166 +0,0 @@
#ifndef _GPRS_BSSGP_H
#define _GPRS_BSSGP_H
#include <stdint.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/gsm/prim.h>
#include <osmocom/gprs/protocol/gsm_08_18.h>
/* 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);
enum bssgp_prim {
PRIM_BSSGP_DL_UD,
PRIM_BSSGP_UL_UD,
PRIM_BSSGP_PTM_UD,
PRIM_BSSGP_GMM_SUSPEND,
PRIM_BSSGP_GMM_RESUME,
PRIM_BSSGP_GMM_PAGING,
PRIM_NM_FLUSH_LL,
PRIM_NM_LLC_DISCARDED,
PRIM_NM_BVC_RESET,
PRIM_NM_BVC_BLOCK,
PRIM_NM_BVC_UNBLOCK,
};
struct osmo_bssgp_prim {
struct osmo_prim_hdr oph;
/* common fields */
uint16_t nsei;
uint16_t bvci;
uint32_t tlli;
struct tlv_parsed *tp;
struct gprs_ra_id *ra_id;
/* specific fields */
union {
struct {
uint8_t *suspend_ref;
} resume;
} u;
};
/* 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>
#include <osmocom/gprs/gprs_msgb.h>
/* BSSGP-UL-UNITDATA.ind */
int bssgp_rcvmsg(struct msgb *msg);
/* BSSGP-DL-UNITDATA.req */
struct bssgp_lv {
uint16_t len;
uint8_t *v;
};
/* parameters for BSSGP downlink userdata transmission */
struct bssgp_dl_ud_par {
uint32_t *tlli;
char *imsi;
uint16_t drx_parms;
/* FIXME: priority */
struct bssgp_lv ms_ra_cap;
uint8_t qos_profile[3];
};
int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime,
struct bssgp_dl_ud_par *dup);
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 bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci,
struct bssgp_paging_info *pinfo);
/* gprs_bssgp_vty.c */
int bssgp_vty_init(void);
void bssgp_set_log_ss(int ss);
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx);
#endif /* _GPRS_BSSGP_H */

View File

@ -1,37 +0,0 @@
#ifndef _LIBGB_MSGB_H
#define _LIBGB_MSGB_H
#include <stdint.h>
/* the data structure stored in msgb->cb for libgb apps */
struct libgb_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 LIBGB_MSGB_CB(__msgb) ((struct libgb_msgb_cb *)&((__msgb)->cb[0]))
#define msgb_tlli(__x) LIBGB_MSGB_CB(__x)->tlli
#define msgb_nsei(__x) LIBGB_MSGB_CB(__x)->nsei
#define msgb_bvci(__x) LIBGB_MSGB_CB(__x)->bvci
#define msgb_gmmh(__x) (__x)->l3h
#define msgb_bssgph(__x) LIBGB_MSGB_CB(__x)->bssgph
#define msgb_bssgp_len(__x) ((__x)->tail - (uint8_t *)msgb_bssgph(__x))
#define msgb_bcid(__x) LIBGB_MSGB_CB(__x)->bssgp_cell_id
#define msgb_llch(__x) LIBGB_MSGB_CB(__x)->llch
/* logging contexts */
#define GPRS_CTX_NSVC 0
#define GPRS_CTX_BVC 1
#include <osmocom/core/logging.h>
int gprs_log_filter_fn(const struct log_context *ctx,
struct log_target *tar);
#endif

View File

@ -1,189 +0,0 @@
#ifndef _GPRS_NS_H
#define _GPRS_NS_H
#include <stdint.h>
/* 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>
#include <osmocom/gprs/gprs_msgb.h>
#include <osmocom/gprs/protocol/gsm_08_16.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, void *ctx);
/* 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);
/* Establish a connection (from the BSS) to the SGSN */
struct gprs_nsvc *gprs_ns_nsip_connect(struct gprs_ns_inst *nsi,
struct sockaddr_in *dest,
uint16_t nsei, uint16_t nsvci);
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);
struct gprs_nsvc *gprs_nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci);
void gprs_nsvc_delete(struct gprs_nsvc *nsvc);
struct gprs_nsvc *gprs_nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei);
struct gprs_nsvc *gprs_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");
}
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;
};
void gprs_ns_set_log_ss(int ss);
/*! }@ */
#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

@ -1,3 +0,0 @@
libgbp_HEADERS = gsm_08_16.h gsm_08_18.h
libgbpdir = $(includedir)/osmocom/gprs/protocol

View File

@ -1,85 +0,0 @@
#ifndef _OSMO_08_16_H
#define _OSMO_08_16_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) */
#include <stdint.h>
/*! \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,
};
#endif

View File

@ -1,144 +0,0 @@
#ifndef _OSMO_08_18_H
#define _OSMO_08_18_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,
};
#endif

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

@ -1,90 +0,0 @@
/* OpenBSC VTY common helpers */
/* (C) 2009-2012 by Harald Welte <laforge@gnumonks.org>
* (C) 2009-2010 by Holger Hans Peter Freyther
* 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 <string.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/logging.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/buffer.h>
#include <osmocom/vty/vty.h>
#include <osmocom/gprs/gprs_msgb.h>
#include "common_vty.h"
/* Down vty node level. */
gDEFUN(libgb_exit,
libgb_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
{
switch (vty->node) {
case L_NS_NODE:
case L_BSSGP_NODE:
vty->node = CONFIG_NODE;
vty->index = NULL;
break;
default:
break;
}
return CMD_SUCCESS;
}
/* End of configuration. */
gDEFUN(libgb_end,
libgb_end_cmd, "end", "End current mode and change to enable mode.")
{
switch (vty->node) {
case L_NS_NODE:
case L_BSSGP_NODE:
vty_config_unlock(vty);
vty->node = ENABLE_NODE;
vty->index = NULL;
vty->index_sub = NULL;
break;
default:
break;
}
return CMD_SUCCESS;
}
int gprs_log_filter_fn(const struct log_context *ctx,
struct log_target *tar)
{
const struct gprs_nsvc *nsvc = ctx->ctx[GPRS_CTX_NSVC];
const struct gprs_bvc *bvc = ctx->ctx[GPRS_CTX_BVC];
/* Filter on the NS Virtual Connection */
if ((tar->filter_map & (1 << FLT_NSVC)) != 0
&& nsvc && (nsvc == tar->filter_data[FLT_NSVC]))
return 1;
/* Filter on the NS Virtual Connection */
if ((tar->filter_map & (1 << FLT_BVC)) != 0
&& bvc && (bvc == tar->filter_data[FLT_BVC]))
return 1;
return 0;
}
int DNS, DBSSGP;

View File

@ -1,14 +0,0 @@
#include <osmocom/vty/command.h>
#include <osmocom/core/logging.h>
extern int DNS, DBSSGP;
enum log_filter {
_FLT_ALL = LOG_FILTER_ALL, /* libosmocore */
FLT_NSVC = 1,
FLT_BVC = 2,
};
extern struct cmd_element libgb_exit_cmd;
extern struct cmd_element libgb_end_cmd;

View File

@ -1,919 +0,0 @@
/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
/* (C) 2009-2012 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 <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_ns.h>
#include "common_vty.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 osmo_bssgp_prim nmp;
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);
}
/* Send NM_BVC_RESET.ind to NM */
memset(&nmp, 0, sizeof(nmp));
nmp.nsei = nsei;
nmp.bvci = bvci;
nmp.tp = tp;
nmp.ra_id = &bctx->ra_id;
osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_BVC_RESET,
PRIM_OP_INDICATION, msg);
bssgp_prim_cb(&nmp.oph, NULL);
/* 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)
{
struct osmo_bssgp_prim nmp;
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]);
/* Send NM_BVC_BLOCK.ind to NM */
memset(&nmp, 0, sizeof(nmp));
nmp.nsei = msgb_nsei(msg);
nmp.bvci = bvci;
nmp.tp = tp;
osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_BVC_BLOCK,
PRIM_OP_INDICATION, msg);
bssgp_prim_cb(&nmp.oph, NULL);
/* 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)
{
struct osmo_bssgp_prim nmp;
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;
/* Send NM_BVC_UNBLOCK.ind to NM */
memset(&nmp, 0, sizeof(nmp));
nmp.nsei = msgb_nsei(msg);
nmp.bvci = bvci;
nmp.tp = tp;
osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_BVC_UNBLOCK,
PRIM_OP_INDICATION, msg);
bssgp_prim_cb(&nmp.oph, NULL);
/* 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 osmo_bssgp_prim gbp;
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);
/* Send BSSGP_UL_UD.ind to NM */
memset(&gbp, 0, sizeof(gbp));
gbp.nsei = ctx->nsei;
gbp.bvci = ctx->bvci;
gbp.tlli = msgb_tlli(msg);
gbp.tp = tp;
osmo_prim_init(&gbp.oph, SAP_BSSGP_LL, PRIM_BSSGP_UL_UD,
PRIM_OP_INDICATION, msg);
return bssgp_prim_cb(&gbp.oph, NULL);
}
static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp,
struct bssgp_bvc_ctx *ctx)
{
struct osmo_bssgp_prim gbp;
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 */
memset(&gbp, 0, sizeof(gbp));
gbp.nsei = msgb_nsei(msg);
gbp.bvci = ctx->bvci;
gbp.tlli = tlli;
gbp.ra_id = &raid;
osmo_prim_init(&gbp.oph, SAP_BSSGP_GMM, PRIM_BSSGP_GMM_SUSPEND,
PRIM_OP_REQUEST, msg);
rc = bssgp_prim_cb(&gbp.oph, NULL);
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 osmo_bssgp_prim gbp;
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 */
memset(&gbp, 0, sizeof(gbp));
gbp.nsei = msgb_nsei(msg);
gbp.bvci = ctx->bvci;
gbp.tlli = tlli;
gbp.ra_id = &raid;
gbp.u.resume.suspend_ref = suspend_ref;
osmo_prim_init(&gbp.oph, SAP_BSSGP_GMM, PRIM_BSSGP_GMM_RESUME,
PRIM_OP_REQUEST, msg);
rc = bssgp_prim_cb(&gbp.oph, NULL);
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)
{
struct osmo_bssgp_prim nmp;
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]);
/* send NM_LLC_DISCARDED to NM */
memset(&nmp, 0, sizeof(nmp));
nmp.nsei = msgb_nsei(msg);
nmp.bvci = ctx->bvci;
nmp.tlli = tlli;
nmp.tp = tp;
osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_LLC_DISCARDED,
PRIM_OP_INDICATION, msg);
return bssgp_prim_cb(&nmp.oph, NULL);
}
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 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 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 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(GPRS_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 = 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 = bssgp_rx_ptp(msg, &tp, bctx);
return rc;
}
int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime,
struct bssgp_dl_ud_par *dup)
{
struct bssgp_bvc_ctx *bctx;
struct bssgp_ud_hdr *budh;
uint8_t llc_pdu_tlv_hdr_len = 2;
uint8_t *llc_pdu_tlv;
uint16_t msg_len = msg->len;
uint16_t bvci = msgb_bvci(msg);
uint16_t nsei = msgb_nsei(msg);
uint16_t _pdu_lifetime = htons(pdu_lifetime); /* centi-seconds */
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 (dup) {
/* Old TLLI to help BSS map from old->new */
if (dup->tlli) {
uint32_t tlli = htonl(*dup->tlli);
msgb_tvlv_push(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &tlli);
}
/* IMSI */
if (strlen(dup->imsi)) {
uint8_t mi[10];
int imsi_len = gsm48_generate_mid_from_imsi(mi, dup->imsi);
if (imsi_len > 2)
msgb_tvlv_push(msg, BSSGP_IE_IMSI,
imsi_len-2, mi+2);
}
/* DRX parameters */
drx_params = htons(dup->drx_parms);
msgb_tvlv_push(msg, BSSGP_IE_DRX_PARAMS, 2,
(uint8_t *) &drx_params);
/* FIXME: Priority */
/* MS Radio Access Capability */
if (dup->ms_ra_cap.len)
msgb_tvlv_push(msg, BSSGP_IE_MS_RADIO_ACCESS_CAP,
dup->ms_ra_cap.len, dup->ms_ra_cap.v);
}
/* prepend the 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, dup->qos_profile, sizeof(budh->qos_profile));
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 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);
}
void bssgp_set_log_ss(int ss)
{
DBSSGP = ss;
}

View File

@ -1,425 +0,0 @@
/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
/* (C) 2009-2012 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 <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_ns.h>
#include "common_vty.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 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,117 +0,0 @@
/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
/* (C) 2009-2012 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 <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_ns.h>
#include "common_vty.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,187 +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 <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 <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.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>
#include "common_vty.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 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;
}
}
static struct cmd_node bssgp_node = {
L_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 = L_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 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(L_BSSGP_NODE);
install_element(L_BSSGP_NODE, &libgb_exit_cmd);
install_element(L_BSSGP_NODE, &libgb_end_cmd);
//install_element(L_BSSGP_NODE, &cfg_bssgp_timer_cmd);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,310 +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 <osmocom/core/socket.h>
#include <osmocom/gprs/gprs_ns.h>
#include "common_vty.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)
{
struct in_addr in;
int rc;
in.s_addr = htonl(nsi->frgre.local_ip);
/* 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;
nsi->frgre.fd.cb = nsfrgre_fd_cb;
nsi->frgre.fd.data = nsi;
rc = osmo_sock_init_ofd(&nsi->frgre.fd, AF_INET, SOCK_RAW,
IPPROTO_GRE, inet_ntoa(in), 0,
OSMO_SOCK_F_BIND);
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,581 +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 <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 <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.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>
#include "common_vty.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 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;
}
}
static struct cmd_node ns_node = {
L_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 = L_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 = gprs_nsvc_by_nsei(nsi, id);
else
nsvc = gprs_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 = gprs_nsvc_by_nsei(vty_nsi, nsei);
if (!nsvc) {
nsvc = gprs_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 = gprs_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 = gprs_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 = gprs_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 = gprs_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 = gprs_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 = gprs_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 = gprs_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 = gprs_nsvc_by_nsei(vty_nsi, id);
else
nsvc = gprs_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(L_NS_NODE);
install_element(L_NS_NODE, &libgb_exit_cmd);
install_element(L_NS_NODE, &libgb_end_cmd);
install_element(L_NS_NODE, &cfg_nse_nsvci_cmd);
install_element(L_NS_NODE, &cfg_nse_remoteip_cmd);
install_element(L_NS_NODE, &cfg_nse_remoteport_cmd);
install_element(L_NS_NODE, &cfg_nse_fr_dlci_cmd);
install_element(L_NS_NODE, &cfg_nse_encaps_cmd);
install_element(L_NS_NODE, &cfg_nse_remoterole_cmd);
install_element(L_NS_NODE, &cfg_no_nse_cmd);
install_element(L_NS_NODE, &cfg_ns_timer_cmd);
install_element(L_NS_NODE, &cfg_nsip_local_ip_cmd);
install_element(L_NS_NODE, &cfg_nsip_local_port_cmd);
install_element(L_NS_NODE, &cfg_frgre_enable_cmd);
install_element(L_NS_NODE, &cfg_frgre_local_ip_cmd);
install_element(ENABLE_NODE, &nsvc_nsei_cmd);
return 0;
}

View File

@ -1,59 +0,0 @@
LIBOSMOGB_1.0 {
global:
bssgp_cause_str;
bssgp_create_cell_id;
bssgp_msgb_alloc;
bssgp_msgb_tlli_put;
bssgp_parse_cell_id;
bssgp_tx_bvc_block;
bssgp_tx_bvc_reset;
bssgp_tx_bvc_unblock;
bssgp_tx_flush_ll_ack;
bssgp_tx_llc_discarded;
bssgp_tx_ra_capa_upd;
bssgp_tx_radio_status_imsi;
bssgp_tx_radio_status_tlli;
bssgp_tx_radio_status_tmsi;
bssgp_tx_resume;
bssgp_tx_resume_ack;
bssgp_tx_resume_nack;
bssgp_tx_simple_bvci;
bssgp_tx_status;
bssgp_tx_suspend;
bssgp_tx_suspend_ack;
bssgp_tx_suspend_nack;
bssgp_tx_ul_ud;
bssgp_rcvmsg
bssgp_rx_paging
bssgp_set_log_ss
bssgp_tx_dl_ud
bssgp_tx_paging
bssgp_vty_init
gprs_ns_cause_str;
gprs_ns_destroy;
gprs_ns_frgre_listen;
gprs_ns_frgre_sendmsg;
gprs_ns_instantiate;
gprs_ns_nsip_listen;
gprs_ns_nsip_connect;
gprs_ns_rcvmsg;
gprs_ns_sendmsg;
gprs_ns_set_log_ss;
gprs_ns_tx_alive;
gprs_ns_tx_alive_ack;
gprs_ns_tx_block;
gprs_ns_tx_reset;
gprs_ns_tx_status;
gprs_ns_tx_unblock;
gprs_ns_vty_init;
gprs_nsvc_create;
gprs_nsvc_delete;
gprs_nsvc_reset;
gprs_nsvc_by_nsvci;
gprs_nsvc_by_nsei;
local: *;
};