diff --git a/openbsc/include/openbsc/crc24.h b/openbsc/include/openbsc/crc24.h new file mode 100644 index 000000000..358fcb58f --- /dev/null +++ b/openbsc/include/openbsc/crc24.h @@ -0,0 +1,8 @@ +#ifndef _CRC24_H +#define _CRC24_H + +#define INIT_CRC24 0xffffff + +u_int32_t crc24_calc(u_int32_t fcs, u_int8_t *cp, unsigned int len); + +#endif diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h index f1c5a699a..49b417e26 100644 --- a/openbsc/include/openbsc/debug.h +++ b/openbsc/include/openbsc/debug.h @@ -29,6 +29,7 @@ enum { DHO, DDB, DREF, + DGPRS, Debug_LastEntry, }; diff --git a/openbsc/include/openbsc/gprs_bssgp.h b/openbsc/include/openbsc/gprs_bssgp.h new file mode 100644 index 000000000..f85ac48ec --- /dev/null +++ b/openbsc/include/openbsc/gprs_bssgp.h @@ -0,0 +1,138 @@ +#ifndef _GPRS_BSSGP_H +#define _GPRS_BSSGP_H + +/* Section 11.3.26 / Table 11.27 */ +enum bssgp_pdu_type { + /* PDUs between RL and BSSGP SAPs */ + BSSGP_PDUT_DL_UNITDATA = 0x00, + BSSGP_PDUT_UL_UNITDATA = 0x01, + BSSGP_PDUT_RA_CAPABILITY = 0x02, + BSSGP_PDUT_PTM_UNITDATA = 0x03, + /* PDUs between GMM SAPs */ + BSSGP_PDUT_PAGING_PS = 0x06, + BSSGP_PDUT_PAGING_CS = 0x07, + BSSGP_PDUT_RA_CAPA_UDPATE = 0x08, + BSSGP_PDUT_RA_CAPA_UPDATE_ACK = 0x09, + BSSGP_PDUT_RADIO_STATUS = 0x0a, + BSSGP_PDUT_SUSPEND = 0x0b, + BSSGP_PDUT_SUSPEND_ACK = 0x0c, + BSSGP_PDUT_SUSPEND_NACK = 0x0d, + BSSGP_PDUT_RESUME = 0x0e, + BSSGP_PDUT_RESUME_ACK = 0x0f, + BSSGP_PDUT_RESUME_NACK = 0x10, + /* PDus between NM SAPs */ + BSSGP_PDUT_BVC_BLOCK = 0x20, + BSSGP_PDUT_BVC_BLOCK_ACK = 0x21, + BSSGP_PDUT_BVC_RESET = 0x22, + BSSGP_PDUT_BVC_RESET_ACK = 0x23, + BSSGP_PDUT_BVC_UNBLOCK = 0x24, + BSSGP_PDUT_BVC_UNBLOCK_ACK = 0x25, + BSSGP_PDUT_FLOW_CONTROL_BVC = 0x26, + BSSGP_PDUT_FLOW_CONTROL_BVC_ACK = 0x27, + BSSGP_PDUT_FLOW_CONTROL_MS = 0x28, + BSSGP_PDUT_FLOW_CONTROL_MS_ACK = 0x29, + BSSGP_PDUT_FLUSH_LL = 0x2a, + BSSGP_PDUT_FLUSH_LL_ACK = 0x2b, + BSSGP_PDUT_LLC_DISCARD = 0x2c, + BSSGP_PDUT_SGSN_INVOKE_TRACE = 0x40, + BSSGP_PDUT_STATUS = 0x41, + /* PDUs between PFM SAP's */ + BSSGP_PDUT_DOWNLOAD_BSS_PFC = 0x50, + BSSGP_PDUT_CREATE_BSS_PFC = 0x51, + BSSGP_PDUT_CREATE_BSS_PFC_ACK = 0x52, + BSSGP_PDUT_CREATE_BSS_PFC_NACK = 0x53, + BSSGP_PDUT_MODIFY_BSS_PFC = 0x54, + BSSGP_PDUT_MODIFY_BSS_PFC_ACK = 0x55, + BSSGP_PDUT_DELETE_BSS_PFC = 0x56, + BSSGP_PDUT_DELETE_BSS_PFC_ACK = 0x57, +}; + +/* Section 10.2.1 and 10.2.2 */ +struct bssgp_ud_hdr { + u_int8_t pdu_type; + u_int32_t tlli; + u_int8_t qos_profile[3]; + u_int8_t data[0]; /* TLV's */ +} __attribute__((packed)); + +struct bssgp_normal_hdr { + u_int8_t pdu_type; + u_int8_t data[0]; /* TLV's */ +}; + +enum bssgp_iei_type { + BSSGP_IE_ALIGNMENT = 0x00, + BSSGP_IE_BMAX_DEFAULT_MS = 0x01, + BSSGP_IE_BSS_AREA_ID = 0x02, + BSSGP_IE_BUCKET_LEAK_RATE = 0x03, + BSSGP_IE_BVCI = 0x04, + BSSGP_IE_BVC_BUCKET_SIZE = 0x05, + BSSGP_IE_BVC_MEASUREMENT = 0x06, + BSSGP_IE_CAUSE = 0x07, + BSSGP_IE_CELL_ID = 0x08, + BSSGP_IE_CHAN_NEEDED = 0x09, + BSSGP_IE_DRX_PARAMS = 0x0a, + BSSGP_IE_EMLPP_PRIO = 0x0b, + BSSGP_IE_FLUSH_ACTION = 0x0c, + BSSGP_IE_IMSI = 0x0d, + BSSGP_IE_LLC_PDU = 0x0e, + BSSGP_IE_LLC_FRAMES_DISCARDED = 0x0f, + BSSGP_IE_LOCATION_AREA = 0x10, + BSSGP_IE_MOBILE_ID = 0x11, + BSSGP_IE_MS_BUCKET_SIZE = 0x12, + BSSGP_IE_MS_RADIO_ACCESS_CAP = 0x13, + BSSGP_IE_OMC_ID = 0x14, + BSSGP_IE_PDU_IN_ERROR = 0x15, + BSSGP_IE_PDU_LIFETIME = 0x16, + BSSGP_IE_PRIORITY = 0x17, + BSSGP_IE_QOS_PROFILE = 0x18, + BSSGP_IE_RADIO_CAUSE = 0x19, + BSSGP_IE_RA_CAP_UPD_CAUSE = 0x1a, + BSSGP_IE_ROUTEING_AREA = 0x1b, + BSSGP_IE_R_DEFAULT_MS = 0x1c, + BSSGP_IE_SUSPEND_REF_NR = 0x1d, + BSSGP_IE_TAG = 0x1e, + BSSGP_IE_TLLI = 0x1f, + BSSGP_IE_TMSI = 0x20, + BSSGP_IE_TRACE_REFERENC = 0x21, + BSSGP_IE_TRACE_TYPE = 0x22, + BSSGP_IE_TRANSACTION_ID = 0x23, + BSSGP_IE_TRIGGER_ID = 0x24, + BSSGP_IE_NUM_OCT_AFF = 0x25, + BSSGP_IE_LSA_ID_LIST = 0x26, + BSSGP_IE_LSA_INFORMATION = 0x27, + BSSGP_IE_PACKET_FLOW_ID = 0x28, + BSSGP_IE_PACKET_FLOW_TIMER = 0x29, + BSSGP_IE_AGG_BSS_QOS_PROFILE = 0x3a, + BSSGP_IE_FEATURE_BITMAP = 0x3b, + BSSGP_IE_BUCKET_FULL_RATIO = 0x3c, + BSSGP_IE_SERVICE_UTRAN_CCO = 0x3d, +}; + +/* Section 11.3.8 / Table 11.10: Cause coding */ +enum gprs_bssgp_cause { + BSSGP_CAUSE_PROC_OVERLOAD = 0x00, + BSSGP_CAUSE_EQUIP_FAIL = 0x01, + BSSGP_CAUSE_TRASIT_NET_FAIL = 0x02, + BSSGP_CAUSE_CAPA_GREATER_0KPBS = 0x03, + BSSGP_CAUSE_UNKNOWN_MS = 0x04, + BSSGP_CAUSE_UNKNOWN_BVCI = 0x05, + BSSGP_CAUSE_CELL_TRAF_CONG = 0x06, + BSSGP_CAUSE_SGSN_CONG = 0x07, + BSSGP_CAUSE_OML_INTERV = 0x08, + BSSGP_CAUSE_BVCI_BLOCKED = 0x09, + BSSGP_CAUSE_PFC_CREATE_FAIL = 0x0a, + BSSGP_CAUSE_SEM_INCORR_PDU = 0x20, + BSSGP_CAUSE_INV_MAND_INF = 0x21, + BSSGP_CAUSE_MISSING_MAND_IE = 0x22, + BSSGP_CAUSE_MISSING_COND_IE = 0x23, + BSSGP_CAUSE_UNEXP_COND_IE = 0x24, + BSSGP_CAUSE_COND_IE_ERR = 0x25, + BSSGP_CAUSE_PDU_INCOMP_STATE = 0x26, + BSSGP_CAUSE_PROTO_ERR_UNSPEC = 0x27, + BSSGP_CAUSE_PDU_INCOMP_FEAT = 0x28, +}; + +extern int gprs_bssgp_rcvmsg(struct msgb *msg, u_int16_t bvci); + +#endif /* _GPRS_BSSGP_H */ diff --git a/openbsc/include/openbsc/gprs_llc.h b/openbsc/include/openbsc/gprs_llc.h new file mode 100644 index 000000000..cd4e21523 --- /dev/null +++ b/openbsc/include/openbsc/gprs_llc.h @@ -0,0 +1,20 @@ +#ifndef _GPRS_LLC_H +#define _GPRS_LLC_H + +/* Section 4.7 LLC Layer Structure */ +enum gprs_llc_sapi { + GPRS_SAPI_GMM = 1, + GPRS_SAPI_TOM2 = 2, + GPRS_SAPI_SNDCP3 = 3, + GPRS_SAPI_SNDCP5 = 5, + GPRS_SAPI_SMS = 7, + GPRS_SAPI_TOM8 = 8, + GPRS_SAPI_SNDCP9 = 9, + GPRS_SAPI_SNDCP11 = 11, +}; + + +int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv); +int gprs_llc_tx_ui(struct msgb *msg, u_int8_t sapi, int command); + +#endif diff --git a/openbsc/include/openbsc/gprs_ns.h b/openbsc/include/openbsc/gprs_ns.h new file mode 100644 index 000000000..90f1adf3e --- /dev/null +++ b/openbsc/include/openbsc/gprs_ns.h @@ -0,0 +1,61 @@ +#ifndef _GPRS_NS_H +#define _GPRS_NS_H + +struct gprs_ns_hdr { + u_int8_t pdu_type; + u_int8_t data[0]; +} __attribute__((packed)); + +/* TS 08.16, Section 10.3.7, Table 14 */ +enum ns_pdu_type { + NS_PDUT_UNITDATA = 0x00, + NS_PDUT_RESET = 0x02, + NS_PDUT_RESET_ACK = 0x03, + NS_PDUT_BLOCK = 0x04, + NS_PDUT_BLOCK_ACK = 0x05, + NS_PDUT_UNBLOCK = 0x06, + NS_PDUT_UNBLOCK_ACK = 0x07, + NS_PDUT_STATUS = 0x08, + NS_PDUT_ALIVE = 0x0a, + NS_PDUT_ALIVE_ACK = 0x0b, +}; + +/* TS 08.16, Section 10.3, Table 12 */ +enum ns_ctrl_ie { + NS_IE_CAUSE = 0x00, + NS_IE_VCI = 0x01, + NS_IE_PDU = 0x02, + NS_IE_BVCI = 0x03, + NS_IE_NSEI = 0x04, +}; + +/* TS 08.16, Section 10.3.2, Table 13 */ +enum ns_cause { + NS_CAUSE_TRANSIT_FAIL = 0x00, + NS_CAUSE_OM_INTERVENTION = 0x01, + NS_CAUSE_EQUIP_FAIL = 0x02, + NS_CAUSE_NSVC_BLOCKED = 0x03, + NS_CAUSE_NSVC_UNKNOWN = 0x04, + NS_CAUSE_BVCI_UNKNOWN = 0x05, + NS_CAUSE_SEM_INCORR_PDU = 0x08, + NS_CAUSE_PDU_INCOMP_PSTATE = 0x0a, + NS_CAUSE_PROTO_ERR_UNSPEC = 0x0b, + NS_CAUSE_INVAL_ESSENT_IE = 0x0c, + NS_CAUSE_MISSING_ESSENT_IE = 0x0d, +}; + +/* a layer 1 entity transporting NS frames */ +struct gprs_ns_link { + union { + struct { + int fd; + } ip; + }; +}; + + +int gprs_ns_rcvmsg(struct msgb *msg); + +int gprs_ns_sendmsg(struct gprs_ns_link *link, u_int16_t bvci, + struct msgb *msg); +#endif diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h new file mode 100644 index 000000000..87c7fa874 --- /dev/null +++ b/openbsc/include/openbsc/gprs_sgsn.h @@ -0,0 +1,107 @@ +#ifndef _GPRS_SGSN_H +#define _GPRS_SGSN_H + +/* TS 04.08 4.1.3.3 GMM mobility management states on the network side */ +enum gprs_mm_state { + GMM_DEREGISTERED, /* 4.1.3.3.1.1 */ + GMM_COMMON_PROC_INIT, /* 4.1.3.3.1.2 */ + GMM_REGISTERED_NORMAL, /* 4.1.3.3.2.1 */ + GMM_REGISTERED_SUSPENDED, /* 4.1.3.3.2.2 */ + GMM_DEREGISTERED_INIT, /* 4.1.3.3.1.4 */ +}; + +enum gprs_ciph_algo { + GPRS_ALGO_GEA0, + GPRS_ALGO_GEA1, + GPRS_ALGO_GEA2, +}; + +#define MS_RADIO_ACCESS_CAPA + +/* According to TS 03.60, Table 5: SGSN MM and PDP Contexts */ +struct sgsn_mm_ctx { + struct llist_head list; + + char imsi[GSM_IMSI_LENGTH]; + enum gprs_mm_state mm_state; + u_int32_t p_tmsi; + u_int32_t p_tmsi_sig; + char imei[GSM_IMEI_LENGTH]; + char msisdn[GSM_EXTENSION_LENGTH]; + struct gprs_ra_id ra; + u_int16_t cell_id; + u_int32_t cell_id_age; + /* VLR number */ + u_int32_t new_sgsn_addr; + /* Authentication Triplets */ + /* Kc */ + /* CKSN */ + enum gprs_ciph_algo ciph_algo; + struct { + u_int8_t buf[14]; /* 10.5.5.12a */ + u_int8_t len; + } ms_radio_access_capa; + struct { + u_int8_t buf[4]; /* 10.5.5.12 */ + u_int8_t len; + } ms_network_capa; + u_int16_t drx_parms; + int mnrg; /* MS reported to HLR? */ + int ngaf; /* MS reported to MSC/VLR? */ + int ppf; /* paging for GPRS + non-GPRS? */ + /* SMS Parameters */ + int recovery; + u_int8_t radio_prio_sms; + + struct llist_head pdp_list; + + /* Additional bits not present in the GSM TS */ + u_int32_t tlli; + struct timer_list timer; + unsigned int T; +}; + +enum pdp_ctx_state { + PDP_STAE_NONE, +}; + +enum pdp_type { + PDP_TYPE_NONE, +}; + +struct sgsn_pdp_ctx { + struct llist_head list; + + unsigned int id; + enum pdp_ctx_state state; + enum pdp_type type; + u_int32_t addresss; + char *apn_subscribed; + char *apn_used; + u_int16_t nsapi; + u_int8_t ti; /* transaction identifier */ + u_int32_t ggsn_in_use; + int vplmn_allowed; + u_int32_t qos_profile_subscr; + u_int32_t qos_profile_req; + u_int32_t qos_profile_neg; + u_int8_t radio_prio; + u_int32_t tx_npdu_nr; + u_int32_t rx_npdu_nr; + u_int32_t tx_gtp_snd; + u_int32_t rx_gtp_snu; + u_int32_t charging_id; + int reordering_reqd; +}; + +/* look-up a SGSN MM context based on TLLI + RAI */ +struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(u_int32_t tlli, + const struct gprs_ra_id *raid); +struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(u_int32_t tmsi); +struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi); + +/* Allocate a new SGSN MM context */ +struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(u_int32_t tlli, + const struct gprs_ra_id *raid); + +#endif /* _GPRS_SGSN_H */ diff --git a/openbsc/include/openbsc/gsm_04_08_gprs.h b/openbsc/include/openbsc/gsm_04_08_gprs.h new file mode 100644 index 000000000..20ab04432 --- /dev/null +++ b/openbsc/include/openbsc/gsm_04_08_gprs.h @@ -0,0 +1,203 @@ +#ifndef _GSM48_GPRS_H +#define _GSM48_GPRS_H + +/* Table 10.4 / 10.4a, GPRS Mobility Management (GMM) */ +#define GSM48_MT_GMM_ATTACH_REQ 0x01 +#define GSM48_MT_GMM_ATTACH_ACK 0x02 +#define GSM48_MT_GMM_ATTACH_COMPL 0x03 +#define GSM48_MT_GMM_ATTACH_REJ 0x04 +#define GSM48_MT_GMM_DETACH_REQ 0x05 +#define GSM48_MT_GMM_DETACH_ACK 0x06 + +#define GSM48_MT_GMM_RA_UPD_REQ 0x08 +#define GSM48_MT_GMM_RA_UPD_ACK 0x09 +#define GSM48_MT_GMM_RA_UPD_COMPL 0x0a +#define GSM48_MT_GMM_RA_UPD_REJ 0x0b + +#define GSM48_MT_GMM_PTMSI_REALL_CMD 0x10 +#define GSM48_MT_GMM_PTMSI_REALL_COMPL 0x11 +#define GSM48_MT_GMM_AUTH_CIPH_REQ 0x12 +#define GSM48_MT_GMM_AUTH_CIPH_RESP 0x13 +#define GSM48_MT_GMM_AUTH_CIPH_REJ 0x14 +#define GSM48_MT_GMM_ID_REQ 0x15 +#define GSM48_MT_GMM_ID_RESP 0x16 +#define GSM48_MT_GMM_STATUS 0x20 +#define GSM48_MT_GMM_INFO 0x21 + +/* Table 10.4a, GPRS Session Management (GSM) */ +#define GSM48_MT_GSM_ACT_PDP_REQ 0x41 +#define GSM48_MT_GSM_ACT_PDP_ACK 0x42 +#define GSM48_MT_GSM_ACT_PDP_REJ 0x43 +#define GSM48_MT_GSM_REQ_PDP_ACT 0x44 +#define GSM48_MT_GSM_REQ_PDP_ACT_REJ 0x45 +#define GSM48_MT_GSM_DEACT_PDP_REQ 0x46 +#define GSM48_MT_GSM_DEACT_PDP_ACK 0x47 +#define GSM48_MT_GSM_ACT_AA_PDP_REQ 0x50 +#define GSM48_MT_GSM_ACT_AA_PDP_ACK 0x51 +#define GSM48_MT_GSM_ACT_AA_PDP_REJ 0x52 +#define GSM48_MT_GSM_DEACT_AA_PDP_REQ 0x53 +#define GSM48_MT_GSM_DEACT_AA_PDP_ACK 0x54 +#define GSM48_MT_GSM_STATUS 0x55 + +/* Chapter 10.5.5.2 / Table 10.5.135 */ +#define GPRS_ATT_T_ATTACH 1 +#define GPRS_ATT_T_ATT_WHILE_IMSI 2 +#define GPRS_ATT_T_COMBINED 3 + +/* Chapter 10.5.5.18 / Table 105.150 */ +#define GPRS_UPD_T_RA 0 +#define GPRS_UPD_T_RA_LA 1 +#define GPRS_UPD_T_RA_LA_IMSI_ATT 2 +#define GPRS_UPD_T_PERIODIC 3 + +enum gsm48_gprs_ie_mm { + GSM48_IE_GMM_TIMER_READY = 0x17, /* 10.5.7.3 */ + GSM48_IE_GMM_PTMSI_SIG = 0x19, /* 10.5.5.8 */ + GSM48_IE_GMM_AUTH_RAND = 0x21, /* 10.5.3.1 */ + GSM48_IE_GMM_AUTH_SRES = 0x22, /* 10.5.3.2 */ + GSM48_IE_GMM_IMEISV = 0x23, /* 10.5.1.4 */ + GSM48_IE_GMM_DRX_PARAM = 0x27, /* 10.5.5.6 */ + GSM48_IE_GMM_MS_NET_CAPA = 0x31, /* 10.5.5.12 */ +}; + +enum gsm48_gprs_ie_sm { + GSM48_IE_GSM_APN = 0x28, /* 10.5.6.1 */ + GSM48_IE_GSM_PROTO_CONF_OPT = 0x27, /* 10.5.6.3 */ + GSM48_IE_GSM_PDP_ADDR = 0x2b, /* 10.5.6.4 */ + GSM48_IE_GSM_AA_TMR = 0x29, /* 10.5.7.3 */ + GSM48_IE_GSM_NAME_FULL = 0x43, /* 10.5.3.5a */ + GSM48_IE_GSM_NAME_SHORT = 0x45, /* 10.5.3.5a */ + GSM48_IE_GSM_TIMEZONE = 0x46, /* 10.5.3.8 */ + GSM48_IE_GSM_UTC_AND_TZ = 0x47, /* 10.5.3.9 */ + GSM48_IE_GSM_LSA_ID = 0x48, /* 10.5.3.11 */ +}; + +/* Chapter 10.4.4.15 */ +struct gsm48_ra_id { + u_int8_t digits[3]; /* MCC + MNC BCD digits */ + u_int16_t lac; /* Location Area Code */ + u_int8_t rac; /* Routing Area Code */ +} __attribute__ ((packed)); + +/* Chapter 9.4.15 / Table 9.4.15 */ +struct gsm48_ra_upd_ack { + u_int8_t force_stby:4, /* 10.5.5.7 */ + upd_result:4; /* 10.5.5.17 */ + u_int8_t ra_upd_timer; /* 10.5.7.3 */ + struct gsm48_ra_id ra_id; /* 10.5.5.15 */ + u_int8_t data[0]; +} __attribute__((packed)); + +/* Chapter 10.5.7.3 */ +enum gsm48_gprs_tmr_unit { + GPRS_TMR_2SECONDS = 0 << 5, + GPRS_TMR_MINUTE = 1 << 5, + GPRS_TMR_6MINUTE = 2 << 5, + GPRS_TMR_DEACTIVATED = 3 << 5, +}; + +/* Chapter 9.4.2 / Table 9.4.2 */ +struct gsm48_attach_ack { + u_int8_t att_result:4, /* 10.5.5.7 */ + force_stby:4; /* 10.5.5.1 */ + u_int8_t ra_upd_timer; /* 10.5.7.3 */ + u_int8_t radio_prio; /* 10.5.7.2 */ + struct gsm48_ra_id ra_id; /* 10.5.5.15 */ + u_int8_t data[0]; +} __attribute__((packed)); + +/* Chapter 9.5.1 / Table 9.5.1 */ +struct gsm48_act_pdp_ctx_req { + u_int8_t req_nsapi; + u_int8_t req_llc_sapi; + u_int8_t req_qos_lv[4]; + u_int8_t data[0]; +} __attribute__((packed)); + +/* Chapter 9.5.2 / Table 9.5.2 */ +struct gsm48_act_pdp_ctx_ack { + u_int8_t llc_sapi; + u_int8_t qos_lv[4]; + u_int8_t radio_prio:4, + spare:4; + u_int8_t data[0]; +} __attribute__((packed)); + +/* Chapter 10.5.5.14 / Table 10.5.147 */ +enum gsm48_gmm_cause { + GMM_CAUSE_IMSI_UNKNOWN = 0x02, + GMM_CAUSE_ILLEGAL_MS = 0x03, + GMM_CAUSE_ILLEGAL_ME = 0x06, + GMM_CAUSE_GPRS_NOTALLOWED = 0x07, + GMM_CAUSE_GPRS_OTHER_NOTALLOWED = 0x08, + GMM_CAUSE_MS_ID_NOT_DERIVED = 0x09, + GMM_CAUSE_IMPL_DETACHED = 0x0a, + GMM_CAUSE_PLMN_NOTALLOWED = 0x0b, + GMM_CAUSE_LA_NOTALLOWED = 0x0c, + GMM_CAUSE_ROAMING_NOTALLOWED = 0x0d, + GMM_CAUSE_NO_GPRS_PLMN = 0x0e, + GMM_CAUSE_MSC_TEMP_NOTREACH = 0x10, + GMM_CAUSE_NET_FAIL = 0x11, + GMM_CAUSE_CONGESTION = 0x16, + GMM_CAUSE_SEM_INCORR_MSG = 0x5f, + GMM_CAUSE_INV_MAND_INFO = 0x60, + GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL = 0x61, + GMM_CAUSE_MSGT_INCOMP_P_STATE = 0x62, + GMM_CAUSE_IE_NOTEXIST_NOTIMPL = 0x63, + GMM_CAUSE_COND_IE_ERR = 0x64, + GMM_CAUSE_MSG_INCOMP_P_STATE = 0x65, + GMM_CAUSE_PROTO_ERR_UNSPEC = 0x6f, +}; + +/* Chapter 10.4.6.6 / Table 10.5.157 */ +enum gsm48_gsm_cause { + GSM_CAUSE_INSUFF_RSRC = 0x1a, + GSM_CAUSE_MISSING_APN = 0x1b, + GSM_CAUSE_UNKNOWN_PDP = 0x1c, + GSM_CAUSE_AUTH_FAILED = 0x1d, + GSM_CAUSE_ACT_REJ_GGSN = 0x1e, + GSM_CAUSE_ACT_REJ_UNSPEC = 0x1f, + GSM_CAUSE_SERV_OPT_NOTSUPP = 0x20, + GSM_CAUSE_REQ_SERV_OPT_NOTSUB = 0x21, + GSM_CAUSE_SERV_OPT_TEMP_OOO = 0x22, + GSM_CAUSE_NSAPI_IN_USE = 0x23, + GSM_CAUSE_DEACT_REGULAR = 0x24, + GSM_CAUSE_QOS_NOT_ACCEPTED = 0x25, + GSM_CAUSE_NET_FAIL = 0x26, + GSM_CAUSE_REACT_RQD = 0x27, + GSM_CAUSE_FEATURE_NOTSUPP = 0x28, + GSM_CAUSE_INVALID_TRANS_ID = 0x51, + GSM_CAUSE_SEM_INCORR_MSG = 0x5f, + GSM_CAUSE_INV_MAND_INFO = 0x60, + GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL = 0x61, + GSM_CAUSE_MSGT_INCOMP_P_STATE = 0x62, + GSM_CAUSE_IE_NOTEXIST_NOTIMPL = 0x63, + GSM_CAUSE_COND_IE_ERR = 0x64, + GSM_CAUSE_MSG_INCOMP_P_STATE = 0x65, + GSM_CAUSE_PROTO_ERR_UNSPEC = 0x6f, +}; + +/* GSM TS 03.03 Chapter 2.6 */ +enum gprs_tlli_tyoe { + TLLI_LOCAL, + TLLI_FOREIGN, + TLLI_RANDOM, + TLLI_AUXILIARY, + TLLI_RESERVED, +}; + +/* Section 6.1.2.2: Session management states on the network side */ +enum gsm48_pdp_state { + PDP_S_INACTIVE, + PDP_S_ACTIVE_PENDING, + PDP_S_ACTIVE, + PDP_S_INACTIVE_PENDING, + PDP_S_MODIFY_PENDING, +}; + +int gprs_tlli_type(u_int32_t tlli); + +struct gsm_bts *gsm48_bts_by_ra_id(struct gsm_network *net, + const u_int8_t *buf, unsigned int len); + +#endif /* _GSM48_GPRS_H */ diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am index 38150a9bf..718252f77 100644 --- a/openbsc/src/Makefile.am +++ b/openbsc/src/Makefile.am @@ -4,7 +4,7 @@ AM_LDFLAGS = $(LIBOSMOCORE_LIBS) sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config \ isdnsync bsc_mgcp ipaccess-proxy -noinst_LIBRARIES = libbsc.a libmsc.a libvty.a +noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libsccp.a libsgsn.a noinst_HEADERS = vty/cardshell.h bscdir = $(libdir) @@ -19,6 +19,9 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \ rtp_proxy.c bts_siemens_bs11.c bts_ipaccess_nanobts.c \ bts_unknown.c bsc_version.c bsc_api.c +libsgsn_a_SOURCES = gprs_ns.c gprs_bssgp.c gprs_llc.c gsm_04_08_gprs.c \ + crc24.c gprs_sgsn.c + libmsc_a_SOURCES = gsm_subscriber.c db.c \ mncc.c gsm_04_08.c gsm_04_11.c transaction.c \ token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c \ @@ -30,7 +33,7 @@ libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c \ libsccp_a_SOURCES = sccp/sccp.c bsc_hack_SOURCES = bsc_hack.c bsc_init.c vty_interface.c vty_interface_layer3.c -bsc_hack_LDADD = libmsc.a libbsc.a libmsc.a libvty.a -ldl -ldbi $(LIBCRYPT) +bsc_hack_LDADD = libmsc.a libbsc.a libmsc.a libsgsn.a libvty.a -ldl -ldbi $(LIBCRYPT) bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c debug.c \ rs232.c bts_siemens_bs11.c diff --git a/openbsc/src/crc24.c b/openbsc/src/crc24.c new file mode 100644 index 000000000..108212083 --- /dev/null +++ b/openbsc/src/crc24.c @@ -0,0 +1,69 @@ +/* GPRS LLC CRC-24 Implementation */ + +/* (C) 2008-2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include + +/* CRC24 table - FCS */ +static const u_int32_t tbl_crc24[256] = { + 0x00000000, 0x00d6a776, 0x00f64557, 0x0020e221, 0x00b78115, 0x00612663, 0x0041c442, 0x00976334, + 0x00340991, 0x00e2aee7, 0x00c24cc6, 0x0014ebb0, 0x00838884, 0x00552ff2, 0x0075cdd3, 0x00a36aa5, + 0x00681322, 0x00beb454, 0x009e5675, 0x0048f103, 0x00df9237, 0x00093541, 0x0029d760, 0x00ff7016, + 0x005c1ab3, 0x008abdc5, 0x00aa5fe4, 0x007cf892, 0x00eb9ba6, 0x003d3cd0, 0x001ddef1, 0x00cb7987, + 0x00d02644, 0x00068132, 0x00266313, 0x00f0c465, 0x0067a751, 0x00b10027, 0x0091e206, 0x00474570, + 0x00e42fd5, 0x003288a3, 0x00126a82, 0x00c4cdf4, 0x0053aec0, 0x008509b6, 0x00a5eb97, 0x00734ce1, + 0x00b83566, 0x006e9210, 0x004e7031, 0x0098d747, 0x000fb473, 0x00d91305, 0x00f9f124, 0x002f5652, + 0x008c3cf7, 0x005a9b81, 0x007a79a0, 0x00acded6, 0x003bbde2, 0x00ed1a94, 0x00cdf8b5, 0x001b5fc3, + 0x00fb4733, 0x002de045, 0x000d0264, 0x00dba512, 0x004cc626, 0x009a6150, 0x00ba8371, 0x006c2407, + 0x00cf4ea2, 0x0019e9d4, 0x00390bf5, 0x00efac83, 0x0078cfb7, 0x00ae68c1, 0x008e8ae0, 0x00582d96, + 0x00935411, 0x0045f367, 0x00651146, 0x00b3b630, 0x0024d504, 0x00f27272, 0x00d29053, 0x00043725, + 0x00a75d80, 0x0071faf6, 0x005118d7, 0x0087bfa1, 0x0010dc95, 0x00c67be3, 0x00e699c2, 0x00303eb4, + 0x002b6177, 0x00fdc601, 0x00dd2420, 0x000b8356, 0x009ce062, 0x004a4714, 0x006aa535, 0x00bc0243, + 0x001f68e6, 0x00c9cf90, 0x00e92db1, 0x003f8ac7, 0x00a8e9f3, 0x007e4e85, 0x005eaca4, 0x00880bd2, + 0x00437255, 0x0095d523, 0x00b53702, 0x00639074, 0x00f4f340, 0x00225436, 0x0002b617, 0x00d41161, + 0x00777bc4, 0x00a1dcb2, 0x00813e93, 0x005799e5, 0x00c0fad1, 0x00165da7, 0x0036bf86, 0x00e018f0, + 0x00ad85dd, 0x007b22ab, 0x005bc08a, 0x008d67fc, 0x001a04c8, 0x00cca3be, 0x00ec419f, 0x003ae6e9, + 0x00998c4c, 0x004f2b3a, 0x006fc91b, 0x00b96e6d, 0x002e0d59, 0x00f8aa2f, 0x00d8480e, 0x000eef78, + 0x00c596ff, 0x00133189, 0x0033d3a8, 0x00e574de, 0x007217ea, 0x00a4b09c, 0x008452bd, 0x0052f5cb, + 0x00f19f6e, 0x00273818, 0x0007da39, 0x00d17d4f, 0x00461e7b, 0x0090b90d, 0x00b05b2c, 0x0066fc5a, + 0x007da399, 0x00ab04ef, 0x008be6ce, 0x005d41b8, 0x00ca228c, 0x001c85fa, 0x003c67db, 0x00eac0ad, + 0x0049aa08, 0x009f0d7e, 0x00bfef5f, 0x00694829, 0x00fe2b1d, 0x00288c6b, 0x00086e4a, 0x00dec93c, + 0x0015b0bb, 0x00c317cd, 0x00e3f5ec, 0x0035529a, 0x00a231ae, 0x007496d8, 0x005474f9, 0x0082d38f, + 0x0021b92a, 0x00f71e5c, 0x00d7fc7d, 0x00015b0b, 0x0096383f, 0x00409f49, 0x00607d68, 0x00b6da1e, + 0x0056c2ee, 0x00806598, 0x00a087b9, 0x007620cf, 0x00e143fb, 0x0037e48d, 0x001706ac, 0x00c1a1da, + 0x0062cb7f, 0x00b46c09, 0x00948e28, 0x0042295e, 0x00d54a6a, 0x0003ed1c, 0x00230f3d, 0x00f5a84b, + 0x003ed1cc, 0x00e876ba, 0x00c8949b, 0x001e33ed, 0x008950d9, 0x005ff7af, 0x007f158e, 0x00a9b2f8, + 0x000ad85d, 0x00dc7f2b, 0x00fc9d0a, 0x002a3a7c, 0x00bd5948, 0x006bfe3e, 0x004b1c1f, 0x009dbb69, + 0x0086e4aa, 0x005043dc, 0x0070a1fd, 0x00a6068b, 0x003165bf, 0x00e7c2c9, 0x00c720e8, 0x0011879e, + 0x00b2ed3b, 0x00644a4d, 0x0044a86c, 0x00920f1a, 0x00056c2e, 0x00d3cb58, 0x00f32979, 0x00258e0f, + 0x00eef788, 0x003850fe, 0x0018b2df, 0x00ce15a9, 0x0059769d, 0x008fd1eb, 0x00af33ca, 0x007994bc, + 0x00dafe19, 0x000c596f, 0x002cbb4e, 0x00fa1c38, 0x006d7f0c, 0x00bbd87a, 0x009b3a5b, 0x004d9d2d +}; + +#define INIT_CRC24 0xffffff + +u_int32_t crc24_calc(u_int32_t fcs, u_int8_t *cp, unsigned int len) +{ + while (len--) + fcs = (fcs >> 8) ^ tbl_crc24[(fcs ^ *cp++) & 0xff]; + return fcs; +} diff --git a/openbsc/src/gprs_bssgp.c b/openbsc/src/gprs_bssgp.c new file mode 100644 index 000000000..de57d25df --- /dev/null +++ b/openbsc/src/gprs_bssgp.c @@ -0,0 +1,397 @@ +/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* global pointer to the gsm network data structure */ +/* FIXME: this must go! */ +extern struct gsm_network *bsc_gsmnet; + +/* Chapter 11.3.9 / Table 11.10: Cause coding */ +static const char *bssgp_cause_strings[] = { + [BSSGP_CAUSE_PROC_OVERLOAD] = "Processor overload", + [BSSGP_CAUSE_EQUIP_FAIL] = "Equipment Failure", + [BSSGP_CAUSE_TRASIT_NET_FAIL] = "Transit netowkr service failure", + [BSSGP_CAUSE_CAPA_GREATER_0KPBS]= "Transmission capacity modified", + [BSSGP_CAUSE_UNKNOWN_MS] = "Unknown MS", + [BSSGP_CAUSE_UNKNOWN_BVCI] = "Unknown BVCI", + [BSSGP_CAUSE_CELL_TRAF_CONG] = "Cell traffic congestion", + [BSSGP_CAUSE_SGSN_CONG] = "SGSN congestion", + [BSSGP_CAUSE_OML_INTERV] = "O&M intervention", + [BSSGP_CAUSE_BVCI_BLOCKED] = "BVCI blocked", + [BSSGP_CAUSE_PFC_CREATE_FAIL] = "PFC create failure", + [BSSGP_CAUSE_SEM_INCORR_PDU] = "Semantically incorrect PDU", + [BSSGP_CAUSE_INV_MAND_INF] = "Invalid mandatory information", + [BSSGP_CAUSE_MISSING_MAND_IE] = "Missing mandatory IE", + [BSSGP_CAUSE_MISSING_COND_IE] = "Missing conditional IE", + [BSSGP_CAUSE_UNEXP_COND_IE] = "Unexpected conditional IE", + [BSSGP_CAUSE_COND_IE_ERR] = "Conditional IE error", + [BSSGP_CAUSE_PDU_INCOMP_STATE] = "PDU incompatible with protocol state", + [BSSGP_CAUSE_PROTO_ERR_UNSPEC] = "Protocol error - unspecified", + [BSSGP_CAUSE_PDU_INCOMP_FEAT] = "PDU not compatible with feature set", +}; + +static const char *bssgp_cause_str(enum gprs_bssgp_cause cause) +{ + if (cause >= ARRAY_SIZE(bssgp_cause_strings)) + return "undefined"; + + if (bssgp_cause_strings[cause]) + return bssgp_cause_strings[cause]; + + return "undefined"; +} + +static inline int bssgp_tlv_parse(struct tlv_parsed *tp, u_int8_t *buf, int len) +{ + return tlv_parse(tp, &tvlv_att_def, buf, len, 0, 0); +} + +static inline struct msgb *bssgp_msgb_alloc(void) +{ + return msgb_alloc_headroom(4096, 128, "BSSGP"); +} + +/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */ +static int bssgp_tx_simple_bvci(u_int8_t pdu_type, u_int16_t bvci, u_int16_t ns_bvci) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + u_int16_t _bvci; + + bgph->pdu_type = pdu_type; + _bvci = htons(bvci); + msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (u_int8_t *) &_bvci); + + return gprs_ns_sendmsg(NULL, ns_bvci, msg); +} + +/* Chapter 10.4.5: Flow Control BVC ACK */ +static int bssgp_tx_fc_bvc_ack(u_int8_t tag, u_int16_t ns_bvci) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + + bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK; + msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag); + + return gprs_ns_sendmsg(NULL, ns_bvci, msg); +} + +/* Chapter 10.4.14: Status */ +static int bssgp_tx_status(u_int8_t cause, u_int16_t *bvci, struct msgb *orig_msg) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + + DEBUGPC(DGPRS, "BSSGP: TX STATUS, cause=%s\n", bssgp_cause_str(cause)); + + bgph->pdu_type = BSSGP_PDUT_STATUS; + msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause); + if (bvci) { + u_int16_t _bvci = htons(*bvci); + msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (u_int8_t *) &_bvci); + } + if (orig_msg) + msgb_tvlv_put(msg, BSSGP_IE_PDU_IN_ERROR, + msgb_l3len(orig_msg), orig_msg->l3h); + + return gprs_ns_sendmsg(NULL, 0, msg); +} + +/* Uplink unit-data */ +static int bssgp_rx_ul_ud(struct msgb *msg, u_int16_t bvci) +{ + struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msg->l3h; + struct gsm_bts *bts; + int data_len = msgb_l3len(msg) - sizeof(*budh); + struct tlv_parsed tp; + int rc; + + DEBUGP(DGPRS, "BSSGP UL-UD\n"); + + msg->tlli = ntohl(budh->tlli); + rc = bssgp_tlv_parse(&tp, budh->data, data_len); + + /* Cell ID and LLC_PDU are the only mandatory IE */ + if (!TLVP_PRESENT(&tp, BSSGP_IE_CELL_ID) || + !TLVP_PRESENT(&tp, BSSGP_IE_LLC_PDU)) + return -EIO; + + /* Determine the BTS based on the Cell ID */ + bts = gsm48_bts_by_ra_id(bsc_gsmnet, + TLVP_VAL(&tp, BSSGP_IE_CELL_ID), + TLVP_LEN(&tp, BSSGP_IE_CELL_ID)); + if (bts) + msg->trx = bts->c0; + + msg->llch = TLVP_VAL(&tp, BSSGP_IE_LLC_PDU); + + return gprs_llc_rcvmsg(msg, &tp); +} + +static int bssgp_rx_suspend(struct msgb *msg, u_int16_t bvci) +{ + struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msg->l3h; + int data_len = msgb_l3len(msg) - sizeof(*bgph); + struct tlv_parsed tp; + int rc; + + DEBUGP(DGPRS, "BSSGP SUSPEND\n"); + + rc = bssgp_tlv_parse(&tp, bgph->data, data_len); + if (rc < 0) + return rc; + + if (!TLVP_PRESENT(&tp, BSSGP_IE_TLLI) || + !TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) + return -EIO; + + /* SEND SUSPEND_ACK or SUSPEND_NACK */ + /* FIXME */ +} + +static int bssgp_rx_resume(struct msgb *msg, u_int16_t bvci) +{ + struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msg->l3h; + int data_len = msgb_l3len(msg) - sizeof(*bgph); + struct tlv_parsed tp; + int rc; + + DEBUGP(DGPRS, "BSSGP RESUME\n"); + + rc = bssgp_tlv_parse(&tp, bgph->data, data_len); + if (rc < 0) + return rc; + + if (!TLVP_PRESENT(&tp, BSSGP_IE_TLLI) || + !TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA) || + !TLVP_PRESENT(&tp, BSSGP_IE_SUSPEND_REF_NR)) + return -EIO; + + /* SEND RESUME_ACK or RESUME_NACK */ + /* FIXME */ +} + +static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp, + u_int16_t ns_bvci) +{ + + DEBUGP(DGPRS, "BSSGP FC BVC\n"); + + if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) || + !TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) || + !TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) || + !TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) || + !TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS)) + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); + + /* Send FLOW_CONTROL_BVC_ACK */ + return bssgp_tx_fc_bvc_ack(*TLVP_VAL(tp, BSSGP_IE_TAG), ns_bvci); +} +/* We expect msg->l3h to point to the BSSGP header */ +int gprs_bssgp_rcvmsg(struct msgb *msg, u_int16_t ns_bvci) +{ + struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msg->l3h; + struct tlv_parsed tp; + u_int8_t pdu_type = bgph->pdu_type; + int data_len = msgb_l3len(msg) - sizeof(*bgph); + u_int16_t bvci; + int rc = 0; + + if (pdu_type != BSSGP_PDUT_UL_UNITDATA && + pdu_type != BSSGP_PDUT_DL_UNITDATA) + rc = bssgp_tlv_parse(&tp, bgph->data, data_len); + + switch (pdu_type) { + case BSSGP_PDUT_UL_UNITDATA: + /* some LLC data from the MS */ + rc = bssgp_rx_ul_ud(msg, ns_bvci); + break; + case BSSGP_PDUT_RA_CAPABILITY: + /* BSS requests RA capability or IMSI */ + DEBUGP(DGPRS, "BSSGP RA CAPABILITY UPDATE\n"); + /* FIXME: send RA_CAPA_UPDATE_ACK */ + break; + case BSSGP_PDUT_RADIO_STATUS: + DEBUGP(DGPRS, "BSSGP RADIO STATUS\n"); + /* BSS informs us of some exception */ + break; + case BSSGP_PDUT_SUSPEND: + /* MS wants to suspend */ + rc = bssgp_rx_suspend(msg, ns_bvci); + break; + case BSSGP_PDUT_RESUME: + /* MS wants to resume */ + rc = bssgp_rx_resume(msg, ns_bvci); + break; + case BSSGP_PDUT_FLUSH_LL: + /* BSS informs MS has moved to one cell to other cell */ + DEBUGP(DGPRS, "BSSGP FLUSH LL\n"); + /* Send FLUSH_LL_ACK */ + break; + case BSSGP_PDUT_LLC_DISCARD: + /* BSS informs that some LLC PDU's have been discarded */ + DEBUGP(DGPRS, "BSSGP LLC DISCARDED\n"); + break; + case BSSGP_PDUT_FLOW_CONTROL_BVC: + /* BSS informs us of available bandwidth in Gb interface */ + rc = bssgp_rx_fc_bvc(msg, &tp, ns_bvci); + break; + case BSSGP_PDUT_FLOW_CONTROL_MS: + /* BSS informs us of available bandwidth to one MS */ + DEBUGP(DGPRS, "BSSGP FC MS\n"); + /* Send FLOW_CONTROL_MS_ACK */ + break; + case BSSGP_PDUT_BVC_BLOCK: + /* BSS tells us that BVC shall be blocked */ + DEBUGP(DGPRS, "BSSGP BVC BLOCK "); + if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI) || + !TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) + goto err_mand_ie; + bvci = ntohs(*(u_int16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI)); + DEBUGPC(DGPRS, "BVCI=%u, cause=%s\n", bvci, + bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE))); + rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, + bvci, ns_bvci); + break; + case BSSGP_PDUT_BVC_UNBLOCK: + /* BSS tells us that BVC shall be unblocked */ + DEBUGP(DGPRS, "BSSGP BVC UNBLOCK "); + if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) + goto err_mand_ie; + bvci = ntohs(*(u_int16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI)); + DEBUGPC(DGPRS, "BVCI=%u\n", bvci); + rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, + bvci, ns_bvci); + break; + case BSSGP_PDUT_BVC_RESET: + /* BSS tells us that BVC init is required */ + DEBUGP(DGPRS, "BSSGP BVC RESET "); + if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI) || + !TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) + goto err_mand_ie; + bvci = ntohs(*(u_int16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI)); + DEBUGPC(DGPRS, "BVCI=%u, cause=%s\n", bvci, + bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE))); + rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, + bvci, ns_bvci); + break; + case BSSGP_PDUT_STATUS: + /* Some exception has occurred */ + case BSSGP_PDUT_DOWNLOAD_BSS_PFC: + case BSSGP_PDUT_CREATE_BSS_PFC_ACK: + case BSSGP_PDUT_CREATE_BSS_PFC_NACK: + case BSSGP_PDUT_MODIFY_BSS_PFC: + case BSSGP_PDUT_DELETE_BSS_PFC_ACK: + DEBUGP(DGPRS, "BSSGP PDU type 0x%02x not [yet] implemented\n", + pdu_type); + break; + /* those only exist in the SGSN -> BSS direction */ + case BSSGP_PDUT_DL_UNITDATA: + case BSSGP_PDUT_PAGING_PS: + case BSSGP_PDUT_PAGING_CS: + case BSSGP_PDUT_RA_CAPA_UPDATE_ACK: + case BSSGP_PDUT_SUSPEND_ACK: + case BSSGP_PDUT_SUSPEND_NACK: + case BSSGP_PDUT_RESUME_ACK: + case BSSGP_PDUT_RESUME_NACK: + case BSSGP_PDUT_FLUSH_LL_ACK: + case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK: + case BSSGP_PDUT_FLOW_CONTROL_MS_ACK: + case BSSGP_PDUT_BVC_BLOCK_ACK: + case BSSGP_PDUT_BVC_UNBLOCK_ACK: + case BSSGP_PDUT_SGSN_INVOKE_TRACE: + DEBUGP(DGPRS, "BSSGP PDU type 0x%02x only exists in DL\n", + pdu_type); + rc = -EINVAL; + break; + default: + DEBUGP(DGPRS, "BSSGP PDU type 0x%02x unknown\n", pdu_type); + break; + } + + return rc; +err_mand_ie: + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); +} + +int gprs_bssgp_tx_dl_ud(struct msgb *msg) +{ + struct gsm_bts *bts; + struct bssgp_ud_hdr *budh; + u_int8_t llc_pdu_tlv_hdr_len = 2; + u_int8_t *llc_pdu_tlv, *qos_profile; + u_int16_t pdu_lifetime = 1000; /* centi-seconds */ + u_int8_t qos_profile_default[3] = { 0x00, 0x00, 0x21 }; + u_int16_t msg_len = msg->len; + + if (!msg->trx) { + DEBUGP(DGPRS, "Cannot transmit DL-UD without TRX assigned\n"); + return -EINVAL; + } + + bts = msg->trx->bts; + + if (msg->len > TVLV_MAX_ONEBYTE) + llc_pdu_tlv_hdr_len += 1; + + /* prepend the tag and length of the LLC-PDU TLV */ + llc_pdu_tlv = msgb_push(msg, llc_pdu_tlv_hdr_len); + llc_pdu_tlv[0] = BSSGP_IE_LLC_PDU; + if (llc_pdu_tlv_hdr_len > 2) { + llc_pdu_tlv[1] = msg_len >> 8; + llc_pdu_tlv[2] = msg_len & 0xff; + } else { + llc_pdu_tlv[1] = msg_len & 0x3f; + llc_pdu_tlv[1] |= 0x80; + } + + /* FIXME: optional elements */ + + /* prepend the pdu lifetime */ + pdu_lifetime = htons(pdu_lifetime); + msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (u_int8_t *)&pdu_lifetime); + + /* prepend the QoS profile, TLLI and pdu type */ + budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh)); + memcpy(budh->qos_profile, qos_profile_default, sizeof(qos_profile_default)); + budh->tlli = htonl(msg->tlli); + budh->pdu_type = BSSGP_PDUT_DL_UNITDATA; + + return gprs_ns_sendmsg(NULL, bts->gprs.cell.bvci, msg); +} diff --git a/openbsc/src/gprs_llc.c b/openbsc/src/gprs_llc.c new file mode 100644 index 000000000..2c927c6cf --- /dev/null +++ b/openbsc/src/gprs_llc.c @@ -0,0 +1,420 @@ +/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Section 4.5.2 Logical Link States + Annex C.2 */ +enum gprs_llc_ll_state { + GPRS_LLS_UNASSIGNED = 1, /* No TLLI yet */ + GPRS_LLS_ASSIGNED_ADM = 2, /* TLLI assigned */ + GPRS_LLS_LOCAL_EST = 3, /* Local Establishment */ + GPRS_LLS_REMOTE_EST = 4, /* Remote Establishment */ + GPRS_LLS_ABM = 5, + GPRS_LLS_LOCAL_REL = 6, /* Local Release */ + GPRS_LLS_TIMER_REC = 7, /* Timer Recovery */ +}; + +/* Section 4.7.1: Logical Link Entity: One per DLCI (TLLI + SAPI) */ +struct gprs_llc_lle { + struct llist_head list; + struct timer_list t200; + struct timer_list t201; /* wait for acknowledgement */ + + enum gprs_llc_ll_state state; + + u_int32_t tlli; + u_int32_t sapi; + + u_int8_t v_sent; + u_int8_t v_ack; + u_int8_t v_recv; + + unsigned int n200; + unsigned int retrans_ctr; +}; + +enum gprs_llc_cmd { + GPRS_LLC_NULL, + GPRS_LLC_RR, + GPRS_LLC_ACK, + GPRS_LLC_RNR, + GPRS_LLC_SACK, + GPRS_LLC_DM, + GPRS_LLC_DISC, + GPRS_LLC_UA, + GPRS_LLC_SABM, + GPRS_LLC_FRMR, + GPRS_LLC_XID, +}; + +struct gprs_llc_hdr_parsed { + u_int8_t sapi; + u_int8_t is_cmd:1, + ack_req:1, + is_encrypted:1; + u_int32_t seq_rx; + u_int32_t seq_tx; + u_int32_t fcs; + u_int32_t fcs_calc; + u_int8_t *data; + enum gprs_llc_cmd cmd; +}; + +#define LLC_ALLOC_SIZE 16384 +#define UI_HDR_LEN 3 +#define N202 4 +#define CRC24_LENGTH 3 + +static int gprs_llc_fcs(u_int8_t *data, unsigned int len) +{ + u_int32_t fcs_calc; + + fcs_calc = crc24_calc(INIT_CRC24, data, len); + fcs_calc = ~fcs_calc; + fcs_calc &= 0xffffff; + + return fcs_calc; +} + +/* transmit a simple U frame */ +static int gprs_llc_tx_u() +{ + struct msgb *msg = msgb_alloc(LLC_ALLOC_SIZE, "GPRS/LLC"); + + if (!msg) + return -ENOMEM; + + + + /* transmit the frame via BSSGP->NS->... */ +} + +static void t200_expired(void *data) +{ + struct gprs_llc_lle *lle = data; + + /* 8.5.1.3: Expiry of T200 */ + + if (lle->retrans_ctr >= lle->n200) { + /* FIXME: LLGM-STATUS-IND, LL-RELEASE-IND/CNF */ + lle->state = GPRS_LLS_ASSIGNED_ADM; + } + + switch (lle->state) { + case GPRS_LLS_LOCAL_EST: + /* retransmit SABM */ + /* re-start T200 */ + lle->retrans_ctr++; + break; + case GPRS_LLS_LOCAL_REL: + /* retransmit DISC */ + /* re-start T200 */ + lle->retrans_ctr++; + break; + } + +} + +static void t201_expired(void *data) +{ + struct gprs_llc_lle *lle = data; + + if (lle->retrans_ctr < lle->n200) { + /* transmit apropriate supervisory frame (8.6.4.1) */ + /* set timer T201 */ + lle->retrans_ctr++; + } +} + +/* Transmit a UI frame over the given SAPI */ +int gprs_llc_tx_ui(struct msgb *msg, u_int8_t sapi, int command) +{ + u_int8_t *fcs, *llch; + u_int8_t addr, ctrl[2]; + u_int32_t fcs_calc; + u_int16_t nu = 0; + + /* Address Field */ + addr = sapi & 0xf; + if (command) + addr |= 0x40; + + /* Control Field */ + ctrl[0] = 0xc0; + ctrl[0] |= nu >> 6; + ctrl[1] = (nu << 2) & 0xfc; + ctrl[1] |= 0x01; /* Protected Mode */ + + /* prepend LLC UI header */ + llch = msgb_push(msg, 3); + llch[0] = addr; + llch[1] = ctrl[0]; + llch[2] = ctrl[1]; + + /* append FCS to end of frame */ + fcs = msgb_put(msg, 3); + fcs_calc = gprs_llc_fcs(llch, fcs - llch); + fcs[0] = fcs_calc & 0xff; + fcs[1] = (fcs_calc >> 8) & 0xff; + fcs[2] = (fcs_calc >> 16) & 0xff; + + return gprs_bssgp_tx_dl_ud(msg); +} + +static int gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph) +{ + DEBUGP(DGPRS, "LLC SAPI=%u %c %c FCS=0x%06x(%s) ", + gph->sapi, gph->is_cmd ? 'C' : 'R', gph->ack_req ? 'A' : ' ', + gph->fcs, gph->fcs_calc == gph->fcs ? "correct" : "WRONG"); + + if (gph->cmd) + DEBUGPC(DGPRS, "CMD=%u ", gph->cmd); + + if (gph->data) + DEBUGPC(DGPRS, "DATA "); + + DEBUGPC(DGPRS, "\n"); +} +static int gprs_llc_hdr_rx(struct gprs_llc_hdr_parsed *gph, + struct gprs_llc_lle *lle) +{ + switch (gph->cmd) { + case GPRS_LLC_SABM: /* Section 6.4.1.1 */ + lle->v_sent = lle->v_ack = lle->v_recv = 0; + if (lle->state == GPRS_LLS_ASSIGNED_ADM) { + /* start re-establishment (8.7.1) */ + } + lle->state = GPRS_LLS_REMOTE_EST; + /* FIXME: Send UA */ + lle->state = GPRS_LLS_ABM; + /* FIXME: process data */ + break; + case GPRS_LLC_DISC: /* Section 6.4.1.2 */ + /* FIXME: Send UA */ + /* terminate ABM */ + lle->state = GPRS_LLS_ASSIGNED_ADM; + break; + case GPRS_LLC_UA: /* Section 6.4.1.3 */ + if (lle->state == GPRS_LLS_LOCAL_EST) + lle->state = GPRS_LLS_ABM; + break; + case GPRS_LLC_DM: /* Section 6.4.1.4: ABM cannot be performed */ + if (lle->state == GPRS_LLS_LOCAL_EST) + lle->state = GPRS_LLS_ASSIGNED_ADM; + break; + case GPRS_LLC_FRMR: /* Section 6.4.1.5 */ + break; + case GPRS_LLC_XID: /* Section 6.4.1.6 */ + break; + } + + return 0; +} + +/* parse a GPRS LLC header, also check for invalid frames */ +static int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp, + const u_int8_t *llc_hdr, int len) +{ + u_int8_t *ctrl = llc_hdr+1; + int is_sack = 0; + unsigned int crc_length; + u_int32_t fcs_calc; + + if (len <= CRC24_LENGTH) + return -EIO; + + crc_length = len - CRC24_LENGTH; + + ghp->ack_req = 0; + + /* Section 5.5: FCS */ + ghp->fcs = *(llc_hdr + len - 3); + ghp->fcs |= *(llc_hdr + len - 2) << 8; + ghp->fcs |= *(llc_hdr + len - 1) << 16; + + /* Section 6.2.1: invalid PD field */ + if (llc_hdr[0] & 0x80) + return -EIO; + + /* This only works for the MS->SGSN direction */ + if (llc_hdr[0] & 0x40) + ghp->is_cmd = 0; + else + ghp->is_cmd = 1; + + ghp->sapi = llc_hdr[0] & 0xf; + + /* Section 6.2.3: check for reserved SAPI */ + switch (ghp->sapi) { + case 0: + case 4: + case 6: + case 0xa: + case 0xc: + case 0xd: + case 0xf: + return -EINVAL; + } + + if ((ctrl[0] & 0x80) == 0) { + /* I (Information transfer + Supervisory) format */ + u_int8_t k; + + ghp->data = ctrl + 3; + + if (ctrl[0] & 0x40) + ghp->ack_req = 1; + + ghp->seq_tx = (ctrl[0] & 0x1f) << 4; + ghp->seq_tx |= (ctrl[1] >> 4); + + ghp->seq_rx = (ctrl[1] & 0x7) << 6; + ghp->seq_rx |= (ctrl[2] >> 2); + + switch (ctrl[2] & 0x03) { + case 0: + ghp->cmd = GPRS_LLC_RR; + break; + case 1: + ghp->cmd = GPRS_LLC_ACK; + break; + case 2: + ghp->cmd = GPRS_LLC_RNR; + break; + case 3: + ghp->cmd = GPRS_LLC_SACK; + k = ctrl[3] & 0x1f; + ghp->data += 1 + k; + break; + } + } else if ((ctrl[0] & 0xc0) == 0x80) { + /* S (Supervisory) format */ + ghp->data = NULL; + + if (ctrl[0] & 0x20) + ghp->ack_req = 1; + ghp->seq_rx = (ctrl[0] & 0x7) << 6; + ghp->seq_rx |= (ctrl[1] >> 2); + + switch (ctrl[1] & 0x03) { + case 0: + ghp->cmd = GPRS_LLC_RR; + break; + case 1: + ghp->cmd = GPRS_LLC_ACK; + break; + case 2: + ghp->cmd = GPRS_LLC_RNR; + break; + case 3: + ghp->cmd = GPRS_LLC_SACK; + break; + } + } else if ((ctrl[0] & 0xe0) == 0xc0) { + /* UI (Unconfirmed Inforamtion) format */ + ghp->data = ctrl + 2; + + ghp->seq_tx = (ctrl[0] & 0x7) << 6; + ghp->seq_tx |= (ctrl[1] >> 2); + if (ctrl[1] & 0x02) { + ghp->is_encrypted = 1; + /* FIXME: encryption */ + } + if (ctrl[1] & 0x01) { + /* FCS over hdr + all inf fields */ + } else { + /* FCS over hdr + N202 octets (4) */ + if (crc_length > UI_HDR_LEN + N202) + crc_length = UI_HDR_LEN + N202; + } + } else { + /* U (Unnumbered) format: 1 1 1 P/F M4 M3 M2 M1 */ + ghp->data = NULL; + + switch (ctrl[0] & 0xf) { + case 0: + ghp->cmd = GPRS_LLC_NULL; + break; + case 0x1: + ghp->cmd = GPRS_LLC_DM; + break; + case 0x4: + ghp->cmd = GPRS_LLC_DISC; + break; + case 0x6: + ghp->cmd = GPRS_LLC_UA; + break; + case 0x7: + ghp->cmd = GPRS_LLC_SABM; + break; + case 0x8: + ghp->cmd = GPRS_LLC_FRMR; + break; + case 0xb: + ghp->cmd = GPRS_LLC_XID; + break; + default: + return -EIO; + } + } + + /* calculate what FCS we expect */ + ghp->fcs_calc = gprs_llc_fcs(llc_hdr, crc_length); + + /* FIXME: parse sack frame */ +} + +/* receive an incoming LLC PDU */ +int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) +{ + struct bssgp_ud_hdr *udh = (struct bssgp_ud_hdr *) msg->l3h; + struct gprs_llc_hdr *lh = msg->llch; + struct gprs_llc_hdr_parsed llhp; + struct gprs_llc_entity *lle; + int rc; + + rc = gprs_llc_hdr_parse(&llhp, lh, TLVP_LEN(tv, BSSGP_IE_LLC_PDU)); + + /* FIXME: find LLC Entity */ + + gprs_llc_hdr_dump(&llhp); + rc = gprs_llc_hdr_rx(&llhp, lle); + + if (llhp.data) { + msg->gmmh = llhp.data; + switch (llhp.sapi) { + case GPRS_SAPI_GMM: + rc = gsm0408_gprs_rcvmsg(msg); + } + } + + return 0; +} diff --git a/openbsc/src/gprs_ns.c b/openbsc/src/gprs_ns.c new file mode 100644 index 000000000..a686a22aa --- /dev/null +++ b/openbsc/src/gprs_ns.c @@ -0,0 +1,348 @@ +/* GPRS Networks Service (NS) messages on the Gb interface + * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/* Some introduction into NS: NS is used typically on top of frame relay, + * but in the ip.access world it is encapsulated in UDP packets. It serves + * as an intermediate shim betwen BSSGP and the underlying medium. It doesn't + * do much, apart from providing congestion notification and status indication. + * + * Terms: + * NS Network Service + * NSVC NS Virtual Connection + * NSEI NS Entity Identifier + * NSVL NS Virtual Link + * NSVLI NS Virtual Link Identifier + * BVC BSSGP Virtual Connection + * BVCI BSSGP Virtual Connection Identifier + * NSVCG NS Virtual Connection Goup + * Blocked NS-VC cannot be used for user traffic + * Alive Ability of a NS-VC to provide communication + * + * There can be multiple BSSGP virtual connections over one (group of) NSVC's. BSSGP will + * therefore identify the BSSGP virtual connection by a BVCI passed down to NS. + * NS then has to firgure out which NSVC's are responsible for this BVCI. + * Those mappings are administratively configured. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define NS_ALLOC_SIZE 1024 + +static const struct tlv_definition ns_att_tlvdef = { + .def = { + [NS_IE_CAUSE] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_VCI] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_PDU] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_BVCI] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_NSEI] = { TLV_TYPE_TvLV, 0 }, + }, +}; + +#define NSE_S_BLOCKED 0x0001 +#define NSE_S_ALIVE 0x0002 + +struct gprs_nsvc { + struct llist_head list; + + u_int16_t nsei; /* end-to-end significance */ + u_int16_t nsvci; /* uniquely identifies NS-VC at SGSN */ + + u_int32_t state; + + struct timer_list alive_timer; + int timer_is_tns_alive; + int alive_retries; +}; + +/* FIXME: dynamically search for the matching NSVC */ +static struct gprs_nsvc dummy_nsvc = { .state = NSE_S_BLOCKED | NSE_S_ALIVE }; + +/* Section 10.3.2, Table 13 */ +static const char *ns_cause_str[] = { + [NS_CAUSE_TRANSIT_FAIL] = "Transit network failure", + [NS_CAUSE_OM_INTERVENTION] = "O&M intervention", + [NS_CAUSE_EQUIP_FAIL] = "Equipment failure", + [NS_CAUSE_NSVC_BLOCKED] = "NS-VC blocked", + [NS_CAUSE_NSVC_UNKNOWN] = "NS-VC unknown", + [NS_CAUSE_BVCI_UNKNOWN] = "BVCI unknown", + [NS_CAUSE_SEM_INCORR_PDU] = "Semantically incorrect PDU", + [NS_CAUSE_PDU_INCOMP_PSTATE] = "PDU not compatible with protocol state", + [NS_CAUSE_PROTO_ERR_UNSPEC] = "Protocol error, unspecified", + [NS_CAUSE_INVAL_ESSENT_IE] = "Invalid essential IE", + [NS_CAUSE_MISSING_ESSENT_IE] = "Missing essential IE", +}; + +static const char *gprs_ns_cause_str(enum ns_cause cause) +{ + if (cause >= ARRAY_SIZE(ns_cause_str)) + return "undefined"; + + if (ns_cause_str[cause]) + return ns_cause_str[cause]; + + return "undefined"; +} + +static int gprs_ns_tx(struct msgb *msg) +{ + return ipac_gprs_send(msg); +} + +static int gprs_ns_tx_simple(struct gprs_ns_link *link, u_int8_t pdu_type) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + + if (!msg) + return -ENOMEM; + + nsh = (struct gprs_ns_hdr *) msgb_put(msg, sizeof(*nsh)); + + nsh->pdu_type = pdu_type; + + return gprs_ns_tx(msg); +} + +#define NS_TIMER_ALIVE 3, 0 /* after 3 seconds without response, we retry */ +#define NS_TIMER_TEST 30, 0 /* every 10 seconds we check if the BTS is still alive */ +#define NS_ALIVE_RETRIES 10 /* after 3 failed retransmit we declare BTS as dead */ + +static void gprs_ns_alive_cb(void *data) +{ + struct gprs_nsvc *nsvc = data; + + if (nsvc->timer_is_tns_alive) { + /* Tns-alive case: we expired without response ! */ + nsvc->alive_retries++; + if (nsvc->alive_retries > NS_ALIVE_RETRIES) { + /* mark as dead and blocked */ + nsvc->state = NSE_S_BLOCKED; + DEBUGP(DGPRS, "Tns-alive more then %u retries, " + " blocking NS-VC\n", NS_ALIVE_RETRIES); + /* FIXME: inform higher layers */ + return; + } + } else { + /* Tns-test case: send NS-ALIVE PDU */ + gprs_ns_tx_simple(NULL, NS_PDUT_ALIVE); + /* start Tns-alive timer */ + nsvc->timer_is_tns_alive = 1; + } + bsc_schedule_timer(&nsvc->alive_timer, NS_TIMER_ALIVE); +} + +/* Section 9.2.6 */ +static int gprs_ns_tx_reset_ack(u_int16_t nsvci, u_int16_t nsei) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + + if (!msg) + return -ENOMEM; + + nsvci = htons(nsvci); + nsei = htons(nsei); + + nsh = (struct gprs_ns_hdr *) msgb_put(msg, sizeof(*nsh)); + + nsh->pdu_type = NS_PDUT_RESET_ACK; + + msgb_tvlv_put(msg, NS_IE_VCI, 2, (u_int8_t *)&nsvci); + msgb_tvlv_put(msg, NS_IE_NSEI, 2, (u_int8_t *)&nsei); + + return gprs_ns_tx(msg); +} + +/* Section 9.2.10: transmit side */ +int gprs_ns_sendmsg(struct gprs_ns_link *link, u_int16_t bvci, + struct msgb *msg) +{ + struct gprs_ns_hdr *nsh; + + nsh = (struct gprs_ns_hdr *) msgb_push(msg, sizeof(*nsh) + 3); + if (!nsh) { + DEBUGP(DGPRS, "Not enough headroom for NS header\n"); + return -EIO; + } + + nsh->pdu_type = NS_PDUT_UNITDATA; + /* spare octet in data[0] */ + nsh->data[1] = bvci >> 8; + nsh->data[2] = bvci & 0xff; + + return gprs_ns_tx(msg); +} + +/* Section 9.2.10: receive side */ +static int gprs_ns_rx_unitdata(struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h; + u_int16_t bvci; + + /* spare octet in data[0] */ + bvci = nsh->data[1] << 8 | nsh->data[2]; + msg->l3h = &nsh->data[3]; + + /* call upper layer (BSSGP) */ + return gprs_bssgp_rcvmsg(msg, bvci); +} + +/* Section 9.2.7 */ +static int gprs_ns_rx_status(struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = msg->l2h; + struct tlv_parsed tp; + u_int8_t cause; + int rc; + + DEBUGP(DGPRS, "NS STATUS "); + + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0); + + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE)) { + DEBUGPC(DGPRS, "missing cause IE\n"); + return -EINVAL; + } + + cause = *TLVP_VAL(&tp, NS_IE_CAUSE); + DEBUGPC(DGPRS, "cause=%s\n", gprs_ns_cause_str(cause)); + + return 0; +} + +/* Section 7.3 */ +static int gprs_ns_rx_reset(struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct gprs_nsvc *nsvc = &dummy_nsvc; + struct tlv_parsed tp; + u_int8_t *cause; + u_int16_t *nsvci, *nsei; + int rc; + + DEBUGP(DGPRS, "NS RESET "); + + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0); + + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || + !TLVP_PRESENT(&tp, NS_IE_VCI) || + !TLVP_PRESENT(&tp, NS_IE_NSEI)) { + /* FIXME: respond with NS_CAUSE_MISSING_ESSENT_IE */ + DEBUGPC(DGPRS, "Missing mandatory IE\n"); + return -EINVAL; + } + + cause = (u_int8_t *) TLVP_VAL(&tp, NS_IE_CAUSE); + nsvci = (u_int16_t *) TLVP_VAL(&tp, NS_IE_VCI); + nsei = (u_int16_t *) TLVP_VAL(&tp, NS_IE_NSEI); + + *nsvci = ntohs(*nsvci); + *nsei = ntohs(*nsei); + + DEBUGPC(DGPRS, "cause=%s, NSVCI=%u, NSEI=%u\n", + gprs_ns_cause_str(*cause), *nsvci, *nsei); + + /* mark the NS-VC as blocked and alive */ + nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; + nsvc->nsei = *nsei; + nsvc->nsvci = *nsvci; + + /* start the test procedure */ + nsvc->alive_timer.cb = gprs_ns_alive_cb; + nsvc->alive_timer.data = nsvc; + bsc_schedule_timer(&nsvc->alive_timer, NS_TIMER_ALIVE); + + return gprs_ns_tx_reset_ack(*nsvci, *nsei); +} + +/* main entry point, here incoming NS frames enter */ +int gprs_ns_rcvmsg(struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct gprs_nsvc *nsvc = &dummy_nsvc; + int rc = 0; + + switch (nsh->pdu_type) { + case NS_PDUT_ALIVE: + /* remote end inquires whether we're still alive, + * we need to respond with ALIVE_ACK */ + rc = gprs_ns_tx_simple(NULL, NS_PDUT_ALIVE_ACK); + break; + case NS_PDUT_ALIVE_ACK: + /* stop Tns-alive */ + bsc_del_timer(&nsvc->alive_timer); + /* start Tns-test */ + nsvc->timer_is_tns_alive = 0; + bsc_schedule_timer(&nsvc->alive_timer, NS_TIMER_TEST); + break; + case NS_PDUT_UNITDATA: + /* actual user data */ + rc = gprs_ns_rx_unitdata(msg); + break; + case NS_PDUT_STATUS: + rc = gprs_ns_rx_status(msg); + break; + case NS_PDUT_RESET: + rc = gprs_ns_rx_reset(msg); + break; + case NS_PDUT_RESET_ACK: + /* FIXME: mark remote NS-VC as blocked + active */ + break; + case NS_PDUT_UNBLOCK: + /* Section 7.2: unblocking procedure */ + DEBUGP(DGPRS, "NS UNBLOCK\n"); + nsvc->state &= ~NSE_S_BLOCKED; + rc = gprs_ns_tx_simple(NULL, NS_PDUT_UNBLOCK_ACK); + break; + case NS_PDUT_UNBLOCK_ACK: + /* FIXME: mark remote NS-VC as unblocked + active */ + break; + case NS_PDUT_BLOCK: + DEBUGP(DGPRS, "NS BLOCK\n"); + nsvc->state |= NSE_S_BLOCKED; + rc = gprs_ns_tx_simple(NULL, NS_PDUT_UNBLOCK_ACK); + break; + case NS_PDUT_BLOCK_ACK: + /* FIXME: mark remote NS-VC as blocked + active */ + break; + default: + DEBUGP(DGPRS, "Unknown NS PDU type 0x%02x\n", nsh->pdu_type); + rc = -EINVAL; + break; + } + return rc; +} + diff --git a/openbsc/src/gprs_sgsn.c b/openbsc/src/gprs_sgsn.c new file mode 100644 index 000000000..477e448be --- /dev/null +++ b/openbsc/src/gprs_sgsn.c @@ -0,0 +1,93 @@ +/* GPRS SGSN functionality */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include + +#include +#include +#include +#include +#include + +static LLIST_HEAD(sgsn_mm_ctxts); + +static int ra_id_equals(const struct gprs_ra_id *id1, + const struct gprs_ra_id *id2) +{ + return (id1->mcc == id2->mcc && id1->mnc == id2->mnc && + id1->lac == id2->lac && id1->rac == id2->rac); +} + +/* look-up a SGSN MM context based on TLLI + RAI */ +struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(u_int32_t tlli, + const struct gprs_ra_id *raid) +{ + struct sgsn_mm_ctx *ctx; + + llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { + if (tlli == ctx->tlli && + ra_id_equals(raid, &ctx->ra)) + return ctx; + } + return NULL; +} + +struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(u_int32_t p_tmsi) +{ + struct sgsn_mm_ctx *ctx; + + llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { + if (p_tmsi == ctx->p_tmsi) + return ctx; + } + return NULL; +} + +struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi) +{ + struct sgsn_mm_ctx *ctx; + + llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { + if (!strcmp(imsi, ctx->imsi)) + return ctx; + } + return NULL; + +} + +/* Allocate a new SGSN MM context */ +struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(u_int32_t tlli, + const struct gprs_ra_id *raid) +{ + struct sgsn_mm_ctx *ctx = talloc_zero(NULL, struct sgsn_mm_ctx); + + if (!ctx) + return NULL; + + memcpy(&ctx->ra, raid, sizeof(ctx->ra)); + ctx->tlli = tlli; + ctx->mm_state = GMM_DEREGISTERED; + + llist_add(&ctx->list, &sgsn_mm_ctxts); + + return ctx; +} diff --git a/openbsc/src/gsm_04_08_gprs.c b/openbsc/src/gsm_04_08_gprs.c new file mode 100644 index 000000000..af6a481cb --- /dev/null +++ b/openbsc/src/gsm_04_08_gprs.c @@ -0,0 +1,716 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 10.5.5.14 GPRS MM Cause / Table 10.5.147 */ +struct value_string gmm_cause_names[] = { + /* FIXME */ + { GMM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" }, + { GMM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" }, + { GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL, + "Message type non-existant or not implemented" }, + { GMM_CAUSE_MSGT_INCOMP_P_STATE, + "Message type not compatible with protocol state" }, + { GMM_CAUSE_IE_NOTEXIST_NOTIMPL, + "Information element non-existent or not implemented" }, + { GMM_CAUSE_COND_IE_ERR, "Conditional IE error" }, + { GMM_CAUSE_MSG_INCOMP_P_STATE, + "Message not compatible with protocol state " }, + { GMM_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" }, + { 0, NULL } +}; + +/* 10.5.6.6 SM Cause / Table 10.5.157 */ +struct value_string gsm_cause_names[] = { + { GSM_CAUSE_INSUFF_RSRC, "Insufficient resources" }, + { GSM_CAUSE_MISSING_APN, "Missing or unknown APN" }, + { GSM_CAUSE_UNKNOWN_PDP, "Unknown PDP address or PDP type" }, + { GSM_CAUSE_AUTH_FAILED, "User Authentication failed" }, + { GSM_CAUSE_ACT_REJ_GGSN, "Activation rejected by GGSN" }, + { GSM_CAUSE_ACT_REJ_UNSPEC, "Activation rejected, unspecified" }, + { GSM_CAUSE_SERV_OPT_NOTSUPP, "Service option not supported" }, + { GSM_CAUSE_REQ_SERV_OPT_NOTSUB, + "Requested service option not subscribed" }, + { GSM_CAUSE_SERV_OPT_TEMP_OOO, + "Service option temporarily out of order" }, + { GSM_CAUSE_NSAPI_IN_USE, "NSAPI already used" }, + { GSM_CAUSE_DEACT_REGULAR, "Regular deactivation" }, + { GSM_CAUSE_QOS_NOT_ACCEPTED, "QoS not accepted" }, + { GSM_CAUSE_NET_FAIL, "Network Failure" }, + { GSM_CAUSE_REACT_RQD, "Reactivation required" }, + { GSM_CAUSE_FEATURE_NOTSUPP, "Feature not supported " }, + { GSM_CAUSE_INVALID_TRANS_ID, "Invalid transaction identifier" }, + { GSM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" }, + { GSM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" }, + { GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL, + "Message type non-existant or not implemented" }, + { GSM_CAUSE_MSGT_INCOMP_P_STATE, + "Message type not compatible with protocol state" }, + { GSM_CAUSE_IE_NOTEXIST_NOTIMPL, + "Information element non-existent or not implemented" }, + { GSM_CAUSE_COND_IE_ERR, "Conditional IE error" }, + { GSM_CAUSE_MSG_INCOMP_P_STATE, + "Message not compatible with protocol state " }, + { GSM_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" }, + { 0, NULL } +}; + +static const char *att_name(u_int8_t type) +{ + switch (type) { + case GPRS_ATT_T_ATTACH: + return "GPRS attach"; + case GPRS_ATT_T_ATT_WHILE_IMSI: + return "GPRS attach while IMSI attached"; + case GPRS_ATT_T_COMBINED: + return "Combined GPRS/IMSI attach"; + default: + return "unknown"; + } +} + +static const char *upd_name(u_int8_t type) +{ + switch (type) { + case GPRS_UPD_T_RA: + return "RA updating"; + case GPRS_UPD_T_RA_LA: + return "combined RA/LA updating"; + case GPRS_UPD_T_RA_LA_IMSI_ATT: + return "combined RA/LA updating + IMSI attach"; + case GPRS_UPD_T_PERIODIC: + return "periodic updating"; + } + return "unknown"; +} + +void gsm48_parse_ra(struct gprs_ra_id *raid, const u_int8_t *buf) +{ + raid->mcc = (buf[0] & 0xf) * 100; + raid->mcc += (buf[0] >> 4) * 10; + raid->mcc += (buf[1] & 0xf) * 1; + + /* I wonder who came up with the stupidity of encoding the MNC + * differently depending on how many digits its decimal number has! */ + if ((buf[1] >> 4) == 0xf) { + raid->mnc = (buf[2] & 0xf) * 10; + raid->mnc += (buf[2] >> 4) * 1; + } else { + raid->mnc = (buf[2] & 0xf) * 100; + raid->mnc += (buf[2] >> 4) * 10; + raid->mnc += (buf[1] >> 4) * 1; + } + + raid->lac = ntohs(*(u_int16_t *)(buf + 3)); + raid->rac = buf[5]; +} + +/* Send a message through the underlying layer */ +static int gsm48_gmm_sendmsg(struct msgb *msg, int command) +{ + return gprs_llc_tx_ui(msg, GPRS_SAPI_GMM, command); +} + +/* TS 03.03 Chapter 2.6 */ +int gprs_tlli_type(u_int32_t tlli) +{ + if ((tlli & 0xc0000000) == 0xc0000000) + return TLLI_LOCAL; + else if ((tlli & 0xc0000000) == 0x80000000) + return TLLI_FOREIGN; + else if ((tlli & 0xf8000000) == 0x78000000) + return TLLI_RANDOM; + else if ((tlli & 0xf8000000) == 0x70000000) + return TLLI_AUXILIARY; + + return TLLI_RESERVED; +} + +/* Chapter 9.4.2: Attach accept */ +static int gsm48_tx_gmm_att_ack(struct msgb *old_msg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm48_attach_ack *aa; + + DEBUGP(DMM, "<- GPRS ATTACH ACCEPT\n"); + + msg->tlli = old_msg->tlli; + msg->trx = old_msg->trx; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_ATTACH_ACK; + + aa = (struct gsm48_attach_ack *) msgb_put(msg, sizeof(*aa)); + aa->force_stby = 0; /* not indicated */ + aa->att_result = 1; /* GPRS only */ + aa->ra_upd_timer = GPRS_TMR_MINUTE | 10; + aa->radio_prio = 4; /* lowest */ + gsm48_ra_id_by_bts(aa->ra_id.digits, old_msg->trx->bts); + + /* Option: P-TMSI signature, allocated P-TMSI, MS ID, ... */ + return gsm48_gmm_sendmsg(msg, 0); +} + +/* Chapter 9.4.5: Attach reject */ +static int gsm48_tx_gmm_att_rej(struct msgb *old_msg, u_int8_t gmm_cause) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + DEBUGP(DMM, "<- GPRS ATTACH REJECT\n"); + + msg->tlli = old_msg->tlli; + msg->trx = old_msg->trx; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_ATTACH_REJ; + gh->data[0] = gmm_cause; + + return gsm48_gmm_sendmsg(msg, 0); +} + +/* Transmit Chapter 9.4.12 Identity Request */ +static int gsm48_tx_gmm_id_req(struct msgb *old_msg, u_int8_t id_type) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + DEBUGP(DMM, "-> GPRS IDENTITY REQUEST: mi_type=%02x\n", id_type); + + msg->tlli = old_msg->tlli; + msg->trx = old_msg->trx; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_ID_REQ; + /* 10.5.5.9 ID type 2 + identity type and 10.5.5.7 'force to standby' IE */ + gh->data[0] = id_type & 0xf; + + return gsm48_gmm_sendmsg(msg, 0); +} + +/* Check if we can already authorize a subscriber */ +static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx, struct msgb *msg) +{ + if (strlen(ctx->imei) && strlen(ctx->imsi)) { + ctx->mm_state = GMM_REGISTERED_NORMAL; + return gsm48_tx_gmm_att_ack(msg); + } + if (!strlen(ctx->imei)) + return gsm48_tx_gmm_id_req(msg, GSM_MI_TYPE_IMEI); + + if (!strlen(ctx->imsi)) + return gsm48_tx_gmm_id_req(msg, GSM_MI_TYPE_IMSI); + + return 0; +} + +/* Parse Chapter 9.4.13 Identity Response */ +static int gsm48_rx_gmm_id_resp(struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->gmmh; + u_int8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK; + char mi_string[GSM48_MI_SIZE]; + struct gprs_ra_id ra_id; + struct sgsn_mm_ctx *ctx; + + gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); + DEBUGP(DMM, "GMM IDENTITY RESPONSE: mi_type=0x%02x MI(%s) ", + mi_type, mi_string); + + gprs_ra_id_by_bts(&ra_id, msg->trx->bts); + ctx = sgsn_mm_ctx_by_tlli(msg->tlli, &ra_id); + if (!ctx) { + DEBUGP(DMM, "from unknown TLLI 0x%08x?!?\n", msg->tlli); + return -EINVAL; + } + + switch (mi_type) { + case GSM_MI_TYPE_IMSI: + /* we already have a mm context with current TLLI, but no + * P-TMSI / IMSI yet. What we now need to do is to fill + * this initial context with data from the HLR */ + strncpy(ctx->imsi, mi_string, sizeof(ctx->imei)); + break; + case GSM_MI_TYPE_IMEI: + strncpy(ctx->imei, mi_string, sizeof(ctx->imei)); + break; + case GSM_MI_TYPE_IMEISV: + break; + } + + DEBUGPC(DMM, "\n"); + /* Check if we can let the mobile station enter */ + return gsm48_gmm_authorize(ctx, msg); +} + +static void attach_rej_cb(void *data) +{ + struct sgsn_mm_ctx *ctx = data; + + /* FIXME: determine through which BTS/TRX to send this */ + //gsm48_tx_gmm_att_rej(ctx->tlli, GMM_CAUSE_MS_ID_NOT_DERIVED); + ctx->mm_state = GMM_DEREGISTERED; + /* FIXME: release the context */ +} + +static void schedule_reject(struct sgsn_mm_ctx *ctx) +{ + ctx->T = 3370; + ctx->timer.cb = attach_rej_cb; + ctx->timer.data = ctx; + bsc_schedule_timer(&ctx->timer, 6, 0); +} + +/* Section 9.4.1 Attach request */ +static int gsm48_rx_gmm_att_req(struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->gmmh; + u_int8_t *cur = gh->data, *msnc, *mi, *old_ra_info; + u_int8_t msnc_len, att_type, mi_len, mi_type; + u_int16_t drx_par; + u_int32_t tmsi; + char mi_string[GSM48_MI_SIZE]; + struct gprs_ra_id ra_id; + struct sgsn_mm_ctx *ctx; + + DEBUGP(DMM, "GMM ATTACH REQUEST "); + + /* As per TS 04.08 Chapter 4.7.1.4, the attach request arrives either + * with a foreign TLLI (P-TMSI that was allocated to the MS before), + * or with random TLLI. */ + + gprs_ra_id_by_bts(&ra_id, msg->trx->bts); + + /* MS network capability 10.5.5.12 */ + msnc_len = *cur++; + msnc = cur; + if (msnc_len > 2) + goto err_inval; + cur += msnc_len; + + /* aTTACH Type 10.5.5.2 */ + att_type = *cur++ & 0x0f; + + /* DRX parameter 10.5.5.6 */ + drx_par = *cur++; + drx_par |= *cur++ << 8; + + /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */ + mi_len = *cur++; + mi = cur; + if (mi_len > 8) + goto err_inval; + mi_type = *mi & GSM_MI_TYPE_MASK; + cur += mi_len; + + gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); + + DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, att_name(att_type)); + + /* Old routing area identification 10.5.5.15 */ + old_ra_info = cur; + cur += 6; + + /* MS Radio Access Capability 10.5.5.12a */ + + /* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status */ + + switch (mi_type) { + case GSM_MI_TYPE_IMSI: + /* Try to find MM context based on IMSI */ + ctx = sgsn_mm_ctx_by_imsi(mi_string); + if (!ctx) { +#if 0 + return gsm48_tx_gmm_att_rej(msg, GMM_CAUSE_IMSI_UNKNOWN); +#else + /* As a temorary hack, we simply assume that the IMSI exists */ + ctx = sgsn_mm_ctx_alloc(0, &ra_id); + if (!ctx) + return gsm48_tx_gmm_att_rej(msg, GMM_CAUSE_NET_FAIL); + strncpy(ctx->imsi, mi_string, sizeof(ctx->imsi)); +#endif + } + /* FIXME: Start some timer */ + ctx->mm_state = GMM_COMMON_PROC_INIT; + ctx->tlli = msg->tlli; + break; + case GSM_MI_TYPE_TMSI: + tmsi = strtoul(mi_string, NULL, 10); + /* Try to find MM context based on P-TMSI */ + ctx = sgsn_mm_ctx_by_ptmsi(tmsi); + if (!ctx) { + ctx = sgsn_mm_ctx_alloc(msg->tlli, &ra_id); + /* FIXME: Start some timer */ + ctx->mm_state = GMM_COMMON_PROC_INIT; + ctx->tlli = msg->tlli; + } + break; + default: + break; + } + + /* FIXME: allocate a new P-TMSI (+ P-TMSI signature) */ + /* FIXME: update the TLLI with the new local TLLI based on the P-TMSI */ + + DEBUGPC(DMM, "\n"); + + return ctx ? gsm48_gmm_authorize(ctx, msg) : 0; + +err_inval: + DEBUGPC(DMM, "\n"); + return gsm48_tx_gmm_att_rej(msg, GMM_CAUSE_SEM_INCORR_MSG); +} + +/* Chapter 9.4.15: Routing area update accept */ +static int gsm48_tx_gmm_ra_upd_ack(struct msgb *old_msg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm48_ra_upd_ack *rua; + + DEBUGP(DMM, "<- ROUTING AREA UPDATE ACCEPT\n"); + + msg->tlli = old_msg->tlli; + msg->trx = old_msg->trx; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_RA_UPD_ACK; + + rua = (struct gsm48_ra_upd_ack *) msgb_put(msg, sizeof(*rua)); + rua->force_stby = 0; /* not indicated */ + rua->upd_result = 0; /* RA updated */ + rua->ra_upd_timer = GPRS_TMR_MINUTE | 10; + gsm48_ra_id_by_bts(rua->ra_id.digits, old_msg->trx->bts); + + /* Option: P-TMSI signature, allocated P-TMSI, MS ID, ... */ + return gsm48_gmm_sendmsg(msg, 0); +} + +/* Chapter 9.4.17: Routing area update reject */ +static int gsm48_tx_gmm_ra_upd_rej(struct msgb *old_msg, u_int8_t cause) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + DEBUGP(DMM, "<- ROUTING AREA UPDATE REJECT\n"); + + msg->tlli = old_msg->tlli; + msg->trx = old_msg->trx; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_RA_UPD_REJ; + gh->data[0] = cause; + gh->data[1] = 0; /* ? */ + + /* Option: P-TMSI signature, allocated P-TMSI, MS ID, ... */ + return gsm48_gmm_sendmsg(msg, 0); +} + +/* Chapter 9.4.14: Routing area update request */ +static int gsm48_rx_gmm_ra_upd_req(struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->gmmh; + struct sgsn_mm_ctx *mmctx; + u_int8_t *cur = gh->data; + struct gprs_ra_id old_ra_id; + u_int8_t upd_type; + + /* Update Type 10.5.5.18 */ + upd_type = *cur++ & 0x0f; + + DEBUGP(DMM, "GMM RA UPDATE REQUEST type=\"%s\" ", upd_name(upd_type)); + + /* Old routing area identification 10.5.5.15 */ + gsm48_parse_ra(&old_ra_id, cur); + cur += 6; + + /* MS Radio Access Capability 10.5.5.12a */ + + /* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status, + * DRX parameter, MS network capability */ + + switch (upd_type) { + case GPRS_UPD_T_RA_LA: + case GPRS_UPD_T_RA_LA_IMSI_ATT: + DEBUGPC(DMM, " unsupported in Mode III, is your SI13 corrupt?\n"); + return gsm48_tx_gmm_ra_upd_rej(msg, GMM_CAUSE_PROTO_ERR_UNSPEC); + break; + case GPRS_UPD_T_RA: + case GPRS_UPD_T_PERIODIC: + break; + } + + /* Look-up the MM context based on old RA-ID and TLLI */ + mmctx = sgsn_mm_ctx_by_tlli(msg->tlli, &old_ra_id); + if (!mmctx || mmctx->mm_state == GMM_DEREGISTERED) { + /* The MS has to perform GPRS attach */ + DEBUGPC(DMM, " REJECT\n"); + return gsm48_tx_gmm_ra_upd_rej(msg, GMM_CAUSE_IMPL_DETACHED); + } + + /* FIXME: Update the MM context with the new RA-ID */ + /* FIXME: Update the MM context with the new TLLI */ + /* FIXME: Update the MM context with the MS radio acc capabilities */ + /* FIXME: Update the MM context with the MS network capabilities */ + + DEBUGPC(DMM, " ACCEPT\n"); + return gsm48_tx_gmm_ra_upd_ack(msg); +} + +static int gsm48_rx_gmm_status(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DMM, "GPRS MM STATUS (cause: %s)\n", + get_value_string(gmm_cause_names, gh->data[0])); + + return 0; +} + +/* GPRS Mobility Management */ +static int gsm0408_rcv_gmm(struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->gmmh; + int rc; + + switch (gh->msg_type) { + case GSM48_MT_GMM_RA_UPD_REQ: + rc = gsm48_rx_gmm_ra_upd_req(msg); + break; + case GSM48_MT_GMM_ATTACH_REQ: + rc = gsm48_rx_gmm_att_req(msg); + break; + case GSM48_MT_GMM_ID_RESP: + rc = gsm48_rx_gmm_id_resp(msg); + break; + case GSM48_MT_GMM_STATUS: + rc = gsm48_rx_gmm_status(msg); + break; + case GSM48_MT_GMM_RA_UPD_COMPL: + /* only in case SGSN offered new P-TMSI */ + case GSM48_MT_GMM_ATTACH_COMPL: + /* only in case SGSN offered new P-TMSI */ + case GSM48_MT_GMM_DETACH_REQ: + case GSM48_MT_GMM_PTMSI_REALL_COMPL: + case GSM48_MT_GMM_AUTH_CIPH_RESP: + DEBUGP(DMM, "Unimplemented GSM 04.08 GMM msg type 0x%02x\n", + gh->msg_type); + break; + default: + DEBUGP(DMM, "Unknown GSM 04.08 GMM msg type 0x%02x\n", + gh->msg_type); + break; + } + + return rc; +} + +/* Section 9.5.2: Ativate PDP Context Accept */ +static int gsm48_tx_gsm_act_pdp_acc(struct msgb *old_msg, struct gsm48_act_pdp_ctx_req *req) +{ + struct gsm48_hdr *old_gh = (struct gsm48_hdr *) old_msg->gmmh; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_act_pdp_ctx_ack *act_ack; + struct gsm48_hdr *gh; + u_int8_t transaction_id = ((old_gh->proto_discr >> 4) ^ 0x8); /* flip */ + + DEBUGP(DMM, "<- ACTIVATE PDP CONTEXT ACK\n"); + + msg->tlli = old_msg->tlli; + msg->trx = old_msg->trx; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); + gh->msg_type = GSM48_MT_GSM_ACT_PDP_ACK; + act_ack = (struct gsm48_act_pdp_ctx_ack *) + msgb_put(msg, sizeof(*act_ack)); + act_ack->llc_sapi = req->req_llc_sapi; + memcpy(act_ack->qos_lv, req->req_qos_lv, sizeof(act_ack->qos_lv)); + //act_ack->radio_prio = 4; + + return gsm48_gmm_sendmsg(msg, 0); +} + +/* Section 9.5.9: Deactivate PDP Context Accept */ +static int gsm48_tx_gsm_deact_pdp_acc(struct msgb *old_msg) +{ + struct gsm48_hdr *old_gh = (struct gsm48_hdr *) old_msg->gmmh; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + u_int8_t transaction_id = ((old_gh->proto_discr >> 4) ^ 0x8); /* flip */ + + DEBUGP(DMM, "<- DEACTIVATE PDP CONTEXT ACK\n"); + + msg->tlli = old_msg->tlli; + msg->trx = old_msg->trx; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); + gh->msg_type = GSM48_MT_GSM_DEACT_PDP_ACK; + + return gsm48_gmm_sendmsg(msg, 0); +} + +/* Section 9.5.1: Activate PDP Context Request */ +static int gsm48_rx_gsm_act_pdp_req(struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->gmmh; + struct gsm48_act_pdp_ctx_req *act_req = (struct gsm48_act_pdp_ctx_req *) gh->data; + u_int8_t *pdp_addr_lv = act_req->data; + + DEBUGP(DMM, "ACTIVATE PDP CONTEXT REQ\n"); + + /* FIXME: parse access point name + IPCP config options */ + + return gsm48_tx_gsm_act_pdp_acc(msg, act_req); +} + +/* Section 9.5.8: Deactivate PDP Context Request */ +static int gsm48_rx_gsm_deact_pdp_req(struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->gmmh; + + DEBUGP(DMM, "DEACTIVATE PDP CONTEXT REQ (cause: %s)\n", + get_value_string(gsm_cause_names, gh->data[0])); + + return gsm48_tx_gsm_deact_pdp_acc(msg); +} + +static int gsm48_rx_gsm_status(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DMM, "GPRS SM STATUS (cause: %s)\n", + get_value_string(gsm_cause_names, gh->data[0])); + + return 0; +} + +/* GPRS Session Management */ +static int gsm0408_rcv_gsm(struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->gmmh; + int rc; + + switch (gh->msg_type) { + case GSM48_MT_GSM_ACT_PDP_REQ: + rc = gsm48_rx_gsm_act_pdp_req(msg); + break; + case GSM48_MT_GSM_DEACT_PDP_REQ: + rc = gsm48_rx_gsm_deact_pdp_req(msg); + case GSM48_MT_GSM_STATUS: + rc = gsm48_rx_gsm_status(msg); + break; + case GSM48_MT_GSM_REQ_PDP_ACT_REJ: + case GSM48_MT_GSM_ACT_AA_PDP_REQ: + case GSM48_MT_GSM_DEACT_AA_PDP_REQ: + DEBUGP(DMM, "Unimplemented GSM 04.08 GSM msg type 0x%02x\n", + gh->msg_type); + break; + default: + DEBUGP(DMM, "Unknown GSM 04.08 GSM msg type 0x%02x\n", + gh->msg_type); + break; + + } + + return rc; +} + +/* Main entry point for incoming 04.08 GPRS messages */ +int gsm0408_gprs_rcvmsg(struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->gmmh; + u_int8_t pdisc = gh->proto_discr & 0x0f; + int rc = -EINVAL; + + switch (pdisc) { + case GSM48_PDISC_MM_GPRS: + rc = gsm0408_rcv_gmm(msg); + break; + case GSM48_PDISC_SM_GPRS: + rc = gsm0408_rcv_gsm(msg); + break; + default: + DEBUGP(DMM, "Unknown GSM 04.08 discriminator 0x%02x\n", + pdisc); + break; + } + + return rc; +} + +/* Determine the 'struct gsm_bts' from a RA ID */ +struct gsm_bts *gsm48_bts_by_ra_id(struct gsm_network *net, + const u_int8_t *buf, unsigned int len) +{ + struct gprs_ra_id raid; + struct gsm_bts *bts; + + if (len < 6) + return NULL; + + gsm48_parse_ra(&raid, buf); + + if (net->country_code != raid.mcc || + net->network_code != raid.mnc) + return NULL; + + llist_for_each_entry(bts, &net->bts_list, list) { + /* FIXME: we actually also need to check the + * routing area code! */ + if (bts->location_area_code == raid.lac) + return bts; + } + + return NULL; +} + diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c index 8a8a1987d..fae9428da 100644 --- a/openbsc/src/input/ipaccess.c +++ b/openbsc/src/input/ipaccess.c @@ -53,6 +53,7 @@ struct ia_e1_handle { struct bsc_fd listen_fd; struct bsc_fd rsl_listen_fd; + struct bsc_fd gprs_fd; struct gsm_network *gsmnet; }; @@ -602,6 +603,87 @@ static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what) return rc; } +/* declare this as a weak symbol to ensure code will still build + * even if it does not provide this function */ +extern int gprs_ns_rcvmsg(struct msgb *msg) __attribute__((weak)); + +static struct msgb *read_gprs_msg(struct bsc_fd *bfd, int *error) +{ + struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "Abis/IP/GPRS"); + int ret = 0; + + if (!msg) { + *error = -ENOMEM; + return NULL; + } + + ret = recv(bfd->fd, msg->data, TS1_ALLOC_SIZE, 0); + if (ret < 0) { + fprintf(stderr, "recv error %s\n", strerror(errno)); + msgb_free(msg); + *error = ret; + return NULL; + } else if (ret == 0) { + msgb_free(msg); + *error = ret; + return NULL; + } + + msg->l2h = msg->data; + msgb_put(msg, ret); + + return msg; +} + +static int handle_gprs_read(struct bsc_fd *bfd) +{ + int error; + struct msgb *msg = read_gprs_msg(bfd, &error); + + if (!msg) + return error; + + if (gprs_ns_rcvmsg) + return gprs_ns_rcvmsg(msg); + else { + msgb_free(msg); + return 0; + } +} + +static int handle_gprs_write(struct bsc_fd *bfd) +{ +} + +int ipac_gprs_send(struct msgb *msg) +{ + struct sockaddr_in sin; + int rc; + + sin.sin_family = AF_INET; + inet_aton("192.168.100.111", &sin.sin_addr); + sin.sin_port = htons(23000); + + rc = sendto(e1h->gprs_fd.fd, msg->data, msg->len, 0, + (struct sockaddr *)&sin, sizeof(sin)); + + talloc_free(msg); + + return rc; +} + +/* UDP Port 23000 carries the LLC-in-BSSGP-in-NS protocol stack */ +static int gprs_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int rc; + + if (what & BSC_FD_READ) + rc = handle_gprs_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_gprs_write(bfd); + + return rc; +} struct e1inp_driver ipaccess_driver = { .name = "ip.access", @@ -708,13 +790,17 @@ static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) return 0; } -static int make_sock(struct bsc_fd *bfd, u_int16_t port, +static int make_sock(struct bsc_fd *bfd, int proto, u_int16_t port, int (*cb)(struct bsc_fd *fd, unsigned int what)) { struct sockaddr_in addr; int ret, on = 1; + int type = SOCK_STREAM; + + if (proto == IPPROTO_UDP) + type = SOCK_DGRAM; - bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + bfd->fd = socket(AF_INET, type, proto); bfd->cb = cb; bfd->when = BSC_FD_READ; //bfd->data = line; @@ -739,11 +825,12 @@ static int make_sock(struct bsc_fd *bfd, u_int16_t port, return -EIO; } - ret = listen(bfd->fd, 1); - if (ret < 0) { - perror("listen"); - close(bfd->fd); - return ret; + if (proto != IPPROTO_UDP) { + ret = listen(bfd->fd, 1); + if (ret < 0) { + perror("listen"); + return ret; + } } ret = bsc_register_fd(bfd); @@ -811,12 +898,19 @@ int ipaccess_setup(struct gsm_network *gsmnet) e1h->gsmnet = gsmnet; /* Listen for OML connections */ - ret = make_sock(&e1h->listen_fd, IPA_TCP_PORT_OML, listen_fd_cb); + ret = make_sock(&e1h->listen_fd, IPPROTO_TCP, IPA_TCP_PORT_OML, + listen_fd_cb); if (ret < 0) return ret; /* Listen for RSL connections */ - ret = make_sock(&e1h->rsl_listen_fd, IPA_TCP_PORT_RSL, rsl_listen_fd_cb); + ret = make_sock(&e1h->rsl_listen_fd, IPPROTO_TCP, + IPA_TCP_PORT_RSL, rsl_listen_fd_cb); + if (ret < 0) + return ret; + + /* Listen for incoming GPRS packets */ + ret = make_sock(&e1h->gprs_fd, IPPROTO_UDP, 23000, gprs_fd_cb); return ret; } diff --git a/openbsc/src/vty_interface_cmds.c b/openbsc/src/vty_interface_cmds.c index 671351e1c..efcd6547b 100644 --- a/openbsc/src/vty_interface_cmds.c +++ b/openbsc/src/vty_interface_cmds.c @@ -142,7 +142,7 @@ DEFUN(logging_prnt_timestamp, } /* FIXME: those have to be kept in sync with the log levels and categories */ -#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref)" +#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref|gprs)" #define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)" DEFUN(logging_level, logging_level_cmd,