diff options
author | Holger Hans Peter Freyther <zecke@selfish.org> | 2010-05-14 02:42:15 +0800 |
---|---|---|
committer | Holger Hans Peter Freyther <zecke@selfish.org> | 2010-05-14 02:42:15 +0800 |
commit | 7373109abc9829c49eabf26e951a0ec6647666ac (patch) | |
tree | f508489f955c15fc36376374072cfb8ce6a9e6de | |
parent | c1cb5eb38d5199de9049c59dc080051deba7aa53 (diff) | |
parent | cf5cc5bb5b472f470eb9584a32dee3c38413f93a (diff) |
Merge branch 'master' into on-waves/mgcp
102 files changed, 8482 insertions, 641 deletions
diff --git a/libosmocore/.gitignore b/libosmocore/.gitignore index d61cdc589..06904fa09 100644 --- a/libosmocore/.gitignore +++ b/libosmocore/.gitignore @@ -7,6 +7,7 @@ Makefile.in *.la *.pc aclocal.m4 +m4/*.m4 autom4te.cache config.h* config.sub @@ -23,3 +24,7 @@ libtool .tarball-version .version + +tests/sms/sms_test +tests/timer/timer_test + diff --git a/libosmocore/include/osmocore/Makefile.am b/libosmocore/include/osmocore/Makefile.am index 1c3a33f33..fde010271 100644 --- a/libosmocore/include/osmocore/Makefile.am +++ b/libosmocore/include/osmocore/Makefile.am @@ -1,7 +1,7 @@ osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h \ tlv.h bitvec.h comp128.h statistics.h gsm_utils.h utils.h \ gsmtap.h write_queue.h rsl.h gsm48.h rxlev_stat.h mncc.h \ - gsm48_ie.h logging.h + gsm48_ie.h logging.h gsm0808.h rate_ctr.h if ENABLE_TALLOC osmocore_HEADERS += talloc.h diff --git a/libosmocore/include/osmocore/gsm0808.h b/libosmocore/include/osmocore/gsm0808.h new file mode 100644 index 000000000..a40713f77 --- /dev/null +++ b/libosmocore/include/osmocore/gsm0808.h @@ -0,0 +1,41 @@ +/* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009,2010 by On-Waves + * 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. + * + */ +#ifndef OSMOCORE_GSM0808_H +#define OSMOCORE_GSM0808_H + +#include "tlv.h" + +struct msgb; + +struct msgb *gsm0808_create_layer3(struct msgb *msg, uint16_t netcode, uint16_t countrycode, int lac, int ci); +struct msgb *gsm0808_create_reset(void); +struct msgb *gsm0808_create_clear_complete(void); +struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id); +struct msgb *gsm0808_create_cipher_reject(uint8_t cause); +struct msgb *gsm0808_create_classmark_update(const uint8_t *classmark, uint8_t length); +struct msgb *gsm0808_create_sapi_reject(uint8_t link_id); +struct msgb *gsm0808_create_assignment_completed(struct gsm_lchan *lchan, uint8_t rr_cause, + uint8_t chosen_channel, uint8_t encr_alg_id, + uint8_t speech_mode); +struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause); + +const struct tlv_definition *gsm0808_att_tlvdef(); + +#endif diff --git a/libosmocore/include/osmocore/gsm48.h b/libosmocore/include/osmocore/gsm48.h index 1e963573c..be9fbbd14 100644 --- a/libosmocore/include/osmocore/gsm48.h +++ b/libosmocore/include/osmocore/gsm48.h @@ -1,9 +1,18 @@ #ifndef _OSMOCORE_GSM48_H +#define _OSMOCORE_GSM48_H #include <osmocore/tlv.h> #include <osmocore/protocol/gsm_04_08.h> #include <osmocore/gsm48_ie.h> +/* A parsed GPRS routing area */ +struct gprs_ra_id { + uint16_t mnc; + uint16_t mcc; + uint16_t lac; + uint8_t rac; +}; + extern const struct tlv_definition gsm48_att_tlvdef; const char *gsm48_cc_state_name(uint8_t state); const char *gsm48_cc_msg_name(uint8_t msgtype); @@ -14,4 +23,12 @@ void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc, int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi); int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi); +/* Convert Mobile Identity (10.5.1.4) to string */ +int gsm48_mi_to_string(char *string, const int str_len, + const uint8_t *mi, const int mi_len); + +/* Parse Routeing Area Identifier */ +void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf); +int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid); + #endif diff --git a/libosmocore/include/osmocore/gsm_utils.h b/libosmocore/include/osmocore/gsm_utils.h index c87e967bd..51e9f2e6a 100644 --- a/libosmocore/include/osmocore/gsm_utils.h +++ b/libosmocore/include/osmocore/gsm_utils.h @@ -27,6 +27,13 @@ #include <stdint.h> +#define ADD_MODULO(sum, delta, modulo) do { \ + if ((sum += delta) >= modulo) \ + sum -= modulo; \ + } while (0) + +#define GSM_MAX_FN (26*51*2048) + struct gsm_time { uint32_t fn; /* FN count */ uint16_t t1; /* FN div (26*51) */ @@ -80,5 +87,17 @@ void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn); /* Convert from GSM time to frame number */ uint32_t gsm_gsmtime2fn(struct gsm_time *time); +/* GSM TS 03.03 Chapter 2.6 */ +enum gprs_tlli_tyoe { + TLLI_LOCAL, + TLLI_FOREIGN, + TLLI_RANDOM, + TLLI_AUXILIARY, + TLLI_RESERVED, +}; + +/* TS 03.03 Chapter 2.6 */ +int gprs_tlli_type(uint32_t tlli); + void generate_backtrace(); #endif diff --git a/libosmocore/include/osmocore/logging.h b/libosmocore/include/osmocore/logging.h index 93f18a07b..2e82959a8 100644 --- a/libosmocore/include/osmocore/logging.h +++ b/libosmocore/include/osmocore/logging.h @@ -117,6 +117,7 @@ void log_set_print_timestamp(struct log_target *target, int); void log_set_log_level(struct log_target *target, int log_level); void log_parse_category_mask(struct log_target *target, const char* mask); int log_parse_level(const char *lvl); +const char *log_level_str(unsigned int lvl); int log_parse_category(const char *category); void log_set_category_filter(struct log_target *target, int category, int enable, int level); @@ -127,4 +128,8 @@ struct log_target *log_target_create_stderr(void); void log_add_target(struct log_target *target); void log_del_target(struct log_target *target); +/* Gernerate command argument strings for VTY use */ +const char *log_vty_category_string(struct log_info *info); +const char *log_vty_level_string(struct log_info *info); + #endif /* _OSMOCORE_LOGGING_H */ diff --git a/libosmocore/include/osmocore/msgb.h b/libosmocore/include/osmocore/msgb.h index 31db71942..2841dc56e 100644 --- a/libosmocore/include/osmocore/msgb.h +++ b/libosmocore/include/osmocore/msgb.h @@ -23,15 +23,11 @@ #include <stdint.h> #include "linuxlist.h" -struct bts_link; - struct msgb { struct llist_head list; - /* ptr to the physical E1 link to the BTS(s) */ - struct gsm_bts_link *bts_link; - /* Part of which TRX logical channel we were received / transmitted */ + /* FIXME: move them into the control buffer */ struct gsm_bts_trx *trx; struct gsm_lchan *lchan; @@ -41,17 +37,11 @@ struct msgb { unsigned char *l2h; /* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */ unsigned char *l3h; - /* the layer 4 header */ - union { - unsigned char *smsh; - unsigned char *llch; - unsigned char *l4h; - }; + unsigned char *l4h; - /* the layer 5 header, GPRS: GMM header */ - unsigned char *gmmh; - uint32_t tlli; + /* the 'control buffer', large enough to contain 5 pointers */ + unsigned long cb[5]; uint16_t data_len; uint16_t len; @@ -71,7 +61,7 @@ extern void msgb_reset(struct msgb *m); #define msgb_l1(m) ((void *)(m->l1h)) #define msgb_l2(m) ((void *)(m->l2h)) #define msgb_l3(m) ((void *)(m->l3h)) -#define msgb_sms(m) ((void *)(m->smsh)) +#define msgb_sms(m) ((void *)(m->l4h)) static inline unsigned int msgb_l1len(const struct msgb *msgb) { diff --git a/libosmocore/include/osmocore/protocol/Makefile.am b/libosmocore/include/osmocore/protocol/Makefile.am index 6d8883e61..557950ec8 100644 --- a/libosmocore/include/osmocore/protocol/Makefile.am +++ b/libosmocore/include/osmocore/protocol/Makefile.am @@ -1,3 +1,3 @@ -osmocore_proto_HEADERS = gsm_04_08.h gsm_04_11.h gsm_04_80.h gsm_08_58.h gsm_12_21.h +osmocore_proto_HEADERS = gsm_04_08.h gsm_04_11.h gsm_04_80.h gsm_08_58.h gsm_12_21.h gsm_08_08.h osmocore_protodir = $(includedir)/osmocore/protocol diff --git a/libosmocore/include/osmocore/protocol/gsm_04_08.h b/libosmocore/include/osmocore/protocol/gsm_04_08.h index 801b9b54c..1a112a085 100644 --- a/libosmocore/include/osmocore/protocol/gsm_04_08.h +++ b/libosmocore/include/osmocore/protocol/gsm_04_08.h @@ -685,6 +685,7 @@ enum chreq_type { /* Chapter 5.1.2.2 */ #define GSM_CSTATE_NULL 0 #define GSM_CSTATE_INITIATED 1 +#define GSM_CSTATE_MM_CONNECTION_PEND 2 /* see 10.5.4.6 */ #define GSM_CSTATE_MO_CALL_PROC 3 #define GSM_CSTATE_CALL_DELIVERED 4 #define GSM_CSTATE_CALL_PRESENT 6 @@ -734,10 +735,17 @@ enum gsm48_bcap_rrq { GSM48_BCAP_RRQ_DUAL_FR = 3, }; - #define GSM48_TMSI_LEN 5 #define GSM48_MID_TMSI_LEN (GSM48_TMSI_LEN + 2) #define GSM48_MI_SIZE 32 +/* Chapter 10.4.4.15 */ +struct gsm48_ra_id { + uint8_t digits[3]; /* MCC + MNC BCD digits */ + uint16_t lac; /* Location Area Code */ + uint8_t rac; /* Routing Area Code */ +} __attribute__ ((packed)); + + #endif /* PROTO_GSM_04_08_H */ diff --git a/libosmocore/include/osmocore/protocol/gsm_08_08.h b/libosmocore/include/osmocore/protocol/gsm_08_08.h new file mode 100644 index 000000000..6b8f93595 --- /dev/null +++ b/libosmocore/include/osmocore/protocol/gsm_08_08.h @@ -0,0 +1,303 @@ +/* From GSM08.08 */ + +#ifndef GSM_0808_H +#define GSM_0808_H + +#include <stdlib.h> + +/* + * this is from GSM 03.03 CGI but is copied in GSM 08.08 + * in § 3.2.2.27 for Cell Identifier List + */ +enum CELL_IDENT { + CELL_IDENT_WHOLE_GLOBAL = 0, + CELL_IDENT_LAC_AND_CI = 1, + CELL_IDENT_CI = 2, + CELL_IDENT_NO_CELL = 3, + CELL_IDENT_LAI_AND_LAC = 4, + CELL_IDENT_LAC = 5, + CELL_IDENT_BSS = 6, + CELL_IDENT_UTRAN_PLMN_LAC_RNC = 8, + CELL_IDENT_UTRAN_RNC = 9, + CELL_IDENT_UTRAN_LAC_RNC = 10, +}; + + +/* GSM 08.06 § 6.3 */ +enum BSSAP_MSG_TYPE { + BSSAP_MSG_BSS_MANAGEMENT = 0x0, + BSSAP_MSG_DTAP = 0x1, +}; + +struct bssmap_header { + uint8_t type; + uint8_t length; +} __attribute__((packed)); + +struct dtap_header { + uint8_t type; + uint8_t link_id; + uint8_t length; +} __attribute__((packed)); + + +enum BSS_MAP_MSG_TYPE { + BSS_MAP_MSG_RESERVED_0 = 0, + + /* ASSIGNMENT MESSAGES */ + BSS_MAP_MSG_ASSIGMENT_RQST = 1, + BSS_MAP_MSG_ASSIGMENT_COMPLETE = 2, + BSS_MAP_MSG_ASSIGMENT_FAILURE = 3, + + /* HANDOVER MESSAGES */ + BSS_MAP_MSG_HANDOVER_RQST = 16, + BSS_MAP_MSG_HANDOVER_REQUIRED = 17, + BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE= 18, + BSS_MAP_MSG_HANDOVER_CMD = 19, + BSS_MAP_MSG_HANDOVER_COMPLETE = 20, + BSS_MAP_MSG_HANDOVER_SUCCEEDED = 21, + BSS_MAP_MSG_HANDOVER_FAILURE = 22, + BSS_MAP_MSG_HANDOVER_PERFORMED = 23, + BSS_MAP_MSG_HANDOVER_CANDIDATE_ENQUIRE = 24, + BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE = 25, + BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT = 26, + BSS_MAP_MSG_HANDOVER_DETECT = 27, + + /* RELEASE MESSAGES */ + BSS_MAP_MSG_CLEAR_CMD = 32, + BSS_MAP_MSG_CLEAR_COMPLETE = 33, + BSS_MAP_MSG_CLEAR_RQST = 34, + BSS_MAP_MSG_RESERVED_1 = 35, + BSS_MAP_MSG_RESERVED_2 = 36, + BSS_MAP_MSG_SAPI_N_REJECT = 37, + BSS_MAP_MSG_CONFUSION = 38, + + /* OTHER CONNECTION RELATED MESSAGES */ + BSS_MAP_MSG_SUSPEND = 40, + BSS_MAP_MSG_RESUME = 41, + BSS_MAP_MSG_CONNECTION_ORIENTED_INFORMATION = 42, + BSS_MAP_MSG_PERFORM_LOCATION_RQST = 43, + BSS_MAP_MSG_LSA_INFORMATION = 44, + BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE = 45, + BSS_MAP_MSG_PERFORM_LOCATION_ABORT = 46, + BSS_MAP_MSG_COMMON_ID = 47, + + /* GENERAL MESSAGES */ + BSS_MAP_MSG_RESET = 48, + BSS_MAP_MSG_RESET_ACKNOWLEDGE = 49, + BSS_MAP_MSG_OVERLOAD = 50, + BSS_MAP_MSG_RESERVED_3 = 51, + BSS_MAP_MSG_RESET_CIRCUIT = 52, + BSS_MAP_MSG_RESET_CIRCUIT_ACKNOWLEDGE = 53, + BSS_MAP_MSG_MSC_INVOKE_TRACE = 54, + BSS_MAP_MSG_BSS_INVOKE_TRACE = 55, + BSS_MAP_MSG_CONNECTIONLESS_INFORMATION = 58, + + /* TERRESTRIAL RESOURCE MESSAGES */ + BSS_MAP_MSG_BLOCK = 64, + BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE = 65, + BSS_MAP_MSG_UNBLOCK = 66, + BSS_MAP_MSG_UNBLOCKING_ACKNOWLEDGE = 67, + BSS_MAP_MSG_CIRCUIT_GROUP_BLOCK = 68, + BSS_MAP_MSG_CIRCUIT_GROUP_BLOCKING_ACKNOWLEDGE = 69, + BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCK = 70, + BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCKING_ACKNOWLEDGE = 71, + BSS_MAP_MSG_UNEQUIPPED_CIRCUIT = 72, + BSS_MAP_MSG_CHANGE_CIRCUIT = 78, + BSS_MAP_MSG_CHANGE_CIRCUIT_ACKNOWLEDGE = 79, + + /* RADIO RESOURCE MESSAGES */ + BSS_MAP_MSG_RESOURCE_RQST = 80, + BSS_MAP_MSG_RESOURCE_INDICATION = 81, + BSS_MAP_MSG_PAGING = 82, + BSS_MAP_MSG_CIPHER_MODE_CMD = 83, + BSS_MAP_MSG_CLASSMARK_UPDATE = 84, + BSS_MAP_MSG_CIPHER_MODE_COMPLETE = 85, + BSS_MAP_MSG_QUEUING_INDICATION = 86, + BSS_MAP_MSG_COMPLETE_LAYER_3 = 87, + BSS_MAP_MSG_CLASSMARK_RQST = 88, + BSS_MAP_MSG_CIPHER_MODE_REJECT = 89, + BSS_MAP_MSG_LOAD_INDICATION = 90, + + /* VGCS/VBS */ + BSS_MAP_MSG_VGCS_VBS_SETUP = 4, + BSS_MAP_MSG_VGCS_VBS_SETUP_ACK = 5, + BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE = 6, + BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST = 7, + BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT = 28, + BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE = 29, + BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION = 30, + BSS_MAP_MSG_UPLINK_RQST = 31, + BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE = 39, + BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION = 73, + BSS_MAP_MSG_UPLINK_RELEASE_INDICATION = 74, + BSS_MAP_MSG_UPLINK_REJECT_CMD = 75, + BSS_MAP_MSG_UPLINK_RELEASE_CMD = 76, + BSS_MAP_MSG_UPLINK_SEIZED_CMD = 77, +}; + +enum GSM0808_IE_CODING { + GSM0808_IE_CIRCUIT_IDENTITY_CODE = 1, + GSM0808_IE_RESERVED_0 = 2, + GSM0808_IE_RESOURCE_AVAILABLE = 3, + GSM0808_IE_CAUSE = 4, + GSM0808_IE_CELL_IDENTIFIER = 5, + GSM0808_IE_PRIORITY = 6, + GSM0808_IE_LAYER_3_HEADER_INFORMATION = 7, + GSM0808_IE_IMSI = 8, + GSM0808_IE_TMSI = 9, + GSM0808_IE_ENCRYPTION_INFORMATION = 10, + GSM0808_IE_CHANNEL_TYPE = 11, + GSM0808_IE_PERIODICITY = 12, + GSM0808_IE_EXTENDED_RESOURCE_INDICATOR = 13, + GSM0808_IE_NUMBER_OF_MSS = 14, + GSM0808_IE_RESERVED_1 = 15, + GSM0808_IE_RESERVED_2 = 16, + GSM0808_IE_RESERVED_3 = 17, + GSM0808_IE_CLASSMARK_INFORMATION_T2 = 18, + GSM0808_IE_CLASSMARK_INFORMATION_T3 = 19, + GSM0808_IE_INTERFERENCE_BAND_TO_USE = 20, + GSM0808_IE_RR_CAUSE = 21, + GSM0808_IE_RESERVED_4 = 22, + GSM0808_IE_LAYER_3_INFORMATION = 23, + GSM0808_IE_DLCI = 24, + GSM0808_IE_DOWNLINK_DTX_FLAG = 25, + GSM0808_IE_CELL_IDENTIFIER_LIST = 26, + GSM0808_IE_RESPONSE_RQST = 27, + GSM0808_IE_RESOURCE_INDICATION_METHOD = 28, + GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1 = 29, + GSM0808_IE_CIRCUIT_IDENTITY_CODE_LIST = 30, + GSM0808_IE_DIAGNOSTIC = 31, + GSM0808_IE_LAYER_3_MESSAGE_CONTENTS = 32, + GSM0808_IE_CHOSEN_CHANNEL = 33, + GSM0808_IE_TOTAL_RESOURCE_ACCESSIBLE = 34, + GSM0808_IE_CIPHER_RESPONSE_MODE = 35, + GSM0808_IE_CHANNEL_NEEDED = 36, + GSM0808_IE_TRACE_TYPE = 37, + GSM0808_IE_TRIGGERID = 38, + GSM0808_IE_TRACE_REFERENCE = 39, + GSM0808_IE_TRANSACTIONID = 40, + GSM0808_IE_MOBILE_IDENTITY = 41, + GSM0808_IE_OMCID = 42, + GSM0808_IE_FORWARD_INDICATOR = 43, + GSM0808_IE_CHOSEN_ENCR_ALG = 44, + GSM0808_IE_CIRCUIT_POOL = 45, + GSM0808_IE_CIRCUIT_POOL_LIST = 46, + GSM0808_IE_TIME_INDICATION = 47, + GSM0808_IE_RESOURCE_SITUATION = 48, + GSM0808_IE_CURRENT_CHANNEL_TYPE_1 = 49, + GSM0808_IE_QUEUEING_INDICATOR = 50, + GSM0808_IE_SPEECH_VERSION = 64, + GSM0808_IE_ASSIGNMENT_REQUIREMENT = 51, + GSM0808_IE_TALKER_FLAG = 53, + GSM0808_IE_CONNECTION_RELEASE_RQSTED = 54, + GSM0808_IE_GROUP_CALL_REFERENCE = 55, + GSM0808_IE_EMLPP_PRIORITY = 56, + GSM0808_IE_CONFIG_EVO_INDI = 57, + GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION = 58, + GSM0808_IE_LSA_IDENTIFIER = 59, + GSM0808_IE_LSA_IDENTIFIER_LIST = 60, + GSM0808_IE_LSA_INFORMATION = 61, + GSM0808_IE_LCS_QOS = 62, + GSM0808_IE_LSA_ACCESS_CTRL_SUPPR = 63, + GSM0808_IE_LCS_PRIORITY = 67, + GSM0808_IE_LOCATION_TYPE = 68, + GSM0808_IE_LOCATION_ESTIMATE = 69, + GSM0808_IE_POSITIONING_DATA = 70, + GSM0808_IE_LCS_CAUSE = 71, + GSM0808_IE_LCS_CLIENT_TYPE = 72, + GSM0808_IE_APDU = 73, + GSM0808_IE_NETWORK_ELEMENT_IDENTITY = 74, + GSM0808_IE_GPS_ASSISTANCE_DATA = 75, + GSM0808_IE_DECIPHERING_KEYS = 76, + GSM0808_IE_RETURN_ERROR_RQST = 77, + GSM0808_IE_RETURN_ERROR_CAUSE = 78, + GSM0808_IE_SEGMENTATION = 79, + GSM0808_IE_SERVICE_HANDOVER = 80, + GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS = 81, + GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000= 82, + GSM0808_IE_RESERVED_5 = 65, + GSM0808_IE_RESERVED_6 = 66, +}; + +enum gsm0808_cause { + GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE = 0, + GSM0808_CAUSE_RADIO_INTERFACE_FAILURE = 1, + GSM0808_CAUSE_UPLINK_QUALITY = 2, + GSM0808_CAUSE_UPLINK_STRENGTH = 3, + GSM0808_CAUSE_DOWNLINK_QUALITY = 4, + GSM0808_CAUSE_DOWNLINK_STRENGTH = 5, + GSM0808_CAUSE_DISTANCE = 6, + GSM0808_CAUSE_O_AND_M_INTERVENTION = 7, + GSM0808_CAUSE_RESPONSE_TO_MSC_INVOCATION = 8, + GSM0808_CAUSE_CALL_CONTROL = 9, + GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION = 10, + GSM0808_CAUSE_HANDOVER_SUCCESSFUL = 11, + GSM0808_CAUSE_BETTER_CELL = 12, + GSM0808_CAUSE_DIRECTED_RETRY = 13, + GSM0808_CAUSE_JOINED_GROUP_CALL_CHANNEL = 14, + GSM0808_CAUSE_TRAFFIC = 15, + GSM0808_CAUSE_EQUIPMENT_FAILURE = 32, + GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE = 33, + GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE = 34, + GSM0808_CAUSE_CCCH_OVERLOAD = 35, + GSM0808_CAUSE_PROCESSOR_OVERLOAD = 36, + GSM0808_CAUSE_BSS_NOT_EQUIPPED = 37, + GSM0808_CAUSE_MS_NOT_EQUIPPED = 38, + GSM0808_CAUSE_INVALID_CELL = 39, + GSM0808_CAUSE_TRAFFIC_LOAD = 40, + GSM0808_CAUSE_PREEMPTION = 41, + GSM0808_CAUSE_RQSTED_TRANSCODING_RATE_ADAPTION_UNAVAILABLE = 48, + GSM0808_CAUSE_CIRCUIT_POOL_MISMATCH = 49, + GSM0808_CAUSE_SWITCH_CIRCUIT_POOL = 50, + GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE = 51, + GSM0808_CAUSE_LSA_NOT_ALLOWED = 52, + GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED = 64, + GSM0808_CAUSE_TERRESTRIAL_CIRCUIT_ALREADY_ALLOCATED = 80, + GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS = 81, + GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING = 82, + GSM0808_CAUSE_INCORRECT_VALUE = 83, + GSM0808_CAUSE_UNKNOWN_MESSAGE_TYPE = 84, + GSM0808_CAUSE_UNKNOWN_INFORMATION_ELEMENT = 85, + GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC = 96, +}; + +/* GSM 08.08 3.2.2.11 Channel Type */ +enum gsm0808_chan_indicator { + GSM0808_CHAN_SPEECH = 1, + GSM0808_CHAN_DATA = 2, + GSM0808_CHAN_SIGN = 3, +}; + +enum gsm0808_chan_rate_type_data { + GSM0808_DATA_FULL_BM = 0x8, + GSM0808_DATA_HALF_LM = 0x9, + GSM0808_DATA_FULL_RPREF = 0xa, + GSM0808_DATA_HALF_PREF = 0xb, + GSM0808_DATA_FULL_PREF_NO_CHANGE = 0x1a, + GSM0808_DATA_HALF_PREF_NO_CHANGE = 0x1b, + GSM0808_DATA_MULTI_MASK = 0x20, + GSM0808_DATA_MULTI_MASK_NO_CHANGE = 0x30, +}; + +enum gsm0808_chan_rate_type_speech { + GSM0808_SPEECH_FULL_BM = 0x8, + GSM0808_SPEECH_HALF_LM = 0x9, + GSM0808_SPEECH_FULL_PREF= 0xa, + GSM0808_SPEECH_HALF_PREF= 0xb, + GSM0808_SPEECH_FULL_PREF_NO_CHANGE = 0x1a, + GSM0808_SPEECH_HALF_PREF_NO_CHANGE = 0x1b, + GSM0808_SPEECH_PERM = 0xf, + GSM0808_SPEECH_PERM_NO_CHANGE = 0x1f, +}; + +enum gsm0808_permitted_speech { + GSM0808_PERM_FR1 = 0x01, + GSM0808_PERM_FR2 = 0x11, + GSM0808_PERM_FR3 = 0x21, + GSM0808_PERM_HR1 = GSM0808_PERM_FR1 | 0x4, + GSM0808_PERM_HR2 = GSM0808_PERM_FR2 | 0x4, + GSM0808_PERM_HR3 = GSM0808_PERM_FR3 | 0x4, +}; + +#endif diff --git a/libosmocore/include/osmocore/rate_ctr.h b/libosmocore/include/osmocore/rate_ctr.h new file mode 100644 index 000000000..88c9de5a6 --- /dev/null +++ b/libosmocore/include/osmocore/rate_ctr.h @@ -0,0 +1,81 @@ +#ifndef _RATE_CTR_H +#define _RATE_CTR_H + +#include <stdint.h> + +#include <osmocore/linuxlist.h> + +#define RATE_CTR_INTV_NUM 4 + +enum rate_ctr_intv { + RATE_CTR_INTV_SEC, + RATE_CTR_INTV_MIN, + RATE_CTR_INTV_HOUR, + RATE_CTR_INTV_DAY, +}; + +/* for each of the intervals, we keep the following values */ +struct rate_ctr_per_intv { + uint64_t last; + uint64_t rate; +}; + +/* for each actual value, we keep the following data */ +struct rate_ctr { + uint64_t current; + struct rate_ctr_per_intv intv[RATE_CTR_INTV_NUM]; +}; + +struct rate_ctr_desc { + const char *name; + const char *description; +}; + +/* Describe a counter group class */ +struct rate_ctr_group_desc { + /* The prefix to the name of all counters in this group */ + char *group_name_prefix; + /* The human-readable description of the group */ + char *group_description; + /* The number of counters in this group */ + unsigned int num_ctr; + /* Pointer to array of counter names */ + struct rate_ctr_desc *ctr_desc; +}; + +/* One instance of a counter group class */ +struct rate_ctr_group { + /* Linked list of all counter groups in the system */ + struct llist_head list; + /* Pointer to the counter group class */ + const struct rate_ctr_group_desc *desc; + /* The index of this ctr_group within its class */ + unsigned int idx; + /* Actual counter structures below */ + struct rate_ctr ctr[0]; +}; + +/* Allocate a new group of counters according to description */ +struct rate_ctr_group *rate_ctr_group_alloc(void *ctx, + const struct rate_ctr_group_desc *desc, + unsigned int idx); + +/* Free the memory for the specified group of counters */ +void rate_ctr_group_free(struct rate_ctr_group *grp); + +/* Add a number to the counter */ +void rate_ctr_add(struct rate_ctr *ctr, int inc); + +/* Increment the counter by 1 */ +static inline void rate_ctr_inc(struct rate_ctr *ctr) +{ + rate_ctr_add(ctr, 1); +} + +/* Initialize the counter module */ +int rate_ctr_init(void *tall_ctx); + +struct vty; +void vty_out_rate_ctr_group(struct vty *vty, const char *prefix, + struct rate_ctr_group *ctrg); +#endif /* RATE_CTR_H */ diff --git a/libosmocore/include/osmocore/write_queue.h b/libosmocore/include/osmocore/write_queue.h index 64d4159a0..ef244c32a 100644 --- a/libosmocore/include/osmocore/write_queue.h +++ b/libosmocore/include/osmocore/write_queue.h @@ -35,6 +35,7 @@ struct write_queue { int (*read_cb)(struct bsc_fd *fd); int (*write_cb)(struct bsc_fd *fd, struct msgb *msg); + int (*except_cb)(struct bsc_fd *fd); }; void write_queue_init(struct write_queue *queue, int max_length); diff --git a/libosmocore/src/Makefile.am b/libosmocore/src/Makefile.am index 16978074a..ce063d058 100644 --- a/libosmocore/src/Makefile.am +++ b/libosmocore/src/Makefile.am @@ -10,7 +10,7 @@ lib_LTLIBRARIES = libosmocore.la libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c rxlev_stat.c \ tlv_parser.c bitvec.c comp128.c gsm_utils.c statistics.c \ write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c \ - logging.c + logging.c gsm0808.c rate_ctr.c if ENABLE_TALLOC libosmocore_la_SOURCES += talloc.c diff --git a/libosmocore/src/gsm0808.c b/libosmocore/src/gsm0808.c new file mode 100644 index 000000000..7a7eb3a08 --- /dev/null +++ b/libosmocore/src/gsm0808.c @@ -0,0 +1,295 @@ +/* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009,2010 by On-Waves + * 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 <osmocore/gsm0808.h> +#include <osmocore/protocol/gsm_08_08.h> +#include <osmocore/gsm48.h> + +#include <arpa/inet.h> + +#define BSSMAP_MSG_SIZE 512 +#define BSSMAP_MSG_HEADROOM 128 + +struct msgb *gsm0808_create_layer3(struct msgb *msg_l3, uint16_t nc, uint16_t cc, int lac, int _ci) +{ + uint8_t *data; + uint16_t *ci; + struct msgb* msg; + struct gsm48_loc_area_id *lai; + + msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap cmpl l3"); + if (!msg) + return NULL; + + /* create the bssmap header */ + msg->l3h = msgb_put(msg, 2); + msg->l3h[0] = 0x0; + + /* create layer 3 header */ + data = msgb_put(msg, 1); + data[0] = BSS_MAP_MSG_COMPLETE_LAYER_3; + + /* create the cell header */ + data = msgb_put(msg, 3); + data[0] = GSM0808_IE_CELL_IDENTIFIER; + data[1] = 1 + sizeof(*lai) + 2; + data[2] = CELL_IDENT_WHOLE_GLOBAL; + + lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); + gsm48_generate_lai(lai, cc, nc, lac); + + ci = (uint16_t *) msgb_put(msg, 2); + *ci = htons(_ci); + + /* copy the layer3 data */ + data = msgb_put(msg, msgb_l3len(msg_l3) + 2); + data[0] = GSM0808_IE_LAYER_3_INFORMATION; + data[1] = msgb_l3len(msg_l3); + memcpy(&data[2], msg_l3->l3h, data[1]); + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + + return msg; +} + +struct msgb *gsm0808_create_reset(void) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: reset"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 6); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0x04; + msg->l3h[2] = 0x30; + msg->l3h[3] = 0x04; + msg->l3h[4] = 0x01; + msg->l3h[5] = 0x20; + return msg; +} + +struct msgb *gsm0808_create_clear_complete(void) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: clear complete"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 1; + msg->l3h[2] = BSS_MAP_MSG_CLEAR_COMPLETE; + + return msg; +} + +struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "cipher-complete"); + if (!msg) + return NULL; + + /* send response with BSS override for A5/1... cheating */ + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_COMPLETE; + + /* include layer3 in case we have at least two octets */ + if (layer3 && msgb_l3len(layer3) > 2) { + msg->l4h = msgb_put(msg, msgb_l3len(layer3) + 2); + msg->l4h[0] = GSM0808_IE_LAYER_3_MESSAGE_CONTENTS; + msg->l4h[1] = msgb_l3len(layer3); + memcpy(&msg->l4h[2], layer3->l3h, msgb_l3len(layer3)); + } + + /* and the optional BSS message */ + msg->l4h = msgb_put(msg, 2); + msg->l4h[0] = GSM0808_IE_CHOSEN_ENCR_ALG; + msg->l4h[1] = alg_id; + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *gsm0808_create_cipher_reject(uint8_t cause) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: clear complete"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 2; + msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_REJECT; + msg->l3h[3] = cause; + + return msg; +} + +struct msgb *gsm0808_create_classmark_update(const uint8_t *classmark_data, uint8_t length) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "classmark-update"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_CLASSMARK_UPDATE; + + msg->l4h = msgb_put(msg, length); + memcpy(msg->l4h, classmark_data, length); + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *gsm0808_create_sapi_reject(uint8_t link_id) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: sapi 'n' reject"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 5); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 3; + msg->l3h[2] = BSS_MAP_MSG_SAPI_N_REJECT; + msg->l3h[3] = link_id; + msg->l3h[4] = GSM0808_CAUSE_BSS_NOT_EQUIPPED; + + return msg; +} + +struct msgb *gsm0808_create_assignment_completed(struct gsm_lchan *lchan, uint8_t rr_cause, + uint8_t chosen_channel, uint8_t encr_alg_id, + uint8_t speech_mode) +{ + uint8_t *data; + + struct msgb *msg = msgb_alloc(35, "bssmap: ass compl"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_COMPLETE; + + /* write 3.2.2.22 */ + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_RR_CAUSE; + data[1] = rr_cause; + + /* write cirtcuit identity code 3.2.2.2 */ + /* write cell identifier 3.2.2.17 */ + /* write chosen channel 3.2.2.33 when BTS picked it */ + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_CHOSEN_CHANNEL; + data[1] = chosen_channel; + + /* write chosen encryption algorithm 3.2.2.44 */ + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_CHOSEN_ENCR_ALG; + data[1] = encr_alg_id; + + /* write circuit pool 3.2.2.45 */ + /* write speech version chosen: 3.2.2.51 when BTS picked it */ + if (speech_mode != 0) { + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_SPEECH_VERSION; + data[1] = speech_mode; + } + + /* write LSA identifier 3.2.2.15 */ + + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause) +{ + uint8_t *data; + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: ass fail"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 6); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_FAILURE; + msg->l3h[3] = GSM0808_IE_CAUSE; + msg->l3h[4] = 1; + msg->l3h[5] = cause; + + /* RR cause 3.2.2.22 */ + if (rr_cause) { + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_RR_CAUSE; + data[1] = *rr_cause; + } + + /* Circuit pool 3.22.45 */ + /* Circuit pool list 3.2.2.46 */ + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +static const struct tlv_definition bss_att_tlvdef = { + .def = { + [GSM0808_IE_IMSI] = { TLV_TYPE_TLV }, + [GSM0808_IE_TMSI] = { TLV_TYPE_TLV }, + [GSM0808_IE_CELL_IDENTIFIER_LIST] = { TLV_TYPE_TLV }, + [GSM0808_IE_CHANNEL_NEEDED] = { TLV_TYPE_TV }, + [GSM0808_IE_EMLPP_PRIORITY] = { TLV_TYPE_TV }, + [GSM0808_IE_CHANNEL_TYPE] = { TLV_TYPE_TLV }, + [GSM0808_IE_PRIORITY] = { TLV_TYPE_TLV }, + [GSM0808_IE_CIRCUIT_IDENTITY_CODE] = { TLV_TYPE_TV }, + [GSM0808_IE_DOWNLINK_DTX_FLAG] = { TLV_TYPE_TV }, + [GSM0808_IE_INTERFERENCE_BAND_TO_USE] = { TLV_TYPE_TV }, + [GSM0808_IE_CLASSMARK_INFORMATION_T2] = { TLV_TYPE_TLV }, + [GSM0808_IE_GROUP_CALL_REFERENCE] = { TLV_TYPE_TLV }, + [GSM0808_IE_TALKER_FLAG] = { TLV_TYPE_T }, + [GSM0808_IE_CONFIG_EVO_INDI] = { TLV_TYPE_TV }, + [GSM0808_IE_LSA_ACCESS_CTRL_SUPPR] = { TLV_TYPE_TV }, + [GSM0808_IE_SERVICE_HANDOVER] = { TLV_TYPE_TV}, + [GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV }, + [GSM0808_IE_CIPHER_RESPONSE_MODE] = { TLV_TYPE_TV }, + }, +}; + +const struct tlv_definition *gsm0808_att_tlvdef() +{ + return &bss_att_tlvdef; +} diff --git a/libosmocore/src/gsm48.c b/libosmocore/src/gsm48.c index 5761c67b4..d957aef60 100644 --- a/libosmocore/src/gsm48.c +++ b/libosmocore/src/gsm48.c @@ -97,10 +97,10 @@ static const struct value_string rr_cause_names[] = { }; /* FIXME: convert to value_string */ -static const char *cc_state_names[32] = { +static const char *cc_state_names[33] = { "NULL", "INITIATED", - "illegal state 2", + "MM_CONNECTION_PEND", "MO_CALL_PROC", "CALL_DELIVERED", "illegal state 5", @@ -261,3 +261,93 @@ int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi) return 2 + buf[1]; } + +/* Convert Mobile Identity (10.5.1.4) to string */ +int gsm48_mi_to_string(char *string, const int str_len, const uint8_t *mi, + const int mi_len) +{ + int i; + uint8_t mi_type; + char *str_cur = string; + uint32_t tmsi; + + mi_type = mi[0] & GSM_MI_TYPE_MASK; + + switch (mi_type) { + case GSM_MI_TYPE_NONE: + break; + case GSM_MI_TYPE_TMSI: + /* Table 10.5.4.3, reverse generate_mid_from_tmsi */ + if (mi_len == GSM48_TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) { + memcpy(&tmsi, &mi[1], 4); + tmsi = ntohl(tmsi); + return snprintf(string, str_len, "%u", tmsi); + } + break; + case GSM_MI_TYPE_IMSI: + case GSM_MI_TYPE_IMEI: + case GSM_MI_TYPE_IMEISV: + *str_cur++ = bcd2char(mi[0] >> 4); + + for (i = 1; i < mi_len; i++) { + if (str_cur + 2 >= string + str_len) + return str_cur - string; + *str_cur++ = bcd2char(mi[i] & 0xf); + /* skip last nibble in last input byte when GSM_EVEN */ + if( (i != mi_len-1) || (mi[0] & GSM_MI_ODD)) + *str_cur++ = bcd2char(mi[i] >> 4); + } + break; + default: + break; + } + *str_cur++ = '\0'; + + return str_cur - string; +} + +void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_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(*(uint16_t *)(buf + 3)); + raid->rac = buf[5]; +} + +int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid) +{ + uint16_t mcc = raid->mcc; + uint16_t mnc = raid->mnc; + + buf[0] = ((mcc / 100) % 10) | (((mcc / 10) % 10) << 4); + buf[1] = (mcc % 10); + + /* I wonder who came up with the stupidity of encoding the MNC + * differently depending on how many digits its decimal number has! */ + if (mnc < 100) { + buf[1] |= 0xf0; + buf[2] = ((mnc / 10) % 10) | ((mnc % 10) << 4); + } else { + buf[1] |= (mnc % 10) << 4; + buf[2] = ((mnc / 100) % 10) | (((mcc / 10) % 10) << 4); + } + + *(uint16_t *)(buf+3) = htons(raid->lac); + + buf[5] = raid->rac; + + return 6; +} diff --git a/libosmocore/src/gsm48_ie.c b/libosmocore/src/gsm48_ie.c index 4ca5fb800..3c2a1f7b6 100644 --- a/libosmocore/src/gsm48_ie.c +++ b/libosmocore/src/gsm48_ie.c @@ -2,7 +2,7 @@ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ /* (C) 2008 by Harald Welte <laforge@gnumonks.org> - * (C) 2008-2010 by Andreas Eversberg + * (C) 2009-2010 by Andreas Eversberg * * All Rights Reserved * diff --git a/libosmocore/src/gsm_utils.c b/libosmocore/src/gsm_utils.c index 593dd5c94..b392fd37c 100644 --- a/libosmocore/src/gsm_utils.c +++ b/libosmocore/src/gsm_utils.c @@ -359,3 +359,18 @@ uint32_t gsm_gsmtime2fn(struct gsm_time *time) /* TS 05.02 Chapter 4.3.3 TDMA frame number */ return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1)); } + +/* TS 03.03 Chapter 2.6 */ +int gprs_tlli_type(uint32_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; +} diff --git a/libosmocore/src/logging.c b/libosmocore/src/logging.c index 508ccfd3e..e72a6e204 100644 --- a/libosmocore/src/logging.c +++ b/libosmocore/src/logging.c @@ -20,11 +20,16 @@ * */ +#include "../config.h" + #include <stdarg.h> #include <stdlib.h> #include <stdio.h> #include <string.h> + +#ifdef HAVE_STRINGS_H #include <strings.h> +#endif #include <time.h> #include <errno.h> @@ -53,6 +58,11 @@ int log_parse_level(const char *lvl) return get_string_value(loglevel_strs, lvl); } +const char *log_level_str(unsigned int lvl) +{ + return get_value_string(loglevel_strs, lvl); +} + int log_parse_category(const char *category) { int i; @@ -302,31 +312,46 @@ void log_set_category_filter(struct log_target *target, int category, target->categories[category].loglevel = level; } +/* since C89/C99 says stderr is a macro, we can safely do this! */ +#ifdef stderr static void _stderr_output(struct log_target *target, const char *log) { fprintf(target->tgt_stdout.out, "%s", log); fflush(target->tgt_stdout.out); } +#endif struct log_target *log_target_create(void) { struct log_target *target; + unsigned int i; target = talloc_zero(tall_log_ctx, struct log_target); if (!target) return NULL; INIT_LLIST_HEAD(&target->entry); - memcpy(target->categories, log_info->cat, - sizeof(struct log_category)*log_info->num_cat); + + /* initialize the per-category enabled/loglevel from defaults */ + for (i = 0; i < log_info->num_cat; i++) { + struct log_category *cat = &target->categories[i]; + cat->enabled = log_info->cat[i].enabled; + cat->loglevel = log_info->cat[i].loglevel; + } + + /* global settings */ target->use_color = 1; target->print_timestamp = 0; + + /* global log level */ target->loglevel = 0; return target; } struct log_target *log_target_create_stderr(void) { +/* since C89/C99 says stderr is a macro, we can safely do this! */ +#ifdef stderr struct log_target *target; target = log_target_create(); @@ -336,6 +361,55 @@ struct log_target *log_target_create_stderr(void) target->tgt_stdout.out = stderr; target->output = _stderr_output; return target; +#else + return NULL; +#endif /* stderr */ +} + +const char *log_vty_level_string(struct log_info *info) +{ + const struct value_string *vs; + unsigned int len = 3; /* ()\0 */ + char *str; + + for (vs = loglevel_strs; vs->value || vs->str; vs++) + len += strlen(vs->str) + 1; + + str = talloc_zero_size(NULL, len); + if (!str) + return NULL; + + str[0] = '('; + for (vs = loglevel_strs; vs->value || vs->str; vs++) { + strcat(str, vs->str); + strcat(str, "|"); + } + str[strlen(str)-1] = ')'; + + return str; +} + +const char *log_vty_category_string(struct log_info *info) +{ + unsigned int len = 3; /* "()\0" */ + unsigned int i; + char *str; + + for (i = 0; i < info->num_cat; i++) + len += strlen(info->cat[i].name) + 1; + + str = talloc_zero_size(NULL, len); + if (!str) + return NULL; + + str[0] = '('; + for (i = 0; i < info->num_cat; i++) { + strcat(str, info->cat[i].name+1); + strcat(str, "|"); + } + str[strlen(str)-1] = ')'; + + return str; } void log_init(const struct log_info *cat) diff --git a/libosmocore/src/msgb.c b/libosmocore/src/msgb.c index 60af373eb..a60e2ffa5 100644 --- a/libosmocore/src/msgb.c +++ b/libosmocore/src/msgb.c @@ -80,10 +80,11 @@ void msgb_reset(struct msgb *msg) msg->head = msg->_data; msg->tail = msg->_data; - msg->bts_link = NULL; msg->trx = NULL; msg->lchan = NULL; msg->l2h = NULL; msg->l3h = NULL; - msg->smsh = NULL; + msg->l4h = NULL; + + memset(&msg->cb, 0, sizeof(msg->cb)); } diff --git a/libosmocore/src/rate_ctr.c b/libosmocore/src/rate_ctr.c new file mode 100644 index 000000000..e48c77928 --- /dev/null +++ b/libosmocore/src/rate_ctr.c @@ -0,0 +1,122 @@ +/* utility routines for keeping conters about events and the event rates */ + +/* (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 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 <stdint.h> +#include <inttypes.h> +#include <string.h> + +#include <osmocore/linuxlist.h> +#include <osmocore/talloc.h> +#include <osmocore/timer.h> +#include <osmocore/rate_ctr.h> + +static LLIST_HEAD(rate_ctr_groups); + +static void *tall_rate_ctr_ctx; + +struct rate_ctr_group *rate_ctr_group_alloc(void *ctx, + const struct rate_ctr_group_desc *desc, + unsigned int idx) +{ + unsigned int size; + struct rate_ctr_group *group; + + size = sizeof(struct rate_ctr_group) + + desc->num_ctr * sizeof(struct rate_ctr); + + if (!ctx) + ctx = tall_rate_ctr_ctx; + + group = talloc_zero_size(ctx, size); + if (!group) + return NULL; + + group->desc = desc; + group->idx = idx; + + llist_add(&group->list, &rate_ctr_groups); + + return group; +} + +void rate_ctr_group_free(struct rate_ctr_group *grp) +{ + llist_del(&grp->list); + talloc_free(grp); +} + +void rate_ctr_add(struct rate_ctr *ctr, int inc) +{ + ctr->current += inc; +} + +static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv) +{ + /* calculate rate over last interval */ + ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last; + /* save current counter for next interval */ + ctr->intv[intv].last = ctr->current; +} + +static struct timer_list rate_ctr_timer; +static uint64_t timer_ticks; + +/* The one-second interval has expired */ +static void rate_ctr_group_intv(struct rate_ctr_group *grp) +{ + unsigned int i; + + for (i = 0; i < grp->desc->num_ctr; i++) { + struct rate_ctr *ctr = &grp->ctr[i]; + + interval_expired(ctr, RATE_CTR_INTV_SEC); + if ((timer_ticks % 60) == 0) + interval_expired(ctr, RATE_CTR_INTV_MIN); + if ((timer_ticks % (60*60)) == 0) + interval_expired(ctr, RATE_CTR_INTV_HOUR); + if ((timer_ticks % (24*60*60)) == 0) + interval_expired(ctr, RATE_CTR_INTV_DAY); + } +} + +static void rate_ctr_timer_cb(void *data) +{ + struct rate_ctr_group *ctrg; + + /* Increment number of ticks before we calculate intervals, + * as a counter value of 0 would already wrap all counters */ + timer_ticks++; + + llist_for_each_entry(ctrg, &rate_ctr_groups, list) + rate_ctr_group_intv(ctrg); + + bsc_schedule_timer(&rate_ctr_timer, 1, 0); +} + +int rate_ctr_init(void *tall_ctx) +{ + tall_rate_ctr_ctx = tall_ctx; + rate_ctr_timer.cb = rate_ctr_timer_cb; + bsc_schedule_timer(&rate_ctr_timer, 1, 0); + + return 0; +} diff --git a/libosmocore/src/select.c b/libosmocore/src/select.c index 9517778ce..2f6afa7f5 100644 --- a/libosmocore/src/select.c +++ b/libosmocore/src/select.c @@ -121,7 +121,8 @@ restart: /* ugly, ugly hack. If more than one filedescriptors were * unregistered, they might have been consecutive and * llist_for_each_entry_safe() is no longer safe */ - if (unregistered_count > 1) + /* this seems to happen with the last element of the list as well */ + if (unregistered_count >= 1) goto restart; } return work; diff --git a/libosmocore/src/write_queue.c b/libosmocore/src/write_queue.c index a0ac2d6fd..618a8c0b3 100644 --- a/libosmocore/src/write_queue.c +++ b/libosmocore/src/write_queue.c @@ -32,6 +32,9 @@ int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what) if (what & BSC_FD_READ) queue->read_cb(fd); + if (what & BSC_FD_EXCEPT) + queue->except_cb(fd); + if (what & BSC_FD_WRITE) { struct msgb *msg; diff --git a/openbsc/README b/openbsc/README index 51807bb44..fa01ff4cd 100644 --- a/openbsc/README +++ b/openbsc/README @@ -14,6 +14,8 @@ Its currently supported interfaces towards the BTS are: * A-bis over IP as used by the ip.access nanoBTS product family +You can find the project documentation at http://openbsc.gnumonks.org/ + This project is still in its early days, and there are lots of areas where it doesn't behave as per GSM spec. diff --git a/openbsc/configure.in b/openbsc/configure.in index 615e59d91..66d4ee1f3 100644 --- a/openbsc/configure.in +++ b/openbsc/configure.in @@ -18,7 +18,7 @@ dnl checks for libraries AC_SEARCH_LIBS(crypt, crypt, [LIBCRYPT="-lcrypt"; AC_DEFINE([VTY_CRYPT_PW], [], [Use crypt functionality of vty.])]) -PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.1.3) +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.1.6) dnl checks for header files AC_HEADER_STDC @@ -48,6 +48,8 @@ AC_OUTPUT( include/sccp/Makefile include/Makefile src/Makefile + src/ipaccess/Makefile + src/gprs/Makefile tests/Makefile tests/debug/Makefile tests/gsm0408/Makefile diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index 259e6d6f5..afb62dec2 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -6,7 +6,9 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \ bsc_rll.h mncc.h transaction.h ussd.h gsm_04_80.h \ silent_call.h mgcp.h meas_rep.h rest_octets.h \ system_information.h handover.h mgcp_internal.h \ - vty.h + vty.h \ + crc24.h gprs_bssgp.h gprs_llc.h gprs_ns.h \ + gb_proxy.h gprs_sgsn.h gsm_04_08_gprs.h sgsn.h openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h openbscdir = $(includedir)/openbsc diff --git a/openbsc/include/openbsc/abis_nm.h b/openbsc/include/openbsc/abis_nm.h index 45307e3c8..c20e4e172 100644 --- a/openbsc/include/openbsc/abis_nm.h +++ b/openbsc/include/openbsc/abis_nm.h @@ -92,7 +92,7 @@ int abis_nm_sw_act_req_ack(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i1, int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *msg); int abis_nm_event_reports(struct gsm_bts *bts, int on); int abis_nm_reset_resource(struct gsm_bts *bts); -int abis_nm_software_load(struct gsm_bts *bts, const char *fname, +int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname, u_int8_t win_size, int forced, gsm_cbfn *cbfn, void *cb_data); int abis_nm_software_load_status(struct gsm_bts *bts); @@ -148,7 +148,7 @@ int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type, u_int8_t *attr, int attr_len); int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, u_int8_t *attr, int attr_len); -int abis_nm_ipaccess_restart(struct gsm_bts *bts); +int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx); int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr, u_int8_t *attr, u_int8_t attr_len); @@ -164,7 +164,8 @@ enum nm_evt { EVT_STATECHG_ADM, }; int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, - struct gsm_nm_state *old_state, struct gsm_nm_state *new_state); + struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, + struct abis_om_obj_inst *obj_inst); const char *nm_opstate_name(u_int8_t os); const char *nm_avail_name(u_int8_t avail); diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h index e6973eef0..8e6774d15 100644 --- a/openbsc/include/openbsc/abis_rsl.h +++ b/openbsc/include/openbsc/abis_rsl.h @@ -70,10 +70,11 @@ u_int64_t str_to_imsi(const char *imsi_str); u_int8_t lchan2chan_nr(const struct gsm_lchan *lchan); int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id); +int rsl_lchan_set_state(struct gsm_lchan *lchan, int); + /* to be provided by external code */ int abis_rsl_sendmsg(struct msgb *msg); int rsl_deact_sacch(struct gsm_lchan *lchan); -int rsl_chan_release(struct gsm_lchan *lchan); /* BCCH related code */ int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf); diff --git a/openbsc/include/openbsc/chan_alloc.h b/openbsc/include/openbsc/chan_alloc.h index f564e9e4d..d4f5858b7 100644 --- a/openbsc/include/openbsc/chan_alloc.h +++ b/openbsc/include/openbsc/chan_alloc.h @@ -45,6 +45,7 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type); /* Free a logical channel (SDCCH, TCH, ...) */ void lchan_free(struct gsm_lchan *lchan); +void lchan_reset(struct gsm_lchan *lchan); /* Consider releasing the channel */ int lchan_auto_release(struct gsm_lchan *lchan); 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/db.h b/openbsc/include/openbsc/db.h index d0a1278ef..1782efb50 100644 --- a/openbsc/include/openbsc/db.h +++ b/openbsc/include/openbsc/db.h @@ -66,6 +66,9 @@ int db_apdu_blob_store(struct gsm_subscriber *subscr, u_int8_t *apdu); /* Statistics counter storage */ +struct counter; int db_store_counter(struct counter *ctr); +struct rate_ctr_group; +int db_store_rate_ctr_group(struct rate_ctr_group *ctrg); #endif /* _DB_H */ diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h index f1c5a699a..65fd0bb53 100644 --- a/openbsc/include/openbsc/debug.h +++ b/openbsc/include/openbsc/debug.h @@ -29,6 +29,9 @@ enum { DHO, DDB, DREF, + DGPRS, + DNS, + DBSSGP, Debug_LastEntry, }; diff --git a/openbsc/include/openbsc/gb_proxy.h b/openbsc/include/openbsc/gb_proxy.h new file mode 100644 index 000000000..db236b5d0 --- /dev/null +++ b/openbsc/include/openbsc/gb_proxy.h @@ -0,0 +1,42 @@ +#ifndef _GB_PROXY_H +#define _GB_PROXY_H + +#include <sys/types.h> + +#include <osmocore/msgb.h> + +#include <openbsc/gprs_ns.h> +#include <vty/command.h> + +struct gbproxy_config { + /* parsed from config file */ + u_int32_t nsip_listen_ip; + u_int16_t nsip_listen_port; + + u_int32_t nsip_sgsn_ip; + u_int16_t nsip_sgsn_port; + + u_int16_t nsip_sgsn_nsei; + u_int16_t nsip_sgsn_nsvci; + + /* misc */ + struct gprs_ns_inst *nsi; +}; + +extern struct gbproxy_config gbcfg; +extern struct cmd_element show_gbproxy_cmd; + +/* gb_proxy_vty .c */ + +int gbproxy_vty_init(void); +int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg); + + +/* gb_proxy.c */ + +/* Main input function for Gb proxy */ +int gbprox_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci); + +int gbprox_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data); +#endif diff --git a/openbsc/include/openbsc/gprs_bssgp.h b/openbsc/include/openbsc/gprs_bssgp.h new file mode 100644 index 000000000..d3ccb12ee --- /dev/null +++ b/openbsc/include/openbsc/gprs_bssgp.h @@ -0,0 +1,163 @@ +#ifndef _GPRS_BSSGP_H +#define _GPRS_BSSGP_H + +#include <stdint.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 { + uint8_t pdu_type; + uint32_t tlli; + uint8_t qos_profile[3]; + uint8_t data[0]; /* TLV's */ +} __attribute__((packed)); + +struct bssgp_normal_hdr { + uint8_t pdu_type; + uint8_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, +}; + +/* Our implementation */ + +/* gprs_bssgp_util.c */ +extern struct gprs_ns_inst *bssgp_nsi; +struct msgb *bssgp_msgb_alloc(void); +const char *bssgp_cause_str(enum gprs_bssgp_cause cause); +/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */ +int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei, + uint16_t bvci, uint16_t ns_bvci); +/* Chapter 10.4.14: Status */ +int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg); + +/* gprs_bssgp.c */ + +#include <osmocore/tlv.h> + +extern int gprs_bssgp_rcvmsg(struct msgb *msg); +uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf); + +/* 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); +} + +#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..5a6682d80 --- /dev/null +++ b/openbsc/include/openbsc/gprs_llc.h @@ -0,0 +1,32 @@ +#ifndef _GPRS_LLC_H +#define _GPRS_LLC_H + +#include <stdint.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, +}; + +/* Section 6.4 Commands and Responses */ +enum gprs_llc_u_cmd { + GPRS_LLC_U_DM_RESP = 0x01, + GPRS_LLC_U_DISC_CMD = 0x04, + GPRS_LLC_U_UA_RESP = 0x06, + GPRS_LLC_U_SABM_CMD = 0x07, + GPRS_LLC_U_FRMR_RESP = 0x08, + GPRS_LLC_U_XID = 0x0b, + GPRS_LLC_U_NULL_CMD = 0x00, +}; + +int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv); +int gprs_llc_tx_ui(struct msgb *msg, uint8_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..4ccf4c7b9 --- /dev/null +++ b/openbsc/include/openbsc/gprs_ns.h @@ -0,0 +1,215 @@ +#ifndef _GPRS_NS_H +#define _GPRS_NS_H + +#include <stdint.h> + +/* GPRS Networks Service (NS) messages on the Gb interface + * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) + * 3GPP TS 48.016 version 6.5.0 Release 6 / ETSI TS 148 016 V6.5.0 (2005-11) */ + +struct gprs_ns_hdr { + uint8_t pdu_type; + uint8_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 48.016 Section 10.3.7, Table 10.3.7.1 */ + SNS_PDUT_ACK = 0x0c, + SNS_PDUT_ADD = 0x0d, + SNS_PDUT_CHANGE_WEIGHT = 0x0e, + SNS_PDUT_CONFIG = 0x0f, + SNS_PDUT_CONFIG_ACK = 0x10, + SNS_PDUT_DELETE = 0x11, + SNS_PDUT_SIZE = 0x12, + SNS_PDUT_SIZE_ACK = 0x13, +}; + +/* TS 08.16, Section 10.3, Table 12 */ +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, +}; + +/* TS 08.16, Section 10.3.2, Table 13 */ +enum ns_cause { + NS_CAUSE_TRANSIT_FAIL = 0x00, + NS_CAUSE_OM_INTERVENTION = 0x01, + NS_CAUSE_EQUIP_FAIL = 0x02, + NS_CAUSE_NSVC_BLOCKED = 0x03, + NS_CAUSE_NSVC_UNKNOWN = 0x04, + NS_CAUSE_BVCI_UNKNOWN = 0x05, + NS_CAUSE_SEM_INCORR_PDU = 0x08, + NS_CAUSE_PDU_INCOMP_PSTATE = 0x0a, + NS_CAUSE_PROTO_ERR_UNSPEC = 0x0b, + NS_CAUSE_INVAL_ESSENT_IE = 0x0c, + NS_CAUSE_MISSING_ESSENT_IE = 0x0d, + /* TS 48.016 Section 10.3.2, Table 10.3.2.1 */ + NS_CAUSE_INVAL_NR_IPv4_EP = 0x0e, + NS_CAUSE_INVAL_NR_IPv6_EP = 0x0f, + NS_CAUSE_INVAL_NR_NS_VC = 0x10, + NS_CAUSE_INVAL_WEIGH = 0x11, + NS_CAUSE_UNKN_IP_EP = 0x12, + NS_CAUSE_UNKN_IP_ADDR = 0x13, + NS_CAUSE_UNKN_IP_TEST_FAILED = 0x14, +}; + +/* Our Implementation */ +#include <netinet/in.h> +#include <osmocore/linuxlist.h> +#include <osmocore/msgb.h> +#include <osmocore/timer.h> +#include <osmocore/select.h> + +#define NS_TIMERS_COUNT 7 +#define NS_TIMERS "(tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries)" +#define NS_TIMERS_HELP \ + "(un)blocking Timer (Tns-block) timeout\n" \ + "(un)blocking Timer (Tns-block) number of retries\n" \ + "Reset Timer (Tns-reset) timeout\n" \ + "Reset Timer (Tns-reset) number of retries\n" \ + "Test Timer (Tns-test) timeout\n" \ + +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 + +enum gprs_ns_ll { + GPRS_NS_LL_UDP, + GPRS_NS_LL_E1, +}; + +enum gprs_ns_evt { + GPRS_NS_EVT_UNIT_DATA, +}; + +struct gprs_nsvc; +typedef int gprs_ns_cb_t(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, + struct msgb *msg, uint16_t bvci); + +/* An instance of the NS protocol stack */ +struct gprs_ns_inst { + /* callback to the user for incoming UNIT DATA IND */ + gprs_ns_cb_t *cb; + + /* linked lists of all NSVC in this instance */ + struct llist_head gprs_nsvcs; + + /* a NSVC object that's needed to deal with packets for unknown NSVC */ + struct gprs_nsvc *unknown_nsvc; + + uint16_t timeout[NS_TIMERS_COUNT]; + + /* which link-layer are we based on? */ + enum gprs_ns_ll ll; + + union { + /* NS-over-IP specific bits */ + struct { + struct bsc_fd fd; + } nsip; + }; +}; + +enum nsvc_timer_mode { + /* standard timers */ + NSVC_TIMER_TNS_TEST, + NSVC_TIMER_TNS_ALIVE, + NSVC_TIMER_TNS_RESET, + _NSVC_TIMER_NR, +}; + +struct gprs_nsvc { + struct llist_head list; + struct gprs_ns_inst *nsi; + + uint16_t nsei; /* end-to-end significance */ + uint16_t nsvci; /* uniquely identifies NS-VC at SGSN */ + + uint32_t state; + uint32_t remote_state; + + struct 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; + + union { + struct { + struct sockaddr_in bts_addr; + } ip; + }; +}; + +/* Create a new NS protocol instance */ +struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb); + +/* Destroy a NS protocol instance */ +void gprs_ns_destroy(struct gprs_ns_inst *nsi); + +/* Listen for incoming GPRS packets */ +int nsip_listen(struct gprs_ns_inst *nsi, uint16_t udp_port); + +struct sockaddr_in; + +/* main entry point, here incoming NS frames enter */ +int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, + struct sockaddr_in *saddr); + +/* main function for higher layers (BSSGP) to send NS messages */ +int gprs_ns_sendmsg(struct gprs_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 */ +int nsip_listen(struct gprs_ns_inst *nsi, uint16_t udp_port); + +/* Establish a connection (from the BSS) to the SGSN */ +struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi, + struct sockaddr_in *dest, uint16_t nsei, + uint16_t nsvci); + +struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci); +void nsvc_delete(struct gprs_nsvc *nsvc); +struct gprs_nsvc *nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei); + +/* Add NS-specific VTY stuff */ +int gprs_ns_vty_init(struct gprs_ns_inst *nsi); + +#endif diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h new file mode 100644 index 000000000..bdc0b1c8f --- /dev/null +++ b/openbsc/include/openbsc/gprs_sgsn.h @@ -0,0 +1,114 @@ +#ifndef _GPRS_SGSN_H +#define _GPRS_SGSN_H + +#include <stdint.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 */ +/* Extended by 3GPP TS 23.060, Table 6: SGSN MM and PDP Contexts */ +struct sgsn_mm_ctx { + struct llist_head list; + + char imsi[GSM_IMSI_LENGTH]; + enum gprs_mm_state mm_state; + uint32_t p_tmsi; + uint32_t p_tmsi_sig; + char imei[GSM_IMEI_LENGTH]; + /* Opt: Software Version Numbber / TS 23.195 */ + char msisdn[GSM_EXTENSION_LENGTH]; + struct gprs_ra_id ra; + uint16_t cell_id; + uint32_t cell_id_age; + uint16_t sac; /* Iu: Service Area Code */ + uint32_t sac_age;/* Iu: Service Area Code age */ + /* VLR number */ + uint32_t new_sgsn_addr; + /* Authentication Triplets */ + /* Kc */ + /* Iu: CK, IK, KSI */ + /* CKSN */ + enum gprs_ciph_algo ciph_algo; + struct { + uint8_t buf[14]; /* 10.5.5.12a */ + uint8_t len; + } ms_radio_access_capa; + struct { + uint8_t buf[4]; /* 10.5.5.12 */ + uint8_t len; + } ms_network_capa; + uint16_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; + uint8_t radio_prio_sms; + + struct llist_head pdp_list; + + /* Additional bits not present in the GSM TS */ + uint32_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; + uint32_t addresss; + char *apn_subscribed; + char *apn_used; + uint16_t nsapi; + uint8_t ti; /* transaction identifier */ + uint32_t ggsn_in_use; + int vplmn_allowed; + uint32_t qos_profile_subscr; + uint32_t qos_profile_req; + uint32_t qos_profile_neg; + uint8_t radio_prio; + uint32_t tx_npdu_nr; + uint32_t rx_npdu_nr; + uint32_t tx_gtp_snd; + uint32_t rx_gtp_snu; + uint32_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(uint32_t tlli, + const struct gprs_ra_id *raid); +struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_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(uint32_t tlli, + const struct gprs_ra_id *raid); + +#endif /* _GPRS_SGSN_H */ diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h index daf3bd780..74dcbe52a 100644 --- a/openbsc/include/openbsc/gsm_04_08.h +++ b/openbsc/include/openbsc/gsm_04_08.h @@ -12,6 +12,15 @@ struct gsm_subscriber; struct gsm_network; struct gsm_trans; +#define GSM48_ALLOC_SIZE 1024 +#define GSM48_ALLOC_HEADROOM 128 + +static inline struct msgb *gsm48_msgb_alloc(void) +{ + return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM, + "GSM 04.08"); +} + /* config options controlling the behaviour of the lower leves */ void gsm0408_allow_everyone(int allow); @@ -22,7 +31,6 @@ enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, in int gsm48_tx_mm_info(struct gsm_lchan *lchan); int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand, int key_seq); int gsm48_tx_mm_auth_rej(struct gsm_lchan *lchan); -struct msgb *gsm48_msgb_alloc(void); int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans); int gsm48_send_rr_release(struct gsm_lchan *lchan); 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..344b2774b --- /dev/null +++ b/openbsc/include/openbsc/gsm_04_08_gprs.h @@ -0,0 +1,350 @@ +#ifndef _GSM48_GPRS_H +#define _GSM48_GPRS_H + +#include <stdint.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 9.4.15 / Table 9.4.15 */ +struct gsm48_ra_upd_ack { + uint8_t force_stby:4, /* 10.5.5.7 */ + upd_result:4; /* 10.5.5.17 */ + uint8_t ra_upd_timer; /* 10.5.7.3 */ + struct gsm48_ra_id ra_id; /* 10.5.5.15 */ + uint8_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 { + uint8_t att_result:4, /* 10.5.5.7 */ + force_stby:4; /* 10.5.5.1 */ + uint8_t ra_upd_timer; /* 10.5.7.3 */ + uint8_t radio_prio; /* 10.5.7.2 */ + struct gsm48_ra_id ra_id; /* 10.5.5.15 */ + uint8_t data[0]; +} __attribute__((packed)); + +/* Chapter 9.5.1 / Table 9.5.1 */ +struct gsm48_act_pdp_ctx_req { + uint8_t req_nsapi; + uint8_t req_llc_sapi; + uint8_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, +}; + +/* 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, +}; + +/* Table 10.5.155/3GPP TS 24.008 */ +enum gsm48_pdp_type_org { + PDP_TYPE_ORG_ETSI = 0x00, + PDP_TYPE_ORG_IETF = 0x01, +}; +enum gsm48_pdp_type_nr { + PDP_TYPE_N_ETSI_RESERVED = 0x00, + PDP_TYPE_N_ETSI_PPP = 0x01, + PDP_TYPE_N_IETF_IPv4 = 0x21, + PDP_TYPE_N_IETF_IPv6 = 0x57, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_reliab_class { + GSM48_QOS_RC_LLC_ACK_RLC_ACK_DATA_PROT = 2, + GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT = 3, + GSM48_QOS_RC_LLC_UN_RLC_UN_PROT_DATA = 4, + GSM48_QOS_RC_LLC_UN_RLC_UN_DATA_UN = 5, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_preced_class { + GSM48_QOS_PC_HIGH = 1, + GSM48_QOS_PC_NORMAL = 2, + GSM48_QOS_PC_LOW = 3, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_peak_tput { + GSM48_QOS_PEAK_TPUT_1000bps = 1, + GSM48_QOS_PEAK_TPUT_2000bps = 2, + GSM48_QOS_PEAK_TPUT_4000bps = 3, + GSM48_QOS_PEAK_TPUT_8000bps = 4, + GSM48_QOS_PEAK_TPUT_16000bps = 5, + GSM48_QOS_PEAK_TPUT_32000bps = 6, + GSM48_QOS_PEAK_TPUT_64000bps = 7, + GSM48_QOS_PEAK_TPUT_128000bps = 8, + GSM48_QOS_PEAK_TPUT_256000bps = 9, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_mean_tput { + GSM48_QOS_MEAN_TPUT_100bph = 1, + GSM48_QOS_MEAN_TPUT_200bph = 2, + GSM48_QOS_MEAN_TPUT_500bph = 3, + GSM48_QOS_MEAN_TPUT_1000bph = 4, + GSM48_QOS_MEAN_TPUT_2000bph = 5, + GSM48_QOS_MEAN_TPUT_5000bph = 6, + GSM48_QOS_MEAN_TPUT_10000bph = 7, + GSM48_QOS_MEAN_TPUT_20000bph = 8, + GSM48_QOS_MEAN_TPUT_50000bph = 9, + GSM48_QOS_MEAN_TPUT_100kbph = 10, + GSM48_QOS_MEAN_TPUT_200kbph = 11, + GSM48_QOS_MEAN_TPUT_500kbph = 0xc, + GSM48_QOS_MEAN_TPUT_1Mbph = 0xd, + GSM48_QOS_MEAN_TPUT_2Mbph = 0xe, + GSM48_QOS_MEAN_TPUT_5Mbph = 0xf, + GSM48_QOS_MEAN_TPUT_10Mbph = 0x10, + GSM48_QOS_MEAN_TPUT_20Mbph = 0x11, + GSM48_QOS_MEAN_TPUT_50Mbph = 0x12, + GSM48_QOS_MEAN_TPUT_BEST_EFFORT = 0x1f, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_err_sdu { + GSM48_QOS_ERRSDU_NODETECT = 1, + GSM48_QOS_ERRSDU_YES = 2, + GSM48_QOS_ERRSDU_NO = 3, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_deliv_order { + GSM48_QOS_DO_ORDERED = 1, + GSM48_QOS_DO_UNORDERED = 2, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_traf_class { + GSM48_QOS_TC_CONVERSATIONAL = 1, + GSM48_QOS_TC_STREAMING = 2, + GSM48_QOS_TC_INTERACTIVE = 3, + GSM48_QOS_TC_BACKGROUND = 4, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_max_sdu_size { + /* values below in 10 octet granularity */ + GSM48_QOS_MAXSDU_1502 = 0x97, + GSM48_QOS_MAXSDU_1510 = 0x98, + GSM48_QOS_MAXSDU_1520 = 0x99, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_max_bitrate { + GSM48_QOS_MBRATE_1k = 0x01, + GSM48_QOS_MBRATE_63k = 0x3f, + GSM48_QOS_MBRATE_64k = 0x40, + GSM48_QOS_MBRATE_568k = 0x7f, + GSM48_QOS_MBRATE_576k = 0x80, + GSM48_QOS_MBRATE_8640k = 0xfe, + GSM48_QOS_MBRATE_0k = 0xff, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_resid_ber { + GSM48_QOS_RBER_5e_2 = 0x01, + GSM48_QOS_RBER_1e_2 = 0x02, + GSM48_QOS_RBER_5e_3 = 0x03, + GSM48_QOS_RBER_4e_3 = 0x04, + GSM48_QOS_RBER_1e_3 = 0x05, + GSM48_QOS_RBER_1e_4 = 0x06, + GSM48_QOS_RBER_1e_5 = 0x07, + GSM48_QOS_RBER_1e_6 = 0x08, + GSM48_QOS_RBER_6e_8 = 0x09, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_sdu_err { + GSM48_QOS_SERR_1e_2 = 0x01, + GSM48_QOS_SERR_7e_2 = 0x02, + GSM48_QOS_SERR_1e_3 = 0x03, + GSM48_QOS_SERR_1e_4 = 0x04, + GSM48_QOS_SERR_1e_5 = 0x05, + GSM48_QOS_SERR_1e_6 = 0x06, + GSM48_QOS_SERR_1e_1 = 0x07, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +struct gsm48_qos { + /* octet 3 */ + uint8_t reliab_class:3; + uint8_t delay_class:3; + uint8_t spare:2; + /* octet 4 */ + uint8_t preced_class:3; + uint8_t spare2:1; + uint8_t peak_tput:4; + /* octet 5 */ + uint8_t mean_tput:5; + uint8_t spare3:3; + /* octet 6 */ + uint8_t deliv_err_sdu:3; + uint8_t deliv_order:2; + uint8_t traf_class:3; + /* octet 7 */ + uint8_t max_sdu_size; + /* octet 8 */ + uint8_t max_bitrate_up; + /* octet 9 */ + uint8_t max_bitrate_down; + /* octet 10 */ + uint8_t sdu_err_ratio:4; + uint8_t resid_ber:4; + /* octet 11 */ + uint8_t handling_prio:2; + uint8_t xfer_delay:6; + /* octet 12 */ + uint8_t guar_bitrate_up; + /* octet 13 */ + uint8_t guar_bitrate_down; + /* octet 14 */ + uint8_t src_stats_desc:4; + uint8_t sig_ind:1; + uint8_t spare5:3; + /* octet 15 */ + uint8_t max_bitrate_down_ext; + /* octet 16 */ + uint8_t guar_bitrate_down_ext; +}; + + +int gprs_tlli_type(uint32_t tlli); + +struct gsm_bts *gsm48_bts_by_ra_id(struct gsm_network *net, + const uint8_t *buf, unsigned int len); + +#endif /* _GSM48_GPRS_H */ diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index 8dfa5886b..91527c933 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -73,6 +73,37 @@ enum gsm_paging_event { GSM_PAGING_OOM, }; +enum bts_gprs_mode { + BTS_GPRS_NONE = 0, + BTS_GPRS_GPRS = 1, + BTS_GPRS_EGPRS = 2, +}; + +/* the data structure stored in msgb->cb for openbsc apps */ +struct openbsc_msgb_cb { + unsigned char *bssgph; + unsigned char *llch; + + /* Cell Identifier */ + unsigned char *bssgp_cell_id; + + /* Identifiers of a BTS, equal to 'struct bssgp_bts_ctx' */ + u_int16_t nsei; + u_int16_t bvci; + + /* Identifier of a MS (inside BTS), equal to 'struct sgsn_mm_ctx' */ + u_int32_t tlli; +} __attribute__((packed)); +#define OBSC_MSGB_CB(__msgb) ((struct openbsc_msgb_cb *)&((__msgb)->cb[0])) +#define msgb_tlli(__x) OBSC_MSGB_CB(__x)->tlli +#define msgb_nsei(__x) OBSC_MSGB_CB(__x)->nsei +#define msgb_bvci(__x) OBSC_MSGB_CB(__x)->bvci +#define msgb_gmmh(__x) (__x)->l3h +#define msgb_bssgph(__x) OBSC_MSGB_CB(__x)->bssgph +#define msgb_bssgp_len(__x) ((__x)->tail - (uint8_t *)msgb_bssgph(__x)) +#define msgb_bcid(__x) OBSC_MSGB_CB(__x)->bssgp_cell_id +#define msgb_llch(__x) OBSC_MSGB_CB(__x)->llch + struct msgb; typedef int gsm_cbfn(unsigned int hooknum, unsigned int event, @@ -99,11 +130,6 @@ typedef int gsm_cbfn(unsigned int hooknum, } while(0); -/* communications link with a BTS */ -struct gsm_bts_link { - struct gsm_bts *bts; -}; - /* Real authentication information containing Ki */ enum gsm_auth_algo { AUTH_ALGO_NONE, @@ -250,6 +276,7 @@ struct gsm_lchan { u_int16_t bound_port; u_int16_t connect_port; u_int16_t conn_id; + u_int8_t rtp_payload; u_int8_t rtp_payload2; u_int8_t speech_mode; struct rtp_socket *rtp_socket; @@ -368,7 +395,6 @@ struct gsm_paging_request { struct gsm_bts_paging_state { /* pending requests */ struct llist_head pending_requests; - struct gsm_paging_request *last_request; struct gsm_bts *bts; struct timer_list work_timer; @@ -383,11 +409,14 @@ struct gsm_envabtse { struct gsm_bts_gprs_nsvc { struct gsm_bts *bts; + /* data read via VTY config file, to configure the BTS + * via OML from BSC */ int id; u_int16_t nsvci; - u_int16_t local_port; - u_int16_t remote_port; - u_int32_t remote_ip; + u_int16_t local_port; /* on the BTS */ + u_int16_t remote_port; /* on the SGSN */ + u_int32_t remote_ip; /* on the SGSN */ + struct gsm_nm_state nm_state; }; @@ -476,18 +505,24 @@ struct gsm_bts { /* Not entirely sure how ip.access specific this is */ struct { - int enabled; + enum bts_gprs_mode mode; struct { struct gsm_nm_state nm_state; u_int16_t nsei; + uint8_t timer[7]; } nse; struct { struct gsm_nm_state nm_state; u_int16_t bvci; + uint8_t timer[11]; } cell; struct gsm_bts_gprs_nsvc nsvc[2]; u_int8_t rac; } gprs; + + /* RACH NM values */ + int rach_b_thresh; + int rach_ldavg_slots; /* transceivers */ int num_trx; @@ -535,6 +570,14 @@ struct gsmnet_stats { struct counter *alerted; /* we alerted the other end */ struct counter *connected;/* how many calls were accepted */ } call; + struct { + struct counter *rf_fail; + struct counter *rll_err; + } chan; + struct { + struct counter *oml_fail; + struct counter *rsl_fail; + } bts; }; enum gsm_auth_policy { @@ -697,15 +740,10 @@ const char *gsm_auth_policy_name(enum gsm_auth_policy policy); enum rrlp_mode rrlp_mode_parse(const char *arg); const char *rrlp_mode_name(enum rrlp_mode mode); -void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked); +enum bts_gprs_mode bts_gprs_mode_parse(const char *arg); +const char *bts_gprs_mode_name(enum bts_gprs_mode mode); -/* A parsed GPRS routing area */ -struct gprs_ra_id { - u_int16_t mnc; - u_int16_t mcc; - u_int16_t lac; - u_int8_t rac; -}; +void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked); int gsm48_ra_id_by_bts(u_int8_t *buf, struct gsm_bts *bts); void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts); diff --git a/openbsc/include/openbsc/ipaccess.h b/openbsc/include/openbsc/ipaccess.h index 86248aae5..f8ddfd467 100644 --- a/openbsc/include/openbsc/ipaccess.h +++ b/openbsc/include/openbsc/ipaccess.h @@ -53,6 +53,8 @@ int ipaccess_send_id_req(int fd); int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len); +int ipaccess_drop_oml(struct gsm_bts *bts); +int ipaccess_drop_rsl(struct gsm_bts_trx *trx); /* * Firmware specific header diff --git a/openbsc/include/openbsc/rest_octets.h b/openbsc/include/openbsc/rest_octets.h index 4e72c0f87..6d9011963 100644 --- a/openbsc/include/openbsc/rest_octets.h +++ b/openbsc/include/openbsc/rest_octets.h @@ -71,6 +71,7 @@ enum gprs_nmo { GPRS_NMO_III = 2, /* no paging coordination */ }; +/* TS 04.60 12.24 */ struct gprs_cell_options { enum gprs_nmo nmo; /* T3168: wait for packet uplink assignment message */ @@ -79,6 +80,16 @@ struct gprs_cell_options { u_int32_t t3192; /* in milliseconds */ u_int32_t drx_timer_max;/* in seconds */ u_int32_t bs_cv_max; + + u_int8_t ext_info_present; + struct { + u_int8_t egprs_supported; + u_int8_t use_egprs_p_ch_req; + u_int8_t bep_period; + u_int8_t pfc_supported; + u_int8_t dtm_supported; + u_int8_t bss_paging_coordination; + } ext_info; }; /* TS 04.60 Table 12.9.2 */ diff --git a/openbsc/include/openbsc/rtp_proxy.h b/openbsc/include/openbsc/rtp_proxy.h index f82711a8e..65b1a5fac 100644 --- a/openbsc/include/openbsc/rtp_proxy.h +++ b/openbsc/include/openbsc/rtp_proxy.h @@ -28,6 +28,12 @@ #include <osmocore/linuxlist.h> #include <osmocore/select.h> +#define RTP_PT_GSM_FULL 3 +#define RTP_PT_GSM_HALF 96 +#define RTP_PT_GSM_EFR 97 +#define RTP_PT_AMR_FULL 98 +#define RTP_PT_AMR_HALF 99 + enum rtp_rx_action { RTP_NONE, RTP_PROXY, diff --git a/openbsc/include/openbsc/sgsn.h b/openbsc/include/openbsc/sgsn.h new file mode 100644 index 000000000..2dc53c13d --- /dev/null +++ b/openbsc/include/openbsc/sgsn.h @@ -0,0 +1,30 @@ +#ifndef _SGSN_H +#define _SGSN_H + +#include <sys/types.h> + +#include <osmocore/msgb.h> + +#include <openbsc/gprs_ns.h> + +struct sgsn_config { + /* parsed from config file */ + u_int32_t nsip_listen_ip; + u_int16_t nsip_listen_port; + + /* misc */ + struct gprs_ns_inst *nsi; +}; + + +/* sgsn_vty.c */ + +int sgsn_vty_init(void); +int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg); + +/* sgsn.c */ + +/* Main input function for Gb proxy */ +int sgsn_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci); + +#endif diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h index 1b974e288..48f7946b3 100644 --- a/openbsc/include/openbsc/signal.h +++ b/openbsc/include/openbsc/signal.h @@ -26,7 +26,6 @@ #include <errno.h> #include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> #include <osmocore/signal.h> @@ -43,6 +42,7 @@ enum signal_subsystems { SS_SCALL, SS_GLOBAL, SS_CHALLOC, + SS_NS, }; /* SS_PAGING signals */ @@ -118,6 +118,8 @@ enum signal_global { S_GLOBAL_SHUTDOWN, }; +struct gsm_subscriber; + struct paging_signal_data { struct gsm_subscriber *subscr; struct gsm_bts *bts; @@ -133,7 +135,7 @@ struct scall_signal_data { }; struct ipacc_ack_signal_data { - struct gsm_bts *bts; + struct gsm_bts_trx *trx; u_int8_t msg_type; }; @@ -143,4 +145,16 @@ struct challoc_signal_data { enum gsm_chan_t type; }; +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; +}; + #endif diff --git a/openbsc/include/openbsc/vty.h b/openbsc/include/openbsc/vty.h index 40e219162..f1b1148ad 100644 --- a/openbsc/include/openbsc/vty.h +++ b/openbsc/include/openbsc/vty.h @@ -1,6 +1,10 @@ #ifndef OPENBSC_VTY_H #define OPENBSC_VTY_H +struct gsm_network; +struct vty; + void openbsc_vty_add_cmds(void); +void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *); #endif diff --git a/openbsc/include/sccp/sccp_types.h b/openbsc/include/sccp/sccp_types.h index 42fda96ae..22bd70f21 100644 --- a/openbsc/include/sccp/sccp_types.h +++ b/openbsc/include/sccp/sccp_types.h @@ -411,4 +411,10 @@ struct sccp_data_it { u_int8_t credit; } __attribute__((packed)); +struct sccp_proto_err { + u_int8_t type; + struct sccp_source_reference destination_local_reference; + u_int8_t error_cause; +}; + #endif diff --git a/openbsc/include/vty/buffer.h b/openbsc/include/vty/buffer.h index 31519400f..c9467a91d 100644 --- a/openbsc/include/vty/buffer.h +++ b/openbsc/include/vty/buffer.h @@ -28,7 +28,7 @@ /* Create a new buffer. Memory will be allocated in chunks of the given size. If the argument is 0, the library will supply a reasonable default size suitable for buffering socket I/O. */ -struct buffer *buffer_new(size_t); +struct buffer *buffer_new(void *ctx, size_t); /* Free all data in the buffer. */ void buffer_reset(struct buffer *); diff --git a/openbsc/include/vty/command.h b/openbsc/include/vty/command.h index 03b071f70..1b6e0a7b7 100644 --- a/openbsc/include/vty/command.h +++ b/openbsc/include/vty/command.h @@ -107,6 +107,9 @@ enum node_type { TS_NODE, SUBSCR_NODE, MGCP_NODE, + GBPROXY_NODE, + SGSN_NODE, + NS_NODE, }; /* Node which has some commands and prompt string and configuration @@ -173,6 +176,17 @@ struct desc { /* helper defines for end-user DEFUN* macros */ #define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ + static struct cmd_element cmdname = \ + { \ + .string = cmdstr, \ + .func = funcname, \ + .doc = helpstr, \ + .attr = attrs, \ + .daemon = dnum, \ + }; + +/* global (non static) cmd_element */ +#define gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ struct cmd_element cmdname = \ { \ .string = cmdstr, \ @@ -195,6 +209,12 @@ struct desc { DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ DEFUN_CMD_FUNC_TEXT(funcname) +/* global (non static) cmd_element */ +#define gDEFUN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ + DEFUN_CMD_FUNC_TEXT(funcname) + #define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ DEFUN_CMD_FUNC_DECL(funcname) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \ @@ -236,6 +256,10 @@ struct desc { #define ALIAS(funcname, cmdname, cmdstr, helpstr) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) +/* global (non static) cmd_element */ +#define gALIAS(funcname, cmdname, cmdstr, helpstr) \ + gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) + #define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) @@ -326,6 +350,7 @@ struct desc { void install_node(struct cmd_node *, int (*)(struct vty *)); void install_default(enum node_type); void install_element(enum node_type, struct cmd_element *); +void install_element_ve(struct cmd_element *cmd); void sort_node(); /* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am index 2c1d37a04..177c410ad 100644 --- a/openbsc/src/Makefile.am +++ b/openbsc/src/Makefile.am @@ -2,29 +2,32 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) 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 +# build current directory before building gprs +SUBDIRS = . ipaccess gprs + +sbin_PROGRAMS = bsc_hack bs11_config isdnsync bsc_mgcp +noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libsccp.a noinst_HEADERS = vty/cardshell.h bscdir = $(libdir) bsc_LIBRARIES = libsccp.a libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \ - chan_alloc.c debug.c \ + chan_alloc.c debug.c socket.c \ gsm_subscriber_base.c subchan_demux.c bsc_rll.c transaction.c \ trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c \ input/misdn.c input/ipaccess.c \ talloc_ctx.c system_information.c rest_octets.c \ rtp_proxy.c bts_siemens_bs11.c bts_ipaccess_nanobts.c \ - bts_unknown.c bsc_version.c bsc_api.c vty_interface_cmds.c + bts_unknown.c bsc_version.c bsc_api.c -libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.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 \ handover_logic.c handover_decision.c meas_rep.c -libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c +libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c \ + telnet_interface.c vty_interface_cmds.c vty/utils.c libsccp_a_SOURCES = sccp/sccp.c @@ -34,15 +37,8 @@ bsc_hack_LDADD = libmsc.a libbsc.a libmsc.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 -ipaccess_find_SOURCES = ipaccess/ipaccess-find.c - -ipaccess_config_SOURCES = ipaccess/ipaccess-config.c ipaccess/ipaccess-firmware.c -ipaccess_config_LDADD = libbsc.a libmsc.a libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT) - isdnsync_SOURCES = isdnsync.c bsc_mgcp_SOURCES = mgcp/mgcp_main.c mgcp/mgcp_protocol.c mgcp/mgcp_network.c mgcp/mgcp_vty.c \ - debug.c telnet_interface.c vty_interface_cmds.c + debug.c bsc_mgcp_LDADD = libvty.a - -ipaccess_proxy_SOURCES = ipaccess/ipaccess-proxy.c debug.c diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c index 5e6e8196c..09285bd43 100644 --- a/openbsc/src/abis_nm.c +++ b/openbsc/src/abis_nm.c @@ -678,7 +678,7 @@ static int update_admstate(struct gsm_bts *bts, u_int8_t obj_class, new_state = *nm_state; new_state.administrative = adm_state; - rc = nm_state_event(EVT_STATECHG_ADM, obj_class, obj, nm_state, &new_state); + rc = nm_state_event(EVT_STATECHG_ADM, obj_class, obj, nm_state, &new_state, obj_inst); nm_state->administrative = adm_state; @@ -732,7 +732,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb) /* Update the operational state of a given object in our in-memory data * structures and send an event to the higher layer */ void *obj = objclass2obj(bts, foh->obj_class, &foh->obj_inst); - rc = nm_state_event(EVT_STATECHG_OPER, foh->obj_class, obj, nm_state, &new_state); + rc = nm_state_event(EVT_STATECHG_OPER, foh->obj_class, obj, nm_state, &new_state, &foh->obj_inst); nm_state->operational = new_state.operational; nm_state->availability = new_state.availability; if (nm_state->administrative == 0) @@ -822,15 +822,56 @@ static int ipacc_sw_activate(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i return abis_nm_sendmsg(bts, msg); } +static int abis_nm_parse_sw_descr(const u_int8_t *sw_descr, int sw_descr_len) +{ + static const struct tlv_definition sw_descr_def = { + .def = { + [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V, }, + [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V, }, + }, + }; + + u_int8_t tag; + u_int16_t tag_len; + const u_int8_t *val; + int ofs = 0, len; + + /* Classic TLV parsing doesn't work well with SW_DESCR because of it's + * nested nature and the fact you have to assume it contains only two sub + * tags NM_ATT_FILE_VERSION & NM_ATT_FILE_ID to parse it */ + + if (sw_descr[0] != NM_ATT_SW_DESCR) { + DEBUGP(DNM, "SW_DESCR attribute identifier not found!\n"); + return -1; + } + ofs += 1; + + len = tlv_parse_one(&tag, &tag_len, &val, + &sw_descr_def, &sw_descr[ofs], sw_descr_len-ofs); + if (len < 0 || (tag != NM_ATT_FILE_ID)) { + DEBUGP(DNM, "FILE_ID attribute identifier not found!\n"); + return -2; + } + ofs += len; + + len = tlv_parse_one(&tag, &tag_len, &val, + &sw_descr_def, &sw_descr[ofs], sw_descr_len-ofs); + if (len < 0 || (tag != NM_ATT_FILE_VERSION)) { + DEBUGP(DNM, "FILE_VERSION attribute identifier not found!\n"); + return -3; + } + ofs += len; + + return ofs; +} + static int abis_nm_rx_sw_act_req(struct msgb *mb) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct tlv_parsed tp; const u_int8_t *sw_config; - int sw_config_len; - int file_id_len; - int ret; + int ret, sw_config_len, sw_descr_len; debugp_foh(foh); @@ -854,20 +895,16 @@ static int abis_nm_rx_sw_act_req(struct msgb *mb) DEBUGP(DNM, "Found SW config: %s\n", hexdump(sw_config, sw_config_len)); } - if (sw_config[0] != NM_ATT_SW_DESCR) - DEBUGP(DNM, "SW_DESCR attribute identifier not found!\n"); - if (sw_config[1] != NM_ATT_FILE_ID) - DEBUGP(DNM, "FILE_ID attribute identifier not found!\n"); - file_id_len = sw_config[2] * 256 + sw_config[3]; + /* Use the first SW_DESCR present in SW config */ + sw_descr_len = abis_nm_parse_sw_descr(sw_config, sw_config_len); + if (sw_descr_len < 0) + return -EINVAL; - /* Assumes first SW file in list is the one to be activated */ - /* sw_config + 4 to skip over 2 attribute ID bytes and 16-bit length field */ return ipacc_sw_activate(mb->trx->bts, foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr, - sw_config + 4, - file_id_len); + sw_config, sw_descr_len); } /* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */ @@ -1102,6 +1139,7 @@ enum sw_state { struct abis_nm_sw { struct gsm_bts *bts; + int trx_nr; gsm_cbfn *cbfn; void *cb_data; int forced; @@ -1555,7 +1593,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb) } /* Load the specified software into the BTS */ -int abis_nm_software_load(struct gsm_bts *bts, const char *fname, +int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname, u_int8_t win_size, int forced, gsm_cbfn *cbfn, void *cb_data) { @@ -1569,6 +1607,7 @@ int abis_nm_software_load(struct gsm_bts *bts, const char *fname, return -EBUSY; sw->bts = bts; + sw->trx_nr = trx_nr; switch (bts->type) { case GSM_BTS_TYPE_BS11: @@ -1579,8 +1618,8 @@ int abis_nm_software_load(struct gsm_bts *bts, const char *fname, break; case GSM_BTS_TYPE_NANOBTS: sw->obj_class = NM_OC_BASEB_TRANSC; - sw->obj_instance[0] = 0x00; - sw->obj_instance[1] = 0x00; + sw->obj_instance[0] = sw->bts->nr; + sw->obj_instance[1] = sw->trx_nr; sw->obj_instance[2] = 0xff; break; case GSM_BTS_TYPE_UNKNOWN: @@ -2514,7 +2553,7 @@ static int bs11_swload_cbfn(unsigned int hook, unsigned int event, fle = fl_dequeue(&bs11_sw->file_list); if (fle) { /* start download the next file of our file list */ - rc = abis_nm_software_load(bs11_sw->bts, fle->fname, + rc = abis_nm_software_load(bs11_sw->bts, 0xff, fle->fname, bs11_sw->win_size, bs11_sw->forced, &bs11_swload_cbfn, bs11_sw); @@ -2570,7 +2609,7 @@ int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, return -EINVAL; /* start download the next file of our file list */ - rc = abis_nm_software_load(bts, fle->fname, win_size, forced, + rc = abis_nm_software_load(bts, 0xff, fle->fname, win_size, forced, bs11_swload_cbfn, bs11_sw); talloc_free(fle); return rc; @@ -2738,12 +2777,12 @@ static int abis_nm_rx_ipacc(struct msgb *msg) case NM_MT_IPACC_RSL_CONNECT_NACK: case NM_MT_IPACC_SET_NVATTR_NACK: case NM_MT_IPACC_GET_NVATTR_NACK: - signal.bts = msg->trx->bts; + signal.trx = gsm_bts_trx_by_nr(msg->trx->bts, foh->obj_inst.trx_nr); signal.msg_type = foh->msg_type; dispatch_signal(SS_NM, S_NM_IPACC_NACK, &signal); break; case NM_MT_IPACC_SET_NVATTR_ACK: - signal.bts = msg->trx->bts; + signal.trx = gsm_bts_trx_by_nr(msg->trx->bts, foh->obj_inst.trx_nr); signal.msg_type = foh->msg_type; dispatch_signal(SS_NM, S_NM_IPACC_ACK, &signal); break; @@ -2829,9 +2868,16 @@ int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, } /* restart / reboot an ip.access nanoBTS */ -int abis_nm_ipaccess_restart(struct gsm_bts *bts) +int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx) { - return __simple_cmd(bts, NM_MT_IPACC_RESTART); + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC, + trx->bts->nr, trx->nr, 0xff); + + return abis_nm_sendmsg(trx->bts, msg); } int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class, diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c index 0e572ccce..53b29823e 100644 --- a/openbsc/src/abis_rsl.c +++ b/openbsc/src/abis_rsl.c @@ -727,7 +727,6 @@ int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id) link_id, 0); msgb_tv_put(msg, RSL_IE_RELEASE_MODE, 0); /* normal release */ - lchan->state = LCHAN_S_REL_REQ; /* FIXME: start some timer in case we don't receive a REL ACK ? */ msg->trx = lchan->ts->trx; @@ -735,6 +734,12 @@ int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id) return abis_rsl_sendmsg(msg); } +int rsl_lchan_set_state(struct gsm_lchan *lchan, int state) +{ + lchan->state = state; + return 0; +} + /* Chapter 8.4.2: Channel Activate Acknowledge */ static int rsl_rx_chan_act_ack(struct msgb *msg) { @@ -749,7 +754,7 @@ static int rsl_rx_chan_act_ack(struct msgb *msg) LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n", gsm_lchan_name(msg->lchan), gsm_lchans_name(msg->lchan->state)); - msg->lchan->state = LCHAN_S_ACTIVE; + rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE); dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_ACK, msg->lchan); @@ -775,9 +780,9 @@ static int rsl_rx_chan_act_nack(struct msgb *msg) print_rsl_cause(LOGL_ERROR, cause, TLVP_LEN(&tp, RSL_IE_CAUSE)); if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC) - msg->lchan->state = LCHAN_S_NONE; + rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE); } else - msg->lchan->state = LCHAN_S_NONE; + rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE); LOGPC(DRSL, LOGL_ERROR, "\n"); @@ -805,6 +810,7 @@ static int rsl_rx_conn_fail(struct msgb *msg) LOGPC(DRSL, LOGL_NOTICE, "\n"); /* FIXME: only free it after channel release ACK */ + counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rf_fail); return rsl_rf_chan_release(msg->lchan); } @@ -981,7 +987,7 @@ static int abis_rsl_rx_dchan(struct msgb *msg) LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n", gsm_lchan_name(msg->lchan), gsm_lchans_name(msg->lchan->state)); - msg->lchan->state = LCHAN_S_NONE; + rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE); lchan_free(msg->lchan); break; case RSL_MT_MODE_MODIFY_ACK: @@ -1124,7 +1130,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg) LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel " "in state %s\n", gsm_lchan_name(lchan), gsm_lchans_name(lchan->state)); - lchan->state = LCHAN_S_ACT_REQ; + rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ); ts_number = lchan->ts->nr; arfcn = lchan->ts->trx->arfcn; @@ -1181,6 +1187,10 @@ static int rsl_rx_ccch_load(struct msgb *msg) switch (rslh->data[0]) { case RSL_IE_PAGING_LOAD: pg_buf_space = rslh->data[1] << 8 | rslh->data[2]; + if (is_ipaccess_bts(msg->trx->bts) && pg_buf_space == 0xffff) { + /* paging load below configured threshold, use 50 as default */ + pg_buf_space = 50; + } paging_update_buffer_space(msg->trx->bts, pg_buf_space); break; case RSL_IE_RACH_LOAD: @@ -1240,8 +1250,10 @@ static int rsl_rx_rll_err_ind(struct msgb *msg) rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND); - if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED) + if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED) { + counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rll_err); return rsl_rf_chan_release(msg->lchan); + } return 0; } @@ -1363,6 +1375,44 @@ static u_int8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan) return 0; } +static u_int8_t ipa_rtp_pt_for_lchan(struct gsm_lchan *lchan) +{ + switch (lchan->tch_mode) { + case GSM48_CMODE_SPEECH_V1: + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return RTP_PT_GSM_FULL; + case GSM_LCHAN_TCH_H: + return RTP_PT_GSM_HALF; + default: + break; + } + case GSM48_CMODE_SPEECH_EFR: + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return RTP_PT_GSM_EFR; + /* there's no half-rate EFR */ + default: + break; + } + case GSM48_CMODE_SPEECH_AMR: + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return RTP_PT_AMR_FULL; + case GSM_LCHAN_TCH_H: + return RTP_PT_AMR_HALF; + default: + break; + } + default: + break; + } + LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access rtp payload type for " + "tch_mode == 0x%02x\n & lchan_type == %d", + lchan->tch_mode, lchan->type); + return 0; +} + /* ip.access specific RSL extensions */ static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv) { @@ -1429,10 +1479,13 @@ int rsl_ipacc_crcx(struct gsm_lchan *lchan) /* 0x1- == receive-only, 0x-1 == EFR codec */ lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan); + lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); + msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); - DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x\n", - gsm_lchan_name(lchan), lchan->abis_ip.speech_mode); + DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x RTP_PAYLOAD=%d\n", + gsm_lchan_name(lchan), lchan->abis_ip.speech_mode, + lchan->abis_ip.rtp_payload); msg->trx = lchan->ts->trx; @@ -1459,11 +1512,13 @@ int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port, /* 0x0- == both directions, 0x-1 == EFR codec */ lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan); + lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); ia.s_addr = htonl(ip); - DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD2=%d CONN_ID=%d " - "speech_mode=0x%02x\n", gsm_lchan_name(lchan), inet_ntoa(ia), port, - rtp_payload2, lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode); + DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD=%d RTP_PAYLOAD2=%d " + "CONN_ID=%d speech_mode=0x%02x\n", gsm_lchan_name(lchan), + inet_ntoa(ia), port, lchan->abis_ip.rtp_payload, rtp_payload2, + lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode); msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id); msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP); @@ -1471,6 +1526,7 @@ int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port, *att_ip = ia.s_addr; msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, port); msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); + msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); if (rtp_payload2) msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2); @@ -1652,9 +1708,21 @@ static int abis_rsl_rx_ipacc(struct msgb *msg) /* Entry-point where L2 RSL from BTS enters */ int abis_rsl_rcvmsg(struct msgb *msg) { - struct abis_rsl_common_hdr *rslh = msgb_l2(msg) ; + struct abis_rsl_common_hdr *rslh; int rc = 0; + if (!msg) { + DEBUGP(DRSL, "Empty RSL msg?..\n"); + return -1; + } + + if (msgb_l2len(msg) < sizeof(*rslh)) { + DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg)); + return -1; + } + + rslh = msgb_l2(msg); + switch (rslh->msg_discr & 0xfe) { case ABIS_RSL_MDISC_RLL: rc = abis_rsl_rx_rll(msg); diff --git a/openbsc/src/bs11_config.c b/openbsc/src/bs11_config.c index a7493b422..8f6de8a77 100644 --- a/openbsc/src/bs11_config.c +++ b/openbsc/src/bs11_config.c @@ -481,7 +481,7 @@ static int handle_state_resp(enum abis_bs11_phase state) * argument, so our swload_cbfn can distinguish * a safety load from a regular software */ if (file_is_readable(fname_safety)) - rc = abis_nm_software_load(g_bts, fname_safety, + rc = abis_nm_software_load(g_bts, 0xff, fname_safety, win_size, param_forced, swload_cbfn, g_bts); else @@ -697,7 +697,8 @@ int handle_serial_msg(struct msgb *rx_msg) } int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, - struct gsm_nm_state *old_state, struct gsm_nm_state *new_state) + struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, + struct abis_om_obj_inst *obj_ins) { return 0; } diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c index f3436621f..77446a2c1 100644 --- a/openbsc/src/bsc_init.c +++ b/openbsc/src/bsc_init.c @@ -31,6 +31,7 @@ #include <openbsc/system_information.h> #include <openbsc/paging.h> #include <openbsc/signal.h> +#include <openbsc/chan_alloc.h> #include <osmocore/talloc.h> /* global pointer to the gsm network data structure */ @@ -377,11 +378,11 @@ static unsigned char nanobts_attr_cell[] = { 4, /* N3103 */ 8, /* N3105 */ 15, /* RLC CV countdown */ - NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x0f, 0x00, + NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x0f, 0x00, /* CS1..CS4 */ NM_ATT_IPACC_RLC_CFG_2, 0, 5, - 0x00, 250, - 0x00, 250, - 2, /* MCS2 */ + 0x00, 250, /* T downlink TBF extension (0..500) */ + 0x00, 250, /* T uplink TBF extension (0..500) */ + 2, /* CS2 */ #if 0 /* EDGE model only, breaks older models. * Should inquire the BTS capabilities */ @@ -400,7 +401,8 @@ static unsigned char nanobts_attr_nsvc0[] = { /* Callback function to be called whenever we get a GSM 12.21 state change event */ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, - struct gsm_nm_state *old_state, struct gsm_nm_state *new_state) + struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, + struct abis_om_obj_inst *obj_inst) { struct gsm_bts *bts; struct gsm_bts_trx *trx; @@ -461,7 +463,7 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, break; case NM_OC_GPRS_NSE: bts = container_of(obj, struct gsm_bts, gprs.nse); - if (!bts->gprs.enabled) + if (bts->gprs.mode == BTS_GPRS_NONE) break; if (new_state->availability == 5) { abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, @@ -475,7 +477,7 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, break; case NM_OC_GPRS_CELL: bts = container_of(obj, struct gsm_bts, gprs.cell); - if (!bts->gprs.enabled) + if (bts->gprs.mode == BTS_GPRS_NONE) break; if (new_state->availability == 5) { abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, @@ -490,9 +492,9 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, case NM_OC_GPRS_NSVC: nsvc = obj; bts = nsvc->bts; - if (!bts->gprs.enabled) + if (bts->gprs.mode == BTS_GPRS_NONE) break; - /* We skip NSVC1 since we only use NSVC0 */ + /* We skip NSVC1 since we only use NSVC0 */ if (nsvc->id == 1) break; if (new_state->availability == NM_AVSTATE_OFF_LINE) { @@ -798,7 +800,7 @@ static int set_system_infos(struct gsm_bts_trx *trx) DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc)); rsl_bcch_info(trx, i, si_tmp, sizeof(si_tmp)); } - if (bts->gprs.enabled) { + if (bts->gprs.mode != BTS_GPRS_NONE) { i = 13; rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_13); if (rc < 0) @@ -852,6 +854,22 @@ static void patch_nm_tables(struct gsm_bts *bts) bs11_attr_radio[2] |= arfcn_high; bs11_attr_radio[3] = arfcn_low; + /* patch the RACH attributes */ + if (bts->rach_b_thresh != -1) { + nanobts_attr_bts[33] = bts->rach_b_thresh & 0xff; + bs11_attr_bts[33] = bts->rach_b_thresh & 0xff; + } + + if (bts->rach_ldavg_slots != -1) { + u_int8_t avg_high = bts->rach_ldavg_slots & 0xff; + u_int8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f; + + nanobts_attr_bts[35] = avg_high; + nanobts_attr_bts[36] = avg_low; + bs11_attr_bts[35] = avg_high; + bs11_attr_bts[36] = avg_low; + } + /* patch BSIC */ bs11_attr_bts[1] = bts->bsic; nanobts_attr_bts[sizeof(nanobts_attr_bts)-11] = bts->bsic; @@ -866,6 +884,10 @@ static void patch_nm_tables(struct gsm_bts *bts) /* patch NSEI */ nanobts_attr_nse[3] = bts->gprs.nse.nsei >> 8; nanobts_attr_nse[4] = bts->gprs.nse.nsei & 0xff; + memcpy(nanobts_attr_nse+8, bts->gprs.nse.timer, + ARRAY_SIZE(bts->gprs.nse.timer)); + memcpy(nanobts_attr_nse+18, bts->gprs.cell.timer, + ARRAY_SIZE(bts->gprs.cell.timer)); /* patch NSVCI */ nanobts_attr_nsvc0[3] = bts->gprs.nsvc[0].nsvci >> 8; @@ -885,6 +907,11 @@ static void patch_nm_tables(struct gsm_bts *bts) /* patch RAC */ nanobts_attr_cell[3] = bts->gprs.rac; + if (bts->gprs.mode == BTS_GPRS_EGPRS) { + /* patch EGPRS coding schemes MCS 1..9 */ + nanobts_attr_cell[29] = 0x8f; + nanobts_attr_cell[30] = 0xff; + } } static void bootstrap_rsl(struct gsm_bts_trx *trx) @@ -899,6 +926,8 @@ static void bootstrap_rsl(struct gsm_bts_trx *trx) void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx) { + int ts_no, lchan_no; + switch (event) { case EVT_E1_TEI_UP: switch (type) { @@ -913,8 +942,35 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx) } break; case EVT_E1_TEI_DN: - LOGP(DMI, LOGL_NOTICE, "Lost some E1 TEI link\n"); - /* FIXME: deal with TEI or L1 link loss */ + LOGP(DMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", type, trx); + + if (type == E1INP_SIGN_OML) + counter_inc(trx->bts->network->stats.bts.oml_fail); + else if (type == E1INP_SIGN_RSL) + counter_inc(trx->bts->network->stats.bts.rsl_fail); + + /* + * free all allocated channels. change the nm_state so the + * trx and trx_ts becomes unusable and chan_alloc.c can not + * allocate from it. + */ + for (ts_no = 0; ts_no < ARRAY_SIZE(trx->ts); ++ts_no) { + struct gsm_bts_trx_ts *ts = &trx->ts[ts_no]; + + for (lchan_no = 0; lchan_no < ARRAY_SIZE(ts->lchan); ++lchan_no) { + if (ts->lchan[lchan_no].state != GSM_LCHAN_NONE) + lchan_free(&ts->lchan[lchan_no]); + lchan_reset(&ts->lchan[lchan_no]); + } + + ts->nm_state.operational = 0; + ts->nm_state.availability = 0; + } + + trx->nm_state.operational = 0; + trx->nm_state.availability = 0; + trx->bb_transc.nm_state.operational = 0; + trx->bb_transc.nm_state.availability = 0; break; default: break; @@ -923,6 +979,8 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx) static int bootstrap_bts(struct gsm_bts *bts) { + int i, n; + switch (bts->band) { case GSM_BAND_1800: if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) { @@ -959,10 +1017,34 @@ static int bootstrap_bts(struct gsm_bts *bts) /* Control Channel Description */ bts->si_common.chan_desc.att = 1; - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C; bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* T3212 is set from vty/config */ + /* Set ccch config by looking at ts config */ + for (n=0, i=0; i<8; i++) + n += bts->c0->ts[i].pchan == GSM_PCHAN_CCCH ? 1 : 0; + + switch (n) { + case 0: + bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C; + break; + case 1: + bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_NC; + break; + case 2: + bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_2_NC; + break; + case 3: + bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_3_NC; + break; + case 4: + bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_4_NC; + break; + default: + LOGP(DNM, LOGL_ERROR, "Unsupported CCCH timeslot configuration\n"); + return -EINVAL; + } + /* some defaults for our system information */ bts->si_common.cell_options.radio_link_timeout = 2; /* 12 */ bts->si_common.cell_options.dtx = 2; /* MS shall not use upplink DTX */ diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c index cd48e4359..107abdc92 100644 --- a/openbsc/src/chan_alloc.c +++ b/openbsc/src/chan_alloc.c @@ -330,6 +330,20 @@ void lchan_free(struct gsm_lchan *lchan) * channel using it */ } +/* + * There was an error with the TRX and we need to forget + * any state so that a lchan can be allocated again after + * the trx is fully usable. + */ +void lchan_reset(struct gsm_lchan *lchan) +{ + bsc_del_timer(&lchan->T3101); + + lchan->type = GSM_LCHAN_NONE; + lchan->state = LCHAN_S_NONE; +} + + /* Consider releasing the channel now */ int lchan_auto_release(struct gsm_lchan *lchan) { @@ -348,6 +362,7 @@ int lchan_auto_release(struct gsm_lchan *lchan) lchan->conn.use_count); DEBUGP(DRLL, "%s Recycling Channel\n", gsm_lchan_name(lchan)); + rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ); rsl_release_request(lchan, 0); return 1; } diff --git a/openbsc/src/db.c b/openbsc/src/db.c index 8bf47ab38..57a7863c9 100644 --- a/openbsc/src/db.c +++ b/openbsc/src/db.c @@ -20,13 +20,8 @@ * */ -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_04_11.h> -#include <openbsc/db.h> -#include <osmocore/talloc.h> -#include <openbsc/debug.h> -#include <osmocore/statistics.h> - +#include <stdint.h> +#include <inttypes.h> #include <libgen.h> #include <stdio.h> #include <stdlib.h> @@ -34,6 +29,14 @@ #include <errno.h> #include <dbi/dbi.h> +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_04_11.h> +#include <openbsc/db.h> +#include <osmocore/talloc.h> +#include <openbsc/debug.h> +#include <osmocore/statistics.h> +#include <osmocore/rate_ctr.h> + static char *db_basename = NULL; static char *db_dirname = NULL; static dbi_conn conn; @@ -124,6 +127,13 @@ static char *create_stmts[] = { "value INTEGER NOT NULL, " "name TEXT NOT NULL " ")", + "CREATE TABLE IF NOT EXISTS RateCounters (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "timestamp TIMESTAMP NOT NULL, " + "value INTEGER NOT NULL, " + "name TEXT NOT NULL, " + "index INTEGER NOT NULL " + ")", "CREATE TABLE IF NOT EXISTS AuthKeys (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "subscriber_id INTEGER UNIQUE NOT NULL, " @@ -1180,3 +1190,42 @@ int db_store_counter(struct counter *ctr) dbi_result_free(result); return 0; } + +static int db_store_rate_ctr(struct rate_ctr_group *ctrg, unsigned int num, + char *q_prefix) +{ + dbi_result result; + char *q_name; + + dbi_conn_quote_string_copy(conn, ctrg->desc->ctr_desc[num].name, + &q_name); + + result = dbi_conn_queryf(conn, + "Insert INTO RateCounters " + "(timestamp,name,index,value) VALUES " + "(datetime('now'),%s.%s,%u,%"PRIu64")", + q_prefix, q_name, ctrg->idx, ctrg->ctr[num].current); + + free(q_name); + + if (!result) + return -EIO; + + dbi_result_free(result); + return 0; +} + +int db_store_rate_ctr_group(struct rate_ctr_group *ctrg) +{ + unsigned int i; + char *q_prefix; + + dbi_conn_quote_string_copy(conn, ctrg->desc->group_name_prefix, &q_prefix); + + for (i = 0; i < ctrg->desc->num_ctr; i++) + db_store_rate_ctr(ctrg, i, q_prefix); + + free(q_prefix); + + return 0; +} diff --git a/openbsc/src/debug.c b/openbsc/src/debug.c index a55d79013..9218f6471 100644 --- a/openbsc/src/debug.c +++ b/openbsc/src/debug.c @@ -39,81 +39,81 @@ static const struct log_info_cat default_categories[] = { [DRLL] = { .name = "DRLL", - .description = "Radio Link Layer", + .description = "A-bis Radio Link Layer (RLL)", .color = "\033[1;31m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DCC] = { .name = "DCC", - .description = "Call Control", + .description = "Layer3 Call Control (CC)", .color = "\033[1;32m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMM] = { .name = "DMM", - .description = "Mobility Management", + .description = "Layer3 Mobility Management (MM)", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DRR] = { .name = "DRR", - .description = "Radio Resource", + .description = "Layer3 Radio Resource (RR)", .color = "\033[1;34m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DRSL] = { .name = "DRSL", - .description = "Radio Siganlling Link", + .description = "A-bis Radio Siganlling Link (RSL)", .color = "\033[1;35m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DNM] = { .name = "DNM", - .description = "Network Management (OML)", + .description = "A-bis Network Management / O&M (NM/OML)", .color = "\033[1;36m", .enabled = 1, .loglevel = LOGL_INFO, }, [DMNCC] = { .name = "DMNCC", - .description = "BSC<->MSC interface", + .description = "MNCC API for Call Control application", .color = "\033[1;39m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSMS] = { .name = "DSMS", - .description = "Short Message Service", + .description = "Layer3 Short Message Service (SMS)", .color = "\033[1;37m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DPAG] = { .name = "DPAG", - .description = "Paging", + .description = "Paging Subsystem", .color = "\033[1;38m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMEAS] = { .name = "DMEAS", - .description = "Measurement Processing", + .description = "Radio Measurement Processing", .enabled = 0, .loglevel = LOGL_NOTICE, }, [DMI] = { .name = "DMI", - .description = "mISDN Input Driver", + .description = "A-bis Input Driver for Signalling", .enabled = 0, .loglevel = LOGL_NOTICE, }, [DMIB] = { .name = "DMIB", - .description = "mISDN B-Channels", + .description = "A-bis Input Driver for B-Channels (voice)", .enabled = 0, .loglevel = LOGL_NOTICE, }, [DMUX] = { .name = "DMUX", - .description = "TRAU Frame Multiplex", + .description = "A-bis B-Subchannel TRAU Frame Multiplex", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DINP] = { .name = "DINP", - .description = "Input Driver", + .description = "A-bis Intput Subsystem", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSCCP] = { @@ -138,7 +138,7 @@ static const struct log_info_cat default_categories[] = { }, [DDB] = { .name = "DDB", - .description = "Database", + .description = "Database Layer", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DREF] = { @@ -146,6 +146,21 @@ static const struct log_info_cat default_categories[] = { .description = "Reference Counting", .enabled = 0, .loglevel = LOGL_NOTICE, }, + [DGPRS] = { + .name = "DGPRS", + .description = "GPRS Packet Service", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DNS] = { + .name = "DNS", + .description = "GPRS Network Service (NS)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DBSSGP] = { + .name = "DBSSGP", + .description = "GPRS BSS Gateway Protocol (BSSGP)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, }; enum log_ctxt { diff --git a/openbsc/src/e1_input.c b/openbsc/src/e1_input.c index fba59a784..b1dfe9b1d 100644 --- a/openbsc/src/e1_input.c +++ b/openbsc/src/e1_input.c @@ -420,7 +420,17 @@ e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type, void e1inp_sign_link_destroy(struct e1inp_sign_link *link) { + struct msgb *msg; + llist_del(&link->list); + while (!llist_empty(&link->tx_list)) { + msg = msgb_dequeue(&link->tx_list); + msgb_free(msg); + } + + if (link->ts->type == E1INP_TS_TYPE_SIGN) + bsc_del_timer(&link->ts->sign.tx_timer); + talloc_free(link); } diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am new file mode 100644 index 000000000..88c358a1b --- /dev/null +++ b/openbsc/src/gprs/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) + +sbin_PROGRAMS = osmo-gbproxy osmo-sgsn +noinst_LIBRARIES = libsgsn.a + +libsgsn_a_SOURCES = gprs_ns.c gprs_bssgp.c gprs_llc.c gsm_04_08_gprs.c \ + crc24.c gprs_sgsn.c gprs_bssgp_util.c + +osmo_gbproxy_SOURCES = gb_proxy.c gb_proxy_main.c gb_proxy_vty.c \ + gprs_ns.c gprs_ns_vty.c gprs_bssgp_util.c \ + $(top_srcdir)/src/socket.c $(top_srcdir)/src/debug.c +osmo_gbproxy_LDADD = $(top_builddir)/src/libvty.a + +osmo_sgsn_SOURCES = sgsn_main.c sgsn_vty.c gprs_ns_vty.c \ + $(top_srcdir)/src/socket.c $(top_srcdir)/src/debug.c +osmo_sgsn_LDADD = $(top_builddir)/src/libvty.a libsgsn.a diff --git a/openbsc/src/gprs/crc24.c b/openbsc/src/gprs/crc24.c new file mode 100644 index 000000000..108212083 --- /dev/null +++ b/openbsc/src/gprs/crc24.c @@ -0,0 +1,69 @@ +/* GPRS LLC CRC-24 Implementation */ + +/* (C) 2008-2009 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 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 <sys/types.h> +#include <openbsc/crc24.h> + +/* 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/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c new file mode 100644 index 000000000..5fbf9bfcc --- /dev/null +++ b/openbsc/src/gprs/gb_proxy.c @@ -0,0 +1,580 @@ +/* NS-over-IP proxy */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by On Waves + * 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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <arpa/inet.h> + +#include <osmocore/talloc.h> +#include <osmocore/select.h> + +#include <openbsc/signal.h> +#include <openbsc/debug.h> +#include <openbsc/gprs_ns.h> +#include <openbsc/gprs_bssgp.h> +#include <openbsc/gb_proxy.h> + +struct gbprox_peer { + struct llist_head list; + + /* NS-VC over which we send/receive data to this BVC */ + struct gprs_nsvc *nsvc; + + /* BVCI used for Point-to-Point to this peer */ + uint16_t bvci; + + /* Routeing Area that this peer is part of (raw 04.08 encoding) */ + uint8_t ra[6]; +}; + +/* Linked list of all Gb peers (except SGSN) */ +static LLIST_HEAD(gbprox_bts_peers); + +/* Find the gbprox_peer by its BVCI */ +static struct gbprox_peer *peer_by_bvci(uint16_t bvci) +{ + struct gbprox_peer *peer; + llist_for_each_entry(peer, &gbprox_bts_peers, list) { + if (peer->bvci == bvci) + return peer; + } + return NULL; +} + +static struct gbprox_peer *peer_by_nsvc(struct gprs_nsvc *nsvc) +{ + struct gbprox_peer *peer; + llist_for_each_entry(peer, &gbprox_bts_peers, list) { + if (peer->nsvc == nsvc) + return peer; + } + return NULL; +} + +/* look-up a peer by its Routeing Area Code (RAC) */ +static struct gbprox_peer *peer_by_rac(const uint8_t *ra) +{ + struct gbprox_peer *peer; + llist_for_each_entry(peer, &gbprox_bts_peers, list) { + if (!memcmp(peer->ra, ra, 6)) + return peer; + } + return NULL; +} + +/* look-up a peer by its Location Area Code (LAC) */ +static struct gbprox_peer *peer_by_lac(const uint8_t *la) +{ + struct gbprox_peer *peer; + llist_for_each_entry(peer, &gbprox_bts_peers, list) { + if (!memcmp(peer->ra, la, 5)) + return peer; + } + return NULL; +} + +static struct gbprox_peer *peer_alloc(uint16_t bvci) +{ + struct gbprox_peer *peer; + + peer = talloc_zero(tall_bsc_ctx, struct gbprox_peer); + if (!peer) + return NULL; + + peer->bvci = bvci; + llist_add(&peer->list, &gbprox_bts_peers); + + return peer; +} + +static void peer_free(struct gbprox_peer *peer) +{ + llist_del(&peer->list); + talloc_free(peer); +} + +/* FIXME: this needs to go to libosmocore/msgb.c */ +static struct msgb *msgb_copy(const struct msgb *msg, const char *name) +{ + struct msgb *new_msg; + + new_msg = msgb_alloc(msg->data_len, name); + if (!new_msg) + return NULL; + + /* copy header */ + memcpy(new_msg, msg, sizeof(*new_msg)); + /* copy data */ + memcpy(new_msg->data, msg->data, new_msg->data_len); + + return new_msg; +} + +/* strip off the NS header */ +static void strip_ns_hdr(struct msgb *msg) +{ + int strip_len = msgb_bssgph(msg) - msg->data; + msgb_pull(msg, strip_len); +} + +/* feed a message down the NS-VC associated with the specified peer */ +static int gbprox_relay2sgsn(struct msgb *old_msg, uint16_t ns_bvci) +{ + /* create a copy of the message so the old one can + * be free()d safely when we return from gbprox_rcvmsg() */ + struct msgb *msg = msgb_copy(old_msg, "msgb_relay2sgsn"); + + DEBUGP(DGPRS, "NSEI=%u proxying BTS->SGSN (NS_BVCI=%u, NSEI=%u)\n", + msgb_nsei(msg), ns_bvci, gbcfg.nsip_sgsn_nsei); + + msgb_bvci(msg) = ns_bvci; + msgb_nsei(msg) = gbcfg.nsip_sgsn_nsei; + + strip_ns_hdr(msg); + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} + +/* feed a message down the NS-VC associated with the specified peer */ +static int gbprox_relay2peer(struct msgb *old_msg, struct gbprox_peer *peer, + uint16_t ns_bvci) +{ + /* create a copy of the message so the old one can + * be free()d safely when we return from gbprox_rcvmsg() */ + struct msgb *msg = msgb_copy(old_msg, "msgb_relay2peer"); + + DEBUGP(DGPRS, "NSEI=%u proxying SGSN->BSS (NS_BVCI=%u, NSEI=%u)\n", + msgb_nsei(msg), ns_bvci, peer->nsvc->nsei); + + msgb_bvci(msg) = ns_bvci; + msgb_nsei(msg) = peer->nsvc->nsei; + + /* Strip the old NS header, it will be replaced with a new one */ + strip_ns_hdr(msg); + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} + +/* Send a message to a peer identified by ptp_bvci but using ns_bvci + * in the NS hdr */ +static int gbprox_relay2bvci(struct msgb *msg, uint16_t ptp_bvci, + uint16_t ns_bvci) +{ + struct gbprox_peer *peer; + + peer = peer_by_bvci(ptp_bvci); + if (!peer) { + LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n", + ptp_bvci); + return -ENOENT; + } + + return gbprox_relay2peer(msg, peer, ns_bvci); +} + +/* Receive an incoming signalling message from a BSS-side NS-VC */ +static int gbprox_rx_sig_from_bss(struct msgb *msg, struct gprs_nsvc *nsvc, + uint16_t ns_bvci) +{ + struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); + struct tlv_parsed tp; + uint8_t pdu_type = bgph->pdu_type; + int data_len = msgb_bssgp_len(msg) - sizeof(*bgph); + struct gbprox_peer *from_peer; + struct gprs_ra_id raid; + + if (ns_bvci != 0) { + LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u BVCI=%u is not signalling\n", + nsvc->nsei, ns_bvci); + return -EINVAL; + } + + /* we actually should never see those two for BVCI == 0, but double-check + * just to make sure */ + if (pdu_type == BSSGP_PDUT_UL_UNITDATA || + pdu_type == BSSGP_PDUT_DL_UNITDATA) { + LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u UNITDATA not allowed in " + "signalling\n", nsvc->nsei); + return -EINVAL; + } + + bssgp_tlv_parse(&tp, bgph->data, data_len); + + switch (pdu_type) { + case BSSGP_PDUT_SUSPEND: + case BSSGP_PDUT_RESUME: + /* We implement RAC snooping during SUSPEND/RESUME, since + * it establishes a relationsip between BVCI/peer and the + * routeing area code. The snooped information is then + * used for routing the {SUSPEND,RESUME}_[N]ACK back to + * the correct BSSGP */ + if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) + goto err_mand_ie; + from_peer = peer_by_nsvc(nsvc); + if (!from_peer) + goto err_no_peer; + memcpy(from_peer->ra, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA), + sizeof(from_peer->ra)); + gsm48_parse_ra(&raid, from_peer->ra); + LOGP(DGPRS, LOGL_INFO, "NSEI=%u BSSGP SUSPEND/RESUME " + "RAC snooping: RAC %u-%u-%u-%u behind BVCI=%u, " + "NSVCI=%u\n",nsvc->nsei, raid.mcc, raid.mnc, raid.lac, + raid.rac , from_peer->bvci, nsvc->nsvci); + /* FIXME: This only supports one BSS per RA */ + break; + case BSSGP_PDUT_BVC_RESET: + /* If we receive a BVC reset on the signalling endpoint, we + * don't want the SGSN to reset, as the signalling endpoint + * is common for all point-to-point BVCs (and thus all BTS) */ + if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) { + uint16_t bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI)); + LOGP(DGPRS, LOGL_INFO, "NSEI=%u Rx BVC RESET (BVCI=%u)\n", + nsvc->nsei, bvci); + if (bvci == 0) { + /* FIXME: only do this if SGSN is alive! */ + LOGP(DGPRS, LOGL_INFO, "NSEI=%u Tx fake " + "BVC RESET ACK of BVCI=0\n", nsvc->nsei); + return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, + nsvc->nsei, 0, ns_bvci); + } + from_peer = peer_by_bvci(bvci); + if (!from_peer) { + /* if a PTP-BVC is reset, and we don't know that + * PTP-BVCI yet, we should allocate a new peer */ + LOGP(DGPRS, LOGL_INFO, "Allocationg new peer for " + "BVCI=%u via NSVCI=%u/NSEI=%u\n", bvci, + nsvc->nsvci, nsvc->nsei); + from_peer = peer_alloc(bvci); + from_peer->nsvc = nsvc; + } + if (TLVP_PRESENT(&tp, BSSGP_IE_CELL_ID)) { + struct gprs_ra_id raid; + /* We have a Cell Identifier present in this + * PDU, this means we can extend our local + * state information about this particular cell + * */ + memcpy(from_peer->ra, + TLVP_VAL(&tp, BSSGP_IE_CELL_ID), + sizeof(from_peer->ra)); + gsm48_parse_ra(&raid, from_peer->ra); + LOGP(DGPRS, LOGL_INFO, "NSEI=%u/BVCI=%u " + "Cell ID %u-%u-%u-%u\n", nsvc->nsei, + bvci, raid.mcc, raid.mnc, raid.lac, + raid.rac); + } + } + break; + } + + /* Normally, we can simply pass on all signalling messages from BSS to + * SGSN */ + return gbprox_relay2sgsn(msg, ns_bvci); +err_no_peer: + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) cannot find peer based on RAC\n", + nsvc->nsei); + return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg); +err_mand_ie: + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) missing mandatory RA IE\n", + nsvc->nsei); + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); +} + +/* Receive paging request from SGSN, we need to relay to proper BSS */ +static int gbprox_rx_paging(struct msgb *msg, struct tlv_parsed *tp, + struct gprs_nsvc *nsvc, uint16_t ns_bvci) +{ + struct gbprox_peer *peer = NULL; + + LOGP(DGPRS, LOGL_INFO, "NSEI=%u(SGSN) BSSGP PAGING ", + nsvc->nsei); + if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { + uint16_t bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI)); + LOGPC(DGPRS, LOGL_INFO, "routing by BVCI to peer BVCI=%u\n", + bvci); + } else if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { + peer = peer_by_rac(TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA)); + LOGPC(DGPRS, LOGL_INFO, "routing by RAC to peer BVCI=%u\n", + peer->bvci); + } else if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) { + peer = peer_by_lac(TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA)); + LOGPC(DGPRS, LOGL_INFO, "routing by LAC to peer BVCI=%u\n", + peer->bvci); + } else + LOGPC(DGPRS, LOGL_INFO, "\n"); + + if (!peer) { + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) BSSGP PAGING: " + "unable to route, missing IE\n", nsvc->nsei); + return -EINVAL; + } + return gbprox_relay2peer(msg, peer, ns_bvci); +} + +/* Receive an incoming BVC-RESET message from the SGSN */ +static int rx_reset_from_sgsn(struct msgb *msg, struct tlv_parsed *tp, + struct gprs_nsvc *nsvc, uint16_t ns_bvci) +{ + struct gbprox_peer *peer; + uint16_t ptp_bvci; + + if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, + NULL, msg); + } + ptp_bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI)); + + if (ptp_bvci >= 2) { + /* A reset for a PTP BVC was received, forward it to its + * respective peer */ + peer = peer_by_bvci(ptp_bvci); + if (!peer) { + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u BVCI=%u: Cannot find BSS\n", + nsvc->nsei, ptp_bvci); + return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, + NULL, msg); + } + return gbprox_relay2peer(msg, peer, ns_bvci); + } + + /* A reset for the Signalling entity has been received + * from the SGSN. As the signalling BVCI is shared + * among all the BSS's that we multiplex, it needs to + * be relayed */ + llist_for_each_entry(peer, &gbprox_bts_peers, list) + gbprox_relay2peer(msg, peer, ns_bvci); + + return 0; +} + +/* Receive an incoming signalling message from the SGSN-side NS-VC */ +static int gbprox_rx_sig_from_sgsn(struct msgb *msg, struct gprs_nsvc *nsvc, + uint16_t ns_bvci) +{ + struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); + struct tlv_parsed tp; + uint8_t pdu_type = bgph->pdu_type; + int data_len = msgb_bssgp_len(msg) - sizeof(*bgph); + struct gbprox_peer *peer; + uint16_t bvci; + int rc = 0; + + if (ns_bvci != 0) { + LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BVCI=%u is not " + "signalling\n", nsvc->nsei, ns_bvci); + /* FIXME: Send proper error message */ + return -EINVAL; + } + + /* we actually should never see those two for BVCI == 0, but double-check + * just to make sure */ + if (pdu_type == BSSGP_PDUT_UL_UNITDATA || + pdu_type == BSSGP_PDUT_DL_UNITDATA) { + LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) UNITDATA not allowed in " + "signalling\n", nsvc->nsei); + return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); + } + + rc = bssgp_tlv_parse(&tp, bgph->data, data_len); + + switch (pdu_type) { + case BSSGP_PDUT_BVC_RESET: + rc = rx_reset_from_sgsn(msg, &tp, nsvc, ns_bvci); + break; + case BSSGP_PDUT_FLUSH_LL: + case BSSGP_PDUT_BVC_BLOCK_ACK: + case BSSGP_PDUT_BVC_UNBLOCK_ACK: + case BSSGP_PDUT_BVC_RESET_ACK: + /* simple case: BVCI IE is mandatory */ + if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) + goto err_mand_ie; + bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI)); + rc = gbprox_relay2bvci(msg, bvci, ns_bvci); + break; + case BSSGP_PDUT_PAGING_PS: + case BSSGP_PDUT_PAGING_CS: + /* process the paging request (LAC/RAC lookup) */ + rc = gbprox_rx_paging(msg, &tp, nsvc, ns_bvci); + break; + case BSSGP_PDUT_STATUS: + /* Some exception has occurred */ + LOGP(DGPRS, LOGL_NOTICE, + "NSEI=%u(SGSN) BSSGP STATUS ", nsvc->nsei); + if (!TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) { + LOGPC(DGPRS, LOGL_NOTICE, "\n"); + goto err_mand_ie; + } + LOGPC(DGPRS, LOGL_NOTICE, + "cause=0x%02x(%s) ", *TLVP_VAL(&tp, BSSGP_IE_CAUSE), + bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE))); + if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) { + uint16_t *bvci = (uint16_t *) + TLVP_VAL(&tp, BSSGP_IE_BVCI); + LOGPC(DGPRS, LOGL_NOTICE, + "BVCI=%u\n", ntohs(*bvci)); + } else + LOGPC(DGPRS, LOGL_NOTICE, "\n"); + break; + /* those only exist in the SGSN -> BSS direction */ + case BSSGP_PDUT_SUSPEND_ACK: + case BSSGP_PDUT_SUSPEND_NACK: + case BSSGP_PDUT_RESUME_ACK: + case BSSGP_PDUT_RESUME_NACK: + /* RAC IE is mandatory */ + if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) + goto err_mand_ie; + peer = peer_by_rac(TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA)); + if (!peer) + goto err_no_peer; + rc = gbprox_relay2peer(msg, peer, ns_bvci); + break; + case BSSGP_PDUT_SGSN_INVOKE_TRACE: + LOGP(DGPRS, LOGL_ERROR, + "NSEI=%u(SGSN) BSSGP INVOKE TRACE not supported\n",nsvc->nsei); + rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg); + break; + default: + LOGP(DGPRS, LOGL_NOTICE, "BSSGP PDU type 0x%02x unknown\n", + pdu_type); + rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); + break; + } + + return rc; +err_mand_ie: + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) missing mandatory IE\n", + nsvc->nsei); + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); +err_no_peer: + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) cannot find peer based on RAC\n", + nsvc->nsei); + return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg); +} + +/* Main input function for Gb proxy */ +int gbprox_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci) +{ + int rc; + + /* Only BVCI=0 messages need special treatment */ + if (ns_bvci == 0 || ns_bvci == 1) { + if (nsvc->remote_end_is_sgsn) + rc = gbprox_rx_sig_from_sgsn(msg, nsvc, ns_bvci); + else + rc = gbprox_rx_sig_from_bss(msg, nsvc, ns_bvci); + } else { + /* All other BVCI are PTP and thus can be simply forwarded */ + if (!nsvc->remote_end_is_sgsn) { + rc = gbprox_relay2sgsn(msg, ns_bvci); + } else { + struct gbprox_peer *peer = peer_by_bvci(ns_bvci); + if (!peer) { + LOGP(DGPRS, LOGL_INFO, "Allocationg new peer for " + "BVCI=%u via NSVC=%u/NSEI=%u\n", ns_bvci, + nsvc->nsvci, nsvc->nsei); + peer = peer_alloc(ns_bvci); + peer->nsvc = nsvc; + } + rc = gbprox_relay2peer(msg, peer, ns_bvci); + } + } + + return rc; +} + +/* Signal handler for signals from NS layer */ +int gbprox_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct ns_signal_data *nssd = signal_data; + struct gprs_nsvc *nsvc = nssd->nsvc; + struct gbprox_peer *peer; + + if (subsys != SS_NS) + return 0; + + if (signal == S_NS_RESET && nsvc->nsei == gbcfg.nsip_sgsn_nsei) { + /* We have received a NS-RESET from the NSEI and NSVC + * of the SGSN. This might happen with SGSN that start + * their own NS-RESET procedure without waiting for our + * NS-RESET */ + nsvc->remote_end_is_sgsn = 1; + } + + if (signal == S_NS_ALIVE_EXP && nsvc->remote_end_is_sgsn) { + LOGP(DGPRS, LOGL_NOTICE, "Tns alive expired too often, " + "re-starting RESET procedure\n"); + nsip_connect(nsvc->nsi, &nsvc->ip.bts_addr, nsvc->nsei, + nsvc->nsvci); + } + + /* We currently only care about signals from the SGSN */ + if (!nsvc->remote_end_is_sgsn) + return 0; + + /* iterate over all BTS peers and send the respective PDU */ + llist_for_each_entry(peer, &gbprox_bts_peers, list) { + switch (signal) { + case S_NS_RESET: + gprs_ns_tx_reset(peer->nsvc, nssd->cause); + break; + case S_NS_BLOCK: + gprs_ns_tx_block(peer->nsvc, nssd->cause); + break; + case S_NS_UNBLOCK: + gprs_ns_tx_unblock(peer->nsvc); + break; + } + } + return 0; +} + + +#include <vty/command.h> + +gDEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy", + SHOW_STR "Display information about the Gb proxy") +{ + struct gbprox_peer *peer; + + llist_for_each_entry(peer, &gbprox_bts_peers, list) { + struct gprs_nsvc *nsvc = peer->nsvc; + struct gprs_ra_id raid; + gsm48_parse_ra(&raid, peer->ra); + + vty_out(vty, "NSEI %5u, NS-VC %5u, PTP-BVCI %u, " + "RAC %u-%u-%u-%u%s", + nsvc->nsei, nsvc->nsvci, peer->bvci, + raid.mcc, raid.mnc, raid.lac, raid.rac, VTY_NEWLINE); + if (nsvc->nsi->ll == GPRS_NS_LL_UDP) + vty_out(vty, " remote address %s:%u%s", + inet_ntoa(nsvc->ip.bts_addr.sin_addr), + ntohs(nsvc->ip.bts_addr.sin_port), VTY_NEWLINE); + } + return CMD_SUCCESS; +} diff --git a/openbsc/src/gprs/gb_proxy_main.c b/openbsc/src/gprs/gb_proxy_main.c new file mode 100644 index 000000000..fc8f1648d --- /dev/null +++ b/openbsc/src/gprs/gb_proxy_main.c @@ -0,0 +1,188 @@ +/* NS-over-IP proxy */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by On Waves + * 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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include <signal.h> +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <osmocore/talloc.h> +#include <osmocore/select.h> +#include <osmocore/rate_ctr.h> + +#include <openbsc/signal.h> +#include <openbsc/debug.h> +#include <openbsc/gprs_ns.h> +#include <openbsc/gprs_bssgp.h> +#include <openbsc/telnet_interface.h> +#include <openbsc/vty.h> +#include <openbsc/gb_proxy.h> + +#include "../../bscconfig.h" + +/* this is here for the vty... it will never be called */ +void subscr_put() { abort(); } + +#define _GNU_SOURCE +#include <getopt.h> + +void *tall_bsc_ctx; + +const char *openbsc_version = "Osmocom NSIP Proxy " PACKAGE_VERSION; +const char *openbsc_copyright = + "Copyright (C) 2010 Harald Welte and On-Waves\n" + "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\n" + "Dieter Spaar, Andreas Eversberg, Holger Freyther\n\n" + "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n"; + +static char *config_file = "osmo_gbproxy.cfg"; +struct gbproxy_config gbcfg; + +/* Pointer to the SGSN peer */ +extern struct gbprox_peer *gbprox_peer_sgsn; + +/* call-back function for the NS protocol */ +static int proxy_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, + struct msgb *msg, u_int16_t bvci) +{ + int rc = 0; + + switch (event) { + case GPRS_NS_EVT_UNIT_DATA: + rc = gbprox_rcvmsg(msg, nsvc, bvci); + break; + default: + LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event); + if (msg) + talloc_free(msg); + rc = -EIO; + break; + } + return rc; +} + +static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGINT: + dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL); + sleep(1); + exit(0); + break; + case SIGABRT: + /* in case of abort, we want to obtain a talloc report + * and then return to the caller, who will abort the process */ + case SIGUSR1: + talloc_report(tall_vty_ctx, stderr); + talloc_report_full(tall_bsc_ctx, stderr); + break; + case SIGUSR2: + talloc_report_full(tall_vty_ctx, stderr); + break; + default: + break; + } +} + +extern void *tall_msgb_ctx; + +int main(int argc, char **argv) +{ + struct gsm_network dummy_network; + struct log_target *stderr_target; + struct sockaddr_in sin; + int rc; + + tall_bsc_ctx = talloc_named_const(NULL, 0, "nsip_proxy"); + tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); + + signal(SIGINT, &signal_handler); + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); + signal(SIGPIPE, SIG_IGN); + + log_init(&log_info); + stderr_target = log_target_create_stderr(); + log_add_target(stderr_target); + log_set_all_filter(stderr_target, 1); + + rate_ctr_init(tall_bsc_ctx); + telnet_init(&dummy_network, 4246); + + bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb); + if (!bssgp_nsi) { + LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); + exit(1); + } + gbcfg.nsi = bssgp_nsi; + gprs_ns_vty_init(bssgp_nsi); + register_signal_handler(SS_NS, &gbprox_signal, NULL); + + rc = gbproxy_parse_config(config_file, &gbcfg); + if (rc < 0) { + LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n"); + exit(2); + } + + nsip_listen(bssgp_nsi, gbcfg.nsip_listen_port); + + /* 'establish' the outgoing connection to the SGSN */ + sin.sin_family = AF_INET; + sin.sin_port = htons(gbcfg.nsip_sgsn_port); + sin.sin_addr.s_addr = htonl(gbcfg.nsip_sgsn_ip); + nsip_connect(bssgp_nsi, &sin, gbcfg.nsip_sgsn_nsei, + gbcfg.nsip_sgsn_nsvci); + + while (1) { + rc = bsc_select_main(0); + if (rc < 0) + exit(3); + } + + exit(0); +} + +struct gsm_network; +int bsc_vty_init(struct gsm_network *dummy) +{ + cmd_init(1); + vty_init(); + + openbsc_vty_add_cmds(); + gbproxy_vty_init(); + return 0; +} + diff --git a/openbsc/src/gprs/gb_proxy_vty.c b/openbsc/src/gprs/gb_proxy_vty.c new file mode 100644 index 000000000..e1cf97e87 --- /dev/null +++ b/openbsc/src/gprs/gb_proxy_vty.c @@ -0,0 +1,183 @@ +/* + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by On-Waves + * 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 <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <osmocore/talloc.h> + +#include <openbsc/debug.h> +#include <openbsc/gb_proxy.h> +#include <openbsc/gprs_ns.h> + +#include <vty/command.h> +#include <vty/vty.h> + +static struct gbproxy_config *g_cfg = NULL; + +/* + * vty code for mgcp below + */ +static struct cmd_node gbproxy_node = { + GBPROXY_NODE, + "%s(gbproxy)#", + 1, +}; + +static int config_write_gbproxy(struct vty *vty) +{ + struct in_addr ia; + + vty_out(vty, "gbproxy%s", VTY_NEWLINE); + + if (g_cfg->nsip_listen_ip) { + ia.s_addr = htonl(g_cfg->nsip_listen_ip); + vty_out(vty, " nsip bss local ip %s%s", inet_ntoa(ia), + VTY_NEWLINE); + } + vty_out(vty, " nsip bss local port %u%s", g_cfg->nsip_listen_port, + VTY_NEWLINE); + ia.s_addr = htonl(g_cfg->nsip_sgsn_ip); + vty_out(vty, " nsip sgsn remote ip %s%s", inet_ntoa(ia), + VTY_NEWLINE); + vty_out(vty, " nsip sgsn remote port %u%s", g_cfg->nsip_sgsn_port, + VTY_NEWLINE); + vty_out(vty, " nsip sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei, + VTY_NEWLINE); + vty_out(vty, " nsip sgsn nsvci %u%s", g_cfg->nsip_sgsn_nsvci, + VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(cfg_gbproxy, + cfg_gbproxy_cmd, + "gbproxy", + "Configure the Gb proxy") +{ + vty->node = GBPROXY_NODE; + return CMD_SUCCESS; +} + +DEFUN(cfg_nsip_bss_local_ip, + cfg_nsip_bss_local_ip_cmd, + "nsip bss local ip A.B.C.D", + "Set the IP address on which we listen for BSS connects") +{ + struct in_addr ia; + + inet_aton(argv[0], &ia); + g_cfg->nsip_listen_ip = ntohl(ia.s_addr); + + return CMD_SUCCESS; +} + +DEFUN(cfg_nsip_bss_local_port, + cfg_nsip_bss_local_port_cmd, + "nsip bss local port <0-65534>", + "Set the UDP port on which we listen for BSS connects") +{ + unsigned int port = atoi(argv[0]); + + g_cfg->nsip_listen_port = port; + return CMD_SUCCESS; +} + + +DEFUN(cfg_nsip_sgsn_ip, + cfg_nsip_sgsn_ip_cmd, + "nsip sgsn remote ip A.B.C.D", + "Set the IP of the SGSN to which the proxy shall connect") +{ + struct in_addr ia; + + inet_aton(argv[0], &ia); + g_cfg->nsip_sgsn_ip = ntohl(ia.s_addr); + + return CMD_SUCCESS; +} + +DEFUN(cfg_nsip_sgsn_port, + cfg_nsip_sgsn_port_cmd, + "nsip sgsn remote port <0-65534>", + "Set the UDP port of the SGSN to which the proxy shall connect") +{ + unsigned int port = atoi(argv[0]); + + g_cfg->nsip_sgsn_port = port; + return CMD_SUCCESS; +} + +DEFUN(cfg_nsip_sgsn_nsei, + cfg_nsip_sgsn_nsei_cmd, + "nsip sgsn nsei <0-65534>", + "Set the NSEI to be used in the connection with the SGSN") +{ + unsigned int port = atoi(argv[0]); + + g_cfg->nsip_sgsn_nsei = port; + return CMD_SUCCESS; +} + +DEFUN(cfg_nsip_sgsn_nsvci, + cfg_nsip_sgsn_nsvci_cmd, + "nsip sgsn nsvci <0-65534>", + "Set the NSVCI to be used in the connection with the SGSN") +{ + unsigned int port = atoi(argv[0]); + + g_cfg->nsip_sgsn_nsvci = port; + return CMD_SUCCESS; +} + +int gbproxy_vty_init(void) +{ + install_element_ve(&show_gbproxy_cmd); + + install_element(CONFIG_NODE, &cfg_gbproxy_cmd); + install_node(&gbproxy_node, config_write_gbproxy); + install_default(GBPROXY_NODE); + install_element(GBPROXY_NODE, &cfg_nsip_bss_local_ip_cmd); + install_element(GBPROXY_NODE, &cfg_nsip_bss_local_port_cmd); + install_element(GBPROXY_NODE, &cfg_nsip_sgsn_ip_cmd); + install_element(GBPROXY_NODE, &cfg_nsip_sgsn_port_cmd); + install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd); + install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsvci_cmd); + + return 0; +} + +int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg) +{ + int rc; + + g_cfg = cfg; + rc = vty_read_config_file(config_file); + if (rc < 0) { + fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); + return rc; + } + + return 0; +} + diff --git a/openbsc/src/gprs/gprs_bssgp.c b/openbsc/src/gprs/gprs_bssgp.c new file mode 100644 index 000000000..9fdfd329d --- /dev/null +++ b/openbsc/src/gprs/gprs_bssgp.c @@ -0,0 +1,448 @@ +/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */ + +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 <errno.h> +#include <stdint.h> + +#include <netinet/in.h> + +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/talloc.h> + +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_04_08_gprs.h> +#include <openbsc/gprs_bssgp.h> +#include <openbsc/gprs_llc.h> +#include <openbsc/gprs_ns.h> + +void *bssgp_tall_ctx = NULL; + +#define BVC_F_BLOCKED 0x0001 + +/* The per-BTS context that we keep on the SGSN side of the BSSGP link */ +struct bssgp_bts_ctx { + struct llist_head list; + + /* parsed RA ID and Cell ID of the remote BTS */ + struct gprs_ra_id ra_id; + uint16_t cell_id; + + /* 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 bvc_state; + + /* 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; +}; +static LLIST_HEAD(bts_ctxts); + +/* Find a BTS Context based on parsed RA ID and Cell ID */ +struct bssgp_bts_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid) +{ + struct bssgp_bts_ctx *bctx; + + llist_for_each_entry(bctx, &bts_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_bts_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei) +{ + struct bssgp_bts_ctx *bctx; + + llist_for_each_entry(bctx, &bts_ctxts, list) { + if (bctx->nsei == nsei && bctx->bvci == bvci) + return bctx; + } + return NULL; +} + +struct bssgp_bts_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei) +{ + struct bssgp_bts_ctx *ctx; + + ctx = talloc_zero(bssgp_tall_ctx, struct bssgp_bts_ctx); + if (!ctx) + return NULL; + ctx->bvci = bvci; + ctx->nsei = nsei; + llist_add(&ctx->list, &bts_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); +} + +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)); +} + +/* Chapter 8.4 BVC-Reset Procedure */ +static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp, + uint16_t ns_bvci) +{ + struct bssgp_bts_ctx *bctx; + uint16_t nsei = msgb_nsei(msg); + uint16_t bvci; + int rc; + + bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI)); + DEBUGPC(DBSSGP, "BVCI=%u, 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); + + /* 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 RESET BVCI=%u " + "missing mandatory IE\n", bvci); + return -EINVAL; + } + /* actually extract RAC / CID */ + bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id, + TLVP_VAL(tp, BSSGP_IE_CELL_ID)); + LOGP(DBSSGP, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n", + bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac, + bctx->ra_id.rac, bctx->cell_id, bvci); + } + + /* Acknowledge the RESET to the BTS */ + rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, + nsei, bvci, ns_bvci); + return 0; +} + +/* Uplink unit-data */ +static int bssgp_rx_ul_ud(struct msgb *msg) +{ + struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg); + int data_len = msgb_bssgp_len(msg) - sizeof(*budh); + struct tlv_parsed tp; + int rc; + + DEBUGP(DBSSGP, "BSSGP UL-UD\n"); + + /* extract TLLI and parse TLV IEs */ + msgb_tlli(msg) = ntohl(budh->tlli); + rc = bssgp_tlv_parse(&tp, budh->data, data_len); + + /* Cell ID and LLC_PDU are the only mandatory IE */ + if (!TLVP_PRESENT(&tp, BSSGP_IE_CELL_ID) || + !TLVP_PRESENT(&tp, BSSGP_IE_LLC_PDU)) + return -EIO; + + /* FIXME: lookup bssgp_bts_ctx based on BVCI + NSEI */ + + /* store pointer to LLC header and CELL ID in msgb->cb */ + msgb_llch(msg) = TLVP_VAL(&tp, BSSGP_IE_LLC_PDU); + msgb_bcid(msg) = TLVP_VAL(&tp, BSSGP_IE_CELL_ID); + + return gprs_llc_rcvmsg(msg, &tp); +} + +static int bssgp_rx_suspend(struct msgb *msg) +{ + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_bssgph(msg); + int data_len = msgb_bssgp_len(msg) - sizeof(*bgph); + struct tlv_parsed tp; + int rc; + + DEBUGP(DBSSGP, "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; + + /* FIXME: pass the SUSPEND request to GMM */ + /* SEND SUSPEND_ACK or SUSPEND_NACK */ +} + +static int bssgp_rx_resume(struct msgb *msg) +{ + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_bssgph(msg); + int data_len = msgb_bssgp_len(msg) - sizeof(*bgph); + struct tlv_parsed tp; + int rc; + + DEBUGP(DBSSGP, "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; + + /* FIXME: pass the RESUME request to GMM */ + /* SEND RESUME_ACK or RESUME_NACK */ +} + +static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp) +{ + + DEBUGP(DBSSGP, "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); + + /* 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)); +} + +/* We expect msgb_bssgph() to point to the BSSGP header */ +int gprs_bssgp_rcvmsg(struct msgb *msg) +{ + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_bssgph(msg); + struct tlv_parsed tp; + uint8_t pdu_type = bgph->pdu_type; + int data_len = msgb_bssgp_len(msg) - sizeof(*bgph); + uint16_t bvci; /* PTP BVCI */ + uint16_t ns_bvci = msgb_bvci(msg); + 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) + 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); + break; + case BSSGP_PDUT_RA_CAPABILITY: + /* BSS requests RA capability or IMSI */ + DEBUGP(DBSSGP, "BSSGP RA CAPABILITY UPDATE\n"); + /* FIXME: send RA_CAPA_UPDATE_ACK */ + break; + case BSSGP_PDUT_RADIO_STATUS: + DEBUGP(DBSSGP, "BSSGP RADIO STATUS\n"); + /* BSS informs us of some exception */ + /* FIXME: notify GMM */ + break; + case BSSGP_PDUT_SUSPEND: + /* MS wants to suspend */ + rc = bssgp_rx_suspend(msg); + break; + case BSSGP_PDUT_RESUME: + /* MS wants to resume */ + rc = bssgp_rx_resume(msg); + break; + case BSSGP_PDUT_FLUSH_LL: + /* BSS informs MS has moved to one cell to other cell */ + DEBUGP(DBSSGP, "BSSGP FLUSH LL\n"); + /* FIXME: notify GMM */ + /* Send FLUSH_LL_ACK */ + break; + case BSSGP_PDUT_LLC_DISCARD: + /* BSS informs that some LLC PDU's have been discarded */ + DEBUGP(DBSSGP, "BSSGP LLC DISCARDED\n"); + /* FIXME: notify GMM */ + break; + case BSSGP_PDUT_FLOW_CONTROL_BVC: + /* BSS informs us of available bandwidth in Gb interface */ + rc = bssgp_rx_fc_bvc(msg, &tp); + break; + case BSSGP_PDUT_FLOW_CONTROL_MS: + /* BSS informs us of available bandwidth to one MS */ + DEBUGP(DBSSGP, "BSSGP FC MS\n"); + /* FIXME: actually implement flow control */ + /* FIXME: Send FLOW_CONTROL_MS_ACK */ + break; + case BSSGP_PDUT_BVC_BLOCK: + /* BSS tells us that BVC shall be blocked */ + DEBUGP(DBSSGP, "BSSGP BVC BLOCK "); + if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI) || + !TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) + goto err_mand_ie; + bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI)); + DEBUGPC(DBSSGP, "BVCI=%u, cause=%s\n", bvci, + bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE))); + /* We always acknowledge the BLOCKing */ + rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, + msgb_nsei(msg), bvci, ns_bvci); + break; + case BSSGP_PDUT_BVC_UNBLOCK: + /* BSS tells us that BVC shall be unblocked */ + DEBUGP(DBSSGP, "BSSGP BVC UNBLOCK "); + if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) + goto err_mand_ie; + bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI)); + DEBUGPC(DBSSGP, "BVCI=%u\n", bvci); + /* We always acknowledge the unBLOCKing */ + rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, + msgb_nsei(msg), bvci, ns_bvci); + break; + case BSSGP_PDUT_BVC_RESET: + /* BSS tells us that BVC init is required */ + DEBUGP(DBSSGP, "BSSGP BVC RESET "); + if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI) || + !TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) + goto err_mand_ie; + rc = bssgp_rx_bvc_reset(msg, &tp, ns_bvci); + break; + case BSSGP_PDUT_STATUS: + /* Some exception has occurred */ + /* FIXME: notify GMM */ + 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 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(DBSSGP, "BSSGP PDU type 0x%02x only exists in DL\n", + pdu_type); + rc = -EINVAL; + break; + default: + DEBUGP(DBSSGP, "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); +} + +/* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU + * to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */ +int gprs_bssgp_tx_dl_ud(struct msgb *msg) +{ + struct bssgp_bts_ctx *bctx; + struct bssgp_ud_hdr *budh; + uint8_t llc_pdu_tlv_hdr_len = 2; + uint8_t *llc_pdu_tlv, *qos_profile; + uint16_t pdu_lifetime = 1000; /* centi-seconds */ + uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x21 }; + uint16_t msg_len = msg->len; + uint16_t bvci = msgb_bvci(msg); + uint16_t nsei = msgb_nsei(msg); + + /* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */ + if (bvci < 2) { + LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n", + bvci); + return -EINVAL; + } + + bctx = btsctx_by_bvci_nsei(bvci, nsei); + if (!bctx) + 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 & 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, (uint8_t *)&pdu_lifetime); + + /* prepend the QoS profile, TLLI and pdu type */ + budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh)); + memcpy(budh->qos_profile, qos_profile_default, sizeof(qos_profile_default)); + budh->tlli = htonl(msgb_tlli(msg)); + budh->pdu_type = BSSGP_PDUT_DL_UNITDATA; + + /* Identifiers down: BVCI, NSEI (in msgb->cb) */ + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} diff --git a/openbsc/src/gprs/gprs_bssgp_util.c b/openbsc/src/gprs/gprs_bssgp_util.c new file mode 100644 index 000000000..d9b5175f6 --- /dev/null +++ b/openbsc/src/gprs/gprs_bssgp_util.c @@ -0,0 +1,119 @@ +/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */ + +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 <errno.h> +#include <stdint.h> + +#include <netinet/in.h> + +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/talloc.h> + +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/gprs_bssgp.h> +#include <openbsc/gprs_ns.h> + +struct gprs_ns_inst *bssgp_nsi; + +/* BSSGP Protocol specific, not implementation specific */ +/* FIXME: This needs to go into libosmocore after finished */ + +/* Chapter 11.3.9 / Table 11.10: Cause coding */ +static const struct value_string bssgp_cause_strings[] = { + { BSSGP_CAUSE_PROC_OVERLOAD, "Processor overload" }, + { BSSGP_CAUSE_EQUIP_FAIL, "Equipment Failure" }, + { BSSGP_CAUSE_TRASIT_NET_FAIL, "Transit netowkr service failure" }, + { BSSGP_CAUSE_CAPA_GREATER_0KPBS,"Transmission capacity modified" }, + { BSSGP_CAUSE_UNKNOWN_MS, "Unknown MS" }, + { BSSGP_CAUSE_UNKNOWN_BVCI, "Unknown BVCI" }, + { BSSGP_CAUSE_CELL_TRAF_CONG, "Cell traffic congestion" }, + { BSSGP_CAUSE_SGSN_CONG, "SGSN congestion" }, + { BSSGP_CAUSE_OML_INTERV, "O&M intervention" }, + { BSSGP_CAUSE_BVCI_BLOCKED, "BVCI blocked" }, + { BSSGP_CAUSE_PFC_CREATE_FAIL, "PFC create failure" }, + { BSSGP_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" }, + { BSSGP_CAUSE_INV_MAND_INF, "Invalid mandatory information" }, + { BSSGP_CAUSE_MISSING_MAND_IE, "Missing mandatory IE" }, + { BSSGP_CAUSE_MISSING_COND_IE, "Missing conditional IE" }, + { BSSGP_CAUSE_UNEXP_COND_IE, "Unexpected conditional IE" }, + { BSSGP_CAUSE_COND_IE_ERR, "Conditional IE error" }, + { BSSGP_CAUSE_PDU_INCOMP_STATE, "PDU incompatible with protocol state" }, + { BSSGP_CAUSE_PROTO_ERR_UNSPEC, "Protocol error - unspecified" }, + { BSSGP_CAUSE_PDU_INCOMP_FEAT, "PDU not compatible with feature set" }, + { 0, NULL }, +}; + +const char *bssgp_cause_str(enum gprs_bssgp_cause cause) +{ + return get_value_string(bssgp_cause_strings, cause); +} + + +struct msgb *bssgp_msgb_alloc(void) +{ + return msgb_alloc_headroom(4096, 128, "BSSGP"); +} + +/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */ +int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei, + uint16_t bvci, uint16_t ns_bvci) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + uint16_t _bvci; + + msgb_nsei(msg) = nsei; + msgb_bvci(msg) = ns_bvci; + + bgph->pdu_type = pdu_type; + _bvci = htons(bvci); + msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci); + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} + +/* Chapter 10.4.14: Status */ +int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + + DEBUGPC(DBSSGP, "BSSGP: TX STATUS, cause=%s\n", bssgp_cause_str(cause)); + msgb_nsei(msg) = msgb_nsei(orig_msg); + msgb_bvci(msg) = 0; + + bgph->pdu_type = BSSGP_PDUT_STATUS; + msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause); + if (bvci) { + uint16_t _bvci = htons(*bvci); + msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci); + } + if (orig_msg) + 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); +} diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c new file mode 100644 index 000000000..9c75a3d4e --- /dev/null +++ b/openbsc/src/gprs/gprs_llc.c @@ -0,0 +1,549 @@ +/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */ + +/* (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 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 <errno.h> +#include <stdint.h> + +#include <osmocore/msgb.h> +#include <osmocore/linuxlist.h> +#include <osmocore/timer.h> +#include <osmocore/talloc.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/debug.h> +#include <openbsc/gprs_bssgp.h> +#include <openbsc/gprs_llc.h> +#include <openbsc/crc24.h> + +/* 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; + + uint32_t tlli; + uint32_t sapi; + + uint8_t v_sent; + uint8_t v_ack; + uint8_t v_recv; + + unsigned int n200; + unsigned int retrans_ctr; + + /* over which BSSGP BTS ctx do we need to transmit */ + uint16_t bvci; + uint16_t nsei; +}; + +static LLIST_HEAD(gprs_llc_lles); +void *llc_tall_ctx; + +/* lookup LLC Entity based on DLCI (TLLI+SAPI tuple) */ +static struct gprs_llc_lle *lle_by_tlli_sapi(uint32_t tlli, uint32_t sapi) +{ + struct gprs_llc_lle *lle; + + llist_for_each_entry(lle, &gprs_llc_lles, list) { + if (lle->tlli == tlli && lle->sapi == sapi) + return lle; + } + return NULL; +} + +static struct gprs_llc_lle *lle_alloc(uint32_t tlli, uint32_t sapi) +{ + struct gprs_llc_lle *lle; + + lle = talloc_zero(llc_tall_ctx, struct gprs_llc_lle); + if (!lle) + return NULL; + + lle->tlli = tlli; + lle->sapi = sapi; + lle->state = GPRS_LLS_UNASSIGNED; + llist_add(&lle->list, &gprs_llc_lles); + + return lle; +} + +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 { + uint8_t sapi; + uint8_t is_cmd:1, + ack_req:1, + is_encrypted:1; + uint32_t seq_rx; + uint32_t seq_tx; + uint32_t fcs; + uint32_t fcs_calc; + uint8_t *data; + uint16_t data_len; + 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(uint8_t *data, unsigned int len) +{ + uint32_t fcs_calc; + + fcs_calc = crc24_calc(INIT_CRC24, data, len); + fcs_calc = ~fcs_calc; + fcs_calc &= 0xffffff; + + return fcs_calc; +} + +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++; + } +} + +int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command, + enum gprs_llc_u_cmd u_cmd, int pf_bit) +{ + uint8_t *fcs, *llch; + uint8_t addr, ctrl; + uint32_t fcs_calc; + + /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ + + /* Address Field */ + addr = sapi & 0xf; + if (command) + addr |= 0x40; + + /* 6.3 Figure 8 */ + ctrl = 0xe0 | u_cmd; + if (pf_bit) + ctrl |= 0x10; + + /* prepend LLC UI header */ + llch = msgb_push(msg, 2); + llch[0] = addr; + llch[1] = ctrl; + + /* 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; + + /* Identifiers passed down: (BVCI, NSEI) */ + + return gprs_bssgp_tx_dl_ud(msg); +} + +/* Send XID response to LLE */ +static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg) +{ + /* copy identifiers from LLE to ensure lower layers can route */ + msgb_tlli(msg) = lle->tlli; + msgb_bvci(msg) = lle->bvci; + msgb_nsei(msg) = lle->nsei; + + return gprs_llc_tx_u(msg, lle->sapi, 0, GPRS_LLC_U_XID, 1); +} + +/* Transmit a UI frame over the given SAPI */ +int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command) +{ + struct gprs_llc_lle *lle; + uint8_t *fcs, *llch; + uint8_t addr, ctrl[2]; + uint32_t fcs_calc; + uint16_t nu = 0; + + /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ + + /* look-up or create the LL Entity for this (TLLI, SAPI) tuple */ + lle = lle_by_tlli_sapi(msgb_tlli(msg), sapi); + if (!lle) + lle = lle_alloc(msgb_tlli(msg), sapi); + /* Update LLE's (BVCI, NSEI) tuple */ + lle->bvci = msgb_bvci(msg); + lle->nsei = msgb_nsei(msg); + + /* 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; + + /* Identifiers passed down: (BVCI, NSEI) */ + + 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 */ + /* FIXME: implement XID negotiation using SNDCP */ + { + struct msgb *resp; + uint8_t *xid; + resp = msgb_alloc_headroom(4096, 1024, "LLC_XID"); + xid = msgb_put(resp, gph->data_len); + memcpy(xid, gph->data, gph->data_len); + gprs_llc_tx_xid(lle, resp); + } + 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 uint8_t *llc_hdr, int len) +{ + uint8_t *ctrl = llc_hdr+1; + int is_sack = 0; + unsigned int crc_length; + uint32_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 */ + uint8_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; + } + ghp->data_len = (llc_hdr + len - 3) - ghp->data; + } else if ((ctrl[0] & 0xc0) == 0x80) { + /* S (Supervisory) format */ + ghp->data = NULL; + ghp->data_len = 0; + + 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->data_len = (llc_hdr + len - 3) - ghp->data; + + 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; + ghp->data_len = 0; + + switch (ctrl[0] & 0xf) { + case GPRS_LLC_U_NULL_CMD: + ghp->cmd = GPRS_LLC_NULL; + break; + case GPRS_LLC_U_DM_RESP: + ghp->cmd = GPRS_LLC_DM; + break; + case GPRS_LLC_U_DISC_CMD: + ghp->cmd = GPRS_LLC_DISC; + break; + case GPRS_LLC_U_UA_RESP: + ghp->cmd = GPRS_LLC_UA; + break; + case GPRS_LLC_U_SABM_CMD: + ghp->cmd = GPRS_LLC_SABM; + break; + case GPRS_LLC_U_FRMR_RESP: + ghp->cmd = GPRS_LLC_FRMR; + break; + case GPRS_LLC_U_XID: + ghp->cmd = GPRS_LLC_XID; + ghp->data = ctrl + 1; + ghp->data_len = (llc_hdr + len - 3) - ghp->data; + 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 (BSSGP-UL-UNITDATA-IND, 7.2.4.2) */ +int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) +{ + struct bssgp_ud_hdr *udh = (struct bssgp_ud_hdr *) msgb_bssgph(msg); + struct gprs_llc_hdr *lh = msgb_llch(msg); + struct gprs_llc_hdr_parsed llhp; + struct gprs_llc_lle *lle; + int rc = 0; + + /* Identifiers from DOWN: NSEI, BVCI, TLLI */ + + rc = gprs_llc_hdr_parse(&llhp, lh, TLVP_LEN(tv, BSSGP_IE_LLC_PDU)); + /* FIXME */ + + gprs_llc_hdr_dump(&llhp); + + /* find the LLC Entity for this TLLI+SAPI tuple */ + lle = lle_by_tlli_sapi(msgb_tlli(msg), llhp.sapi); + /* allocate a new LLE if needed */ + if (!lle) + lle = lle_alloc(msgb_tlli(msg), llhp.sapi); + + /* Update LLE's (BVCI, NSEI) tuple */ + lle->bvci = msgb_bvci(msg); + lle->nsei = msgb_nsei(msg); + + rc = gprs_llc_hdr_rx(&llhp, lle); + /* FIXME */ + + if (llhp.data) { + msgb_gmmh(msg) = llhp.data; + switch (llhp.sapi) { + case GPRS_SAPI_GMM: + rc = gsm0408_gprs_rcvmsg(msg); + break; + case GPRS_SAPI_TOM2: + case GPRS_SAPI_TOM8: + /* FIXME */ + case GPRS_SAPI_SNDCP3: + case GPRS_SAPI_SNDCP5: + case GPRS_SAPI_SNDCP9: + case GPRS_SAPI_SNDCP11: + /* FIXME */ + case GPRS_SAPI_SMS: + /* FIXME */ + default: + LOGP(DGPRS, LOGL_NOTICE, "Unsupported SAPI %u\n", llhp.sapi); + rc = -EINVAL; + break; + } + } + + return rc; +} diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c new file mode 100644 index 000000000..dc12953e0 --- /dev/null +++ b/openbsc/src/gprs/gprs_ns.c @@ -0,0 +1,926 @@ +/* GPRS Networks Service (NS) messages on the Gb interfacebvci = msgb_bvci(msg); + * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */ + +/* (C) 2009-2010 by Harald Welte <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 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. + */ + +/* This implementation has the following limitations: + * o Only one NS-VC for each NSE: No load-sharing function + * o NSVCI 65535 and 65534 are reserved for internal use + * o Only UDP is supported as of now, no frame relay support + * o The IP Sub-Network-Service (SNS) as specified in 48.016 is not implemented + * o There are no BLOCK and UNBLOCK timers (yet?) + */ + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <stdint.h> + +#include <arpa/inet.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> +#include <osmocore/rate_ctr.h> +#include <openbsc/debug.h> +#include <openbsc/signal.h> +#include <openbsc/gprs_ns.h> +#include <openbsc/gprs_bssgp.h> + +#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 }, + }, +}; + +enum ns_ctr { + NS_CTR_PKTS_IN, + NS_CTR_PKTS_OUT, + NS_CTR_BYTES_IN, + NS_CTR_BYTES_OUT, + NS_CTR_BLOCKED, + NS_CTR_DEAD, +}; + +static const struct rate_ctr_desc nsvc_ctr_description[] = { + { "packets.in", "Packets at NS Level ( In)" }, + { "packets.out","Packets at NS Level (Out)" }, + { "bytes.in", "Bytes at NS Level ( In)" }, + { "bytes.out", "Bytes at NS Level (Out)" }, + { "blocked", "NS-VC Block count " }, + { "dead", "NS-VC gone dead count " }, +}; + +static const struct rate_ctr_group_desc nsvc_ctrg_desc = { + .group_name_prefix = "ns.nsvc", + .group_description = "NSVC Peer Statistics", + .num_ctr = ARRAY_SIZE(nsvc_ctr_description), + .ctr_desc = nsvc_ctr_description, +}; + +/* Lookup struct gprs_nsvc based on NSVCI */ +static struct gprs_nsvc *nsvc_by_nsvci(struct gprs_ns_inst *nsi, + uint16_t nsvci) +{ + struct gprs_nsvc *nsvc; + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (nsvc->nsvci == nsvci) + return nsvc; + } + return NULL; +} + +/* Lookup struct gprs_nsvc based on NSVCI */ +struct gprs_nsvc *nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei) +{ + struct gprs_nsvc *nsvc; + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (nsvc->nsei == nsei) + return nsvc; + } + return NULL; +} + +/* Lookup struct gprs_nsvc based on remote peer socket addr */ +static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi, + struct sockaddr_in *sin) +{ + struct gprs_nsvc *nsvc; + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (nsvc->ip.bts_addr.sin_addr.s_addr == + sin->sin_addr.s_addr && + nsvc->ip.bts_addr.sin_port == sin->sin_port) + return nsvc; + } + return NULL; +} + +static void gprs_ns_timer_cb(void *data); + +struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci) +{ + struct gprs_nsvc *nsvc; + + nsvc = talloc_zero(nsi, struct gprs_nsvc); + nsvc->nsvci = nsvci; + /* before RESET procedure: BLOCKED and DEAD */ + nsvc->state = NSE_S_BLOCKED; + nsvc->nsi = nsi; + nsvc->timer.cb = gprs_ns_timer_cb; + nsvc->timer.data = nsvc; + nsvc->ctrg = rate_ctr_group_alloc(nsvc, &nsvc_ctrg_desc, nsvci); + + llist_add(&nsvc->list, &nsi->gprs_nsvcs); + + return nsvc; +} + +void nsvc_delete(struct gprs_nsvc *nsvc) +{ + if (bsc_timer_pending(&nsvc->timer)) + bsc_del_timer(&nsvc->timer); + llist_del(&nsvc->list); + talloc_free(nsvc); +} + +static void ns_dispatch_signal(struct gprs_nsvc *nsvc, unsigned int signal, + uint8_t cause) +{ + struct ns_signal_data nssd; + + nssd.nsvc = nsvc; + nssd.cause = cause; + + dispatch_signal(SS_NS, signal, &nssd); +} + +/* Section 10.3.2, Table 13 */ +static const struct value_string 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" }, + { 0, NULL } +}; + +const char *gprs_ns_cause_str(enum ns_cause cause) +{ + return get_value_string(ns_cause_str, cause); +} + +static int nsip_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg); + +static int gprs_ns_tx(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + int ret; + + /* Increment number of Uplink bytes */ + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_OUT]); + rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_OUT], msgb_l2len(msg)); + + switch (nsvc->nsi->ll) { + case GPRS_NS_LL_UDP: + ret = nsip_sendmsg(nsvc, msg); + break; + default: + LOGP(DNS, LOGL_ERROR, "unsupported NS linklayer %u\n", nsvc->nsi->ll); + msgb_free(msg); + ret = -EIO; + break; + } + return ret; +} + +static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, uint8_t pdu_type) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + + if (!msg) + return -ENOMEM; + + msg->l2h = msgb_put(msg, sizeof(*nsh)); + nsh = (struct gprs_ns_hdr *) msg->l2h; + + nsh->pdu_type = pdu_type; + + return gprs_ns_tx(nsvc, msg); +} + +int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + uint16_t nsvci = htons(nsvc->nsvci); + uint16_t nsei = htons(nsvc->nsei); + + if (!msg) + return -ENOMEM; + + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS RESET (NSVCI=%u, cause=%s)\n", + nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); + + msg->l2h = msgb_put(msg, sizeof(*nsh)); + nsh = (struct gprs_ns_hdr *) msg->l2h; + nsh->pdu_type = NS_PDUT_RESET; + + msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci); + msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei); + + return gprs_ns_tx(nsvc, msg); + +} + +int gprs_ns_tx_status(struct gprs_nsvc *nsvc, uint8_t cause, + uint16_t bvci, struct msgb *orig_msg) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + uint16_t nsvci = htons(nsvc->nsvci); + + bvci = htons(bvci); + + if (!msg) + return -ENOMEM; + + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS STATUS (NSVCI=%u, cause=%s)\n", + nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); + + msg->l2h = msgb_put(msg, sizeof(*nsh)); + nsh = (struct gprs_ns_hdr *) msg->l2h; + nsh->pdu_type = NS_PDUT_STATUS; + + msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); + + /* Section 9.2.7.1: Static conditions for NS-VCI */ + if (cause == NS_CAUSE_NSVC_BLOCKED || + cause == NS_CAUSE_NSVC_UNKNOWN) + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci); + + /* Section 9.2.7.2: Static conditions for NS PDU */ + switch (cause) { + case NS_CAUSE_SEM_INCORR_PDU: + case NS_CAUSE_PDU_INCOMP_PSTATE: + case NS_CAUSE_PROTO_ERR_UNSPEC: + case NS_CAUSE_INVAL_ESSENT_IE: + case NS_CAUSE_MISSING_ESSENT_IE: + msgb_tvlv_put(msg, NS_IE_PDU, msgb_l2len(orig_msg), + orig_msg->l2h); + break; + default: + break; + } + + /* Section 9.2.7.3: Static conditions for BVCI */ + if (cause == NS_CAUSE_BVCI_UNKNOWN) + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&bvci); + + return gprs_ns_tx(nsvc, msg); +} + +int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + uint16_t nsvci = htons(nsvc->nsvci); + + if (!msg) + return -ENOMEM; + + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS BLOCK (NSVCI=%u, cause=%s)\n", + nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); + + /* be conservative and mark it as blocked even now! */ + nsvc->state |= NSE_S_BLOCKED; + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); + + msg->l2h = msgb_put(msg, sizeof(*nsh)); + nsh = (struct gprs_ns_hdr *) msg->l2h; + nsh->pdu_type = NS_PDUT_BLOCK; + + msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci); + + return gprs_ns_tx(nsvc, msg); +} + +int gprs_ns_tx_unblock(struct gprs_nsvc *nsvc) +{ + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n", + nsvc->nsei, nsvc->nsvci); + + return gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK); +} + +int gprs_ns_tx_alive(struct gprs_nsvc *nsvc) +{ + LOGP(DNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE (NSVCI=%u)\n", + nsvc->nsei, nsvc->nsvci); + + return gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); +} + +int gprs_ns_tx_alive_ack(struct gprs_nsvc *nsvc) +{ + LOGP(DNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE_ACK (NSVCI=%u)\n", + nsvc->nsei, nsvc->nsvci); + + return gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE_ACK); +} + +static const enum ns_timeout timer_mode_tout[_NSVC_TIMER_NR] = { + [NSVC_TIMER_TNS_RESET] = NS_TOUT_TNS_RESET, + [NSVC_TIMER_TNS_ALIVE] = NS_TOUT_TNS_ALIVE, + [NSVC_TIMER_TNS_TEST] = NS_TOUT_TNS_TEST, +}; + +static const struct value_string timer_mode_strs[] = { + { NSVC_TIMER_TNS_RESET, "tns-reset" }, + { NSVC_TIMER_TNS_ALIVE, "tns-alive" }, + { NSVC_TIMER_TNS_TEST, "tns-test" }, + { 0, NULL } +}; + +static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode) +{ + enum ns_timeout tout = timer_mode_tout[mode]; + unsigned int seconds = nsvc->nsi->timeout[tout]; + + DEBUGP(DNS, "NSEI=%u Starting timer in mode %s (%u seconds)\n", + nsvc->nsei, get_value_string(timer_mode_strs, mode), + seconds); + + if (bsc_timer_pending(&nsvc->timer)) + bsc_del_timer(&nsvc->timer); + + nsvc->timer_mode = mode; + bsc_schedule_timer(&nsvc->timer, seconds, 0); +} + +static void gprs_ns_timer_cb(void *data) +{ + struct gprs_nsvc *nsvc = data; + enum ns_timeout tout = timer_mode_tout[nsvc->timer_mode]; + unsigned int seconds = nsvc->nsi->timeout[tout]; + + DEBUGP(DNS, "NSEI=%u Timer expired in mode %s (%u seconds)\n", + nsvc->nsei, get_value_string(timer_mode_strs, nsvc->timer_mode), + seconds); + + switch (nsvc->timer_mode) { + case NSVC_TIMER_TNS_ALIVE: + /* Tns-alive case: we expired without response ! */ + nsvc->alive_retries++; + if (nsvc->alive_retries > + nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) { + /* mark as dead and blocked */ + nsvc->state = NSE_S_BLOCKED; + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_DEAD]); + LOGP(DNS, LOGL_NOTICE, + "NSEI=%u Tns-alive expired more then " + "%u times, blocking NS-VC\n", nsvc->nsei, + nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]); + ns_dispatch_signal(nsvc, S_NS_ALIVE_EXP, 0); + ns_dispatch_signal(nsvc, S_NS_BLOCK, NS_CAUSE_NSVC_BLOCKED); + return; + } + /* Tns-test case: send NS-ALIVE PDU */ + gprs_ns_tx_alive(nsvc); + /* start Tns-alive timer */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); + break; + case NSVC_TIMER_TNS_TEST: + /* Tns-test case: send NS-ALIVE PDU */ + gprs_ns_tx_alive(nsvc); + /* start Tns-alive timer (transition into faster + * alive retransmissions) */ + nsvc->alive_retries = 0; + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); + break; + case NSVC_TIMER_TNS_RESET: + /* Chapter 7.3: Re-send the RESET */ + gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION); + /* Re-start Tns-reset timer */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); + break; + case _NSVC_TIMER_NR: + break; + } +} + +/* Section 9.2.6 */ +static int gprs_ns_tx_reset_ack(struct gprs_nsvc *nsvc) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS"); + struct gprs_ns_hdr *nsh; + uint16_t nsvci, nsei; + + if (!msg) + return -ENOMEM; + + nsvci = htons(nsvc->nsvci); + nsei = htons(nsvc->nsei); + + msg->l2h = msgb_put(msg, sizeof(*nsh)); + nsh = (struct gprs_ns_hdr *) msg->l2h; + + nsh->pdu_type = NS_PDUT_RESET_ACK; + + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n", + nsvc->nsei, nsvc->nsvci); + + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci); + msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei); + + return gprs_ns_tx(nsvc, msg); +} + +/* Section 9.2.10: transmit side / NS-UNITDATA-REQUEST primitive */ +int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg) +{ + struct gprs_nsvc *nsvc; + struct gprs_ns_hdr *nsh; + uint16_t bvci = msgb_bvci(msg); + + nsvc = nsvc_by_nsei(nsi, msgb_nsei(msg)); + if (!nsvc) { + LOGP(DNS, LOGL_ERROR, "Unable to resolve NSEI %u " + "to NS-VC!\n", msgb_nsei(msg)); + return -EINVAL; + } + + if (!(nsvc->state & NSE_S_ALIVE)) { + LOGP(DNS, LOGL_ERROR, "NSEI=%u is not alive, cannot send\n", + nsvc->nsei); + return -EBUSY; + } + if (nsvc->state & NSE_S_BLOCKED) { + LOGP(DNS, LOGL_ERROR, "NSEI=%u is blocked, cannot send\n", + nsvc->nsei); + return -EBUSY; + } + + msg->l2h = msgb_push(msg, sizeof(*nsh) + 3); + nsh = (struct gprs_ns_hdr *) msg->l2h; + if (!nsh) { + LOGP(DNS, LOGL_ERROR, "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(nsvc, msg); +} + +/* Section 9.2.10: receive side */ +static int gprs_ns_rx_unitdata(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h; + uint16_t bvci; + + if (nsvc->state & NSE_S_BLOCKED) + return gprs_ns_tx_status(nsvc, NS_CAUSE_NSVC_BLOCKED, + 0, msg); + + /* spare octet in data[0] */ + bvci = nsh->data[1] << 8 | nsh->data[2]; + msgb_bssgph(msg) = &nsh->data[3]; + msgb_bvci(msg) = bvci; + + /* call upper layer (BSSGP) */ + return nsvc->nsi->cb(GPRS_NS_EVT_UNIT_DATA, nsvc, msg, bvci); +} + +/* Section 9.2.7 */ +static int gprs_ns_rx_status(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct tlv_parsed tp; + uint8_t cause; + int rc; + + LOGP(DNS, LOGL_INFO, "NSEI=%u NS STATUS ", nsvc->nsei); + + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0); + + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE)) { + LOGPC(DNS, LOGL_INFO, "missing cause IE\n"); + return -EINVAL; + } + + cause = *TLVP_VAL(&tp, NS_IE_CAUSE); + LOGPC(DNS, LOGL_INFO, "cause=%s\n", gprs_ns_cause_str(cause)); + + return 0; +} + +/* Section 7.3 */ +static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct tlv_parsed tp; + uint8_t *cause; + uint16_t *nsvci, *nsei; + int rc; + + 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)) { + LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg); + return -EINVAL; + } + + cause = (uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE); + nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI); + nsei = (uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI); + + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET (NSVCI=%u, cause=%s)\n", + nsvc->nsvci, nsvc->nsei, gprs_ns_cause_str(*cause)); + + /* Mark NS-VC as blocked and alive */ + nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; + + nsvc->nsei = ntohs(*nsei); + nsvc->nsvci = ntohs(*nsvci); + + /* start the test procedure */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); + + /* inform interested parties about the fact that this NSVC + * has received RESET */ + ns_dispatch_signal(nsvc, S_NS_RESET, *cause); + + return gprs_ns_tx_reset_ack(nsvc); +} + +static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct tlv_parsed tp; + uint8_t *cause; + int rc; + + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK\n", nsvc->nsei); + + nsvc->state |= NSE_S_BLOCKED; + + 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)) { + LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg); + return -EINVAL; + } + + cause = (uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE); + //nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI); + + ns_dispatch_signal(nsvc, S_NS_BLOCK, *cause); + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); + + return gprs_ns_tx_simple(nsvc, NS_PDUT_BLOCK_ACK); +} + +/* main entry point, here incoming NS frames enter */ +int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, + struct sockaddr_in *saddr) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct gprs_nsvc *nsvc; + int rc = 0; + + /* look up the NSVC based on source address */ + nsvc = nsvc_by_rem_addr(nsi, saddr); + if (!nsvc) { + struct tlv_parsed tp; + uint16_t nsei; + /* Only the RESET procedure creates a new NSVC */ + if (nsh->pdu_type != NS_PDUT_RESET) { + /* Since we have no NSVC, we have to use a fake */ + nsvc = nsi->unknown_nsvc; + LOGP(DNS, LOGL_INFO, "Rejecting NS PDU type 0x%0x " + "from %s:%u for non-existing NS-VC\n", + nsh->pdu_type, inet_ntoa(saddr->sin_addr), + ntohs(saddr->sin_port)); + nsvc->nsvci = nsvc->nsei = 0xfffe; + nsvc->ip.bts_addr = *saddr; + nsvc->state = NSE_S_ALIVE; + return gprs_ns_tx_status(nsvc, + NS_CAUSE_PDU_INCOMP_PSTATE, 0, + msg); + } + 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)) { + LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, + msg); + return -EINVAL; + } + nsei = ntohs(*(uint16_t *)TLVP_VAL(&tp, NS_IE_NSEI)); + /* Check if we already know this NSEI, the remote end might + * simply have changed addresses, or it is a SGSN */ + nsvc = nsvc_by_nsei(nsi, nsei); + if (!nsvc) { + LOGP(DNS, LOGL_INFO, "Creating NS-VC for BSS at %s:%u\n", + inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); + nsvc = nsvc_create(nsi, 0xffff); + } + /* Update the remote peer IP address/port */ + nsvc->ip.bts_addr = *saddr; + } else + msgb_nsei(msg) = nsvc->nsei; + + /* Increment number of Incoming bytes */ + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_IN]); + rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_IN], msgb_l2len(msg)); + + switch (nsh->pdu_type) { + case NS_PDUT_ALIVE: + /* If we're dead and blocked and suddenly receive a + * NS-ALIVE out of the blue, we might have been re-started + * and should send a NS-RESET to make sure everything recovers + * fine. */ + if (nsvc->state == NSE_S_BLOCKED) + rc = gprs_ns_tx_reset(nsvc, NS_CAUSE_PDU_INCOMP_PSTATE); + else + rc = gprs_ns_tx_alive_ack(nsvc); + break; + case NS_PDUT_ALIVE_ACK: + /* stop Tns-alive */ + bsc_del_timer(&nsvc->timer); + /* start Tns-test */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST); + if (nsvc->remote_end_is_sgsn) { + /* FIXME: this should be one level higher */ + if (nsvc->state & NSE_S_BLOCKED) + rc = gprs_ns_tx_unblock(nsvc); + } + break; + case NS_PDUT_UNITDATA: + /* actual user data */ + rc = gprs_ns_rx_unitdata(nsvc, msg); + break; + case NS_PDUT_STATUS: + rc = gprs_ns_rx_status(nsvc, msg); + break; + case NS_PDUT_RESET: + rc = gprs_ns_rx_reset(nsvc, msg); + break; + case NS_PDUT_RESET_ACK: + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET ACK\n", nsvc->nsei); + /* mark NS-VC as blocked + active */ + nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; + nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); + if (nsvc->remote_end_is_sgsn) { + /* stop RESET timer */ + bsc_del_timer(&nsvc->timer); + /* Initiate TEST proc.: Send ALIVE and start timer */ + rc = gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); + } + break; + case NS_PDUT_UNBLOCK: + /* Section 7.2: unblocking procedure */ + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK\n", nsvc->nsei); + nsvc->state &= ~NSE_S_BLOCKED; + ns_dispatch_signal(nsvc, S_NS_UNBLOCK, 0); + rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); + break; + case NS_PDUT_UNBLOCK_ACK: + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK ACK\n", nsvc->nsei); + /* mark NS-VC as unblocked + active */ + nsvc->state = NSE_S_ALIVE; + nsvc->remote_state = NSE_S_ALIVE; + ns_dispatch_signal(nsvc, S_NS_UNBLOCK, 0); + break; + case NS_PDUT_BLOCK: + rc = gprs_ns_rx_block(nsvc, msg); + break; + case NS_PDUT_BLOCK_ACK: + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK ACK\n", nsvc->nsei); + /* mark remote NS-VC as blocked + active */ + nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; + break; + default: + LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx Unknown NS PDU type 0x%02x\n", + nsvc->nsei, nsh->pdu_type); + rc = -EINVAL; + break; + } + return rc; +} + +struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb) +{ + struct gprs_ns_inst *nsi = talloc_zero(tall_bsc_ctx, struct gprs_ns_inst); + + nsi->cb = cb; + INIT_LLIST_HEAD(&nsi->gprs_nsvcs); + nsi->timeout[NS_TOUT_TNS_BLOCK] = 3; + nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES] = 3; + nsi->timeout[NS_TOUT_TNS_RESET] = 3; + nsi->timeout[NS_TOUT_TNS_RESET_RETRIES] = 3; + nsi->timeout[NS_TOUT_TNS_TEST] = 30; + nsi->timeout[NS_TOUT_TNS_ALIVE] = 3; + nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES] = 10; + + /* Create the dummy NSVC that we use for sending + * messages to non-existant/unknown NS-VC's */ + nsi->unknown_nsvc = nsvc_create(nsi, 0xfffe); + llist_del(&nsi->unknown_nsvc->list); + + return nsi; +} + +void gprs_ns_destroy(struct gprs_ns_inst *nsi) +{ + /* FIXME: clear all timers */ + + /* recursively free the NSI and all its NSVCs */ + talloc_free(nsi); +} + + +/* NS-over-IP code, according to 3GPP TS 48.016 Chapter 6.2 + * We don't support Size Procedure, Configuration Procedure, ChangeWeight Procedure */ + +/* Read a single NS-over-IP message */ +static struct msgb *read_nsip_msg(struct bsc_fd *bfd, int *error, + struct sockaddr_in *saddr) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Abis/IP/GPRS-NS"); + int ret = 0; + socklen_t saddr_len = sizeof(*saddr); + + if (!msg) { + *error = -ENOMEM; + return NULL; + } + + ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, + (struct sockaddr *)saddr, &saddr_len); + if (ret < 0) { + LOGP(DNS, LOGL_ERROR, "recv error %s during NSIP recv\n", + strerror(errno)); + msgb_free(msg); + *error = ret; + return NULL; + } else if (ret == 0) { + msgb_free(msg); + *error = ret; + return NULL; + } + + msg->l2h = msg->data; + msgb_put(msg, ret); + + return msg; +} + +static int handle_nsip_read(struct bsc_fd *bfd) +{ + int error; + struct sockaddr_in saddr; + struct gprs_ns_inst *nsi = bfd->data; + struct msgb *msg = read_nsip_msg(bfd, &error, &saddr); + + if (!msg) + return error; + + error = gprs_ns_rcvmsg(nsi, msg, &saddr); + + msgb_free(msg); + + return error; +} + +static int handle_nsip_write(struct bsc_fd *bfd) +{ + /* FIXME: actually send the data here instead of nsip_sendmsg() */ + return -EIO; +} + +int nsip_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + int rc; + struct gprs_ns_inst *nsi = nsvc->nsi; + struct sockaddr_in *daddr = &nsvc->ip.bts_addr; + + rc = sendto(nsi->nsip.fd.fd, msg->data, msg->len, 0, + (struct sockaddr *)daddr, sizeof(*daddr)); + + talloc_free(msg); + + return rc; +} + +/* UDP Port 23000 carries the LLC-in-BSSGP-in-NS protocol stack */ +static int nsip_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int rc = 0; + + if (what & BSC_FD_READ) + rc = handle_nsip_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_nsip_write(bfd); + + return rc; +} + +extern int make_sock(struct bsc_fd *bfd, int proto, uint16_t port, + int (*cb)(struct bsc_fd *fd, unsigned int what)); + +/* Listen for incoming GPRS packets */ +int nsip_listen(struct gprs_ns_inst *nsi, uint16_t udp_port) +{ + int ret; + + ret = make_sock(&nsi->nsip.fd, IPPROTO_UDP, udp_port, nsip_fd_cb); + if (ret < 0) + return ret; + + nsi->ll = GPRS_NS_LL_UDP; + nsi->nsip.fd.data = nsi; + + return ret; +} + +/* Establish a connection (from the BSS) to the SGSN */ +struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi, + struct sockaddr_in *dest, uint16_t nsei, + uint16_t nsvci) +{ + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_rem_addr(nsi, dest); + if (!nsvc) + nsvc = nsvc_create(nsi, nsvci); + nsvc->ip.bts_addr = *dest; + nsvc->nsei = nsei; + nsvc->nsvci = nsvci; + nsvc->remote_end_is_sgsn = 1; + + /* Initiate a RESET procedure */ + /* Mark NS-VC locally as blocked and dead */ + nsvc->state = NSE_S_BLOCKED; + /* Send NS-RESET PDU */ + if (gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION) < 0) { + LOGP(DNS, LOGL_ERROR, "NSEI=%u, error resetting NS-VC\n", + nsei); + } + /* Start Tns-reset */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); + + return nsvc; +} + + diff --git a/openbsc/src/gprs/gprs_ns_vty.c b/openbsc/src/gprs/gprs_ns_vty.c new file mode 100644 index 000000000..8f0628afd --- /dev/null +++ b/openbsc/src/gprs/gprs_ns_vty.c @@ -0,0 +1,292 @@ +/* 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 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 <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <stdint.h> + +#include <arpa/inet.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> +#include <osmocore/rate_ctr.h> +#include <openbsc/debug.h> +#include <openbsc/signal.h> +#include <openbsc/gprs_ns.h> +#include <openbsc/gprs_bssgp.h> + +#include <vty/vty.h> +#include <vty/command.h> + +static struct gprs_ns_inst *vty_nsi = NULL; + +/* FIXME: this should go to some common file as it is copied + * in vty_interface.c of the BSC */ +static const struct value_string gprs_ns_timer_strs[] = { + { 0, "tns-block" }, + { 1, "tns-block-retries" }, + { 2, "tns-reset" }, + { 3, "tns-reset-retries" }, + { 4, "tns-test" }, + { 5, "tns-alive" }, + { 6, "tns-alive-retries" }, + { 0, NULL } +}; + +static struct cmd_node ns_node = { + NS_NODE, + "%s(ns)#", + 1, +}; + +static int config_write_ns(struct vty *vty) +{ + struct gprs_nsvc *nsvc; + unsigned int i; + + 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); + if (nsvc->nsi->ll == GPRS_NS_LL_UDP) { + 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); + } + } + + 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); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ns, cfg_ns_cmd, + "ns", + "Configure the GPRS Network Service") +{ + vty->node = NS_NODE; + return CMD_SUCCESS; +} + +static void dump_ns(struct vty *vty, struct gprs_ns_inst *nsi, int stats) +{ + struct gprs_nsvc *nsvc; + + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (nsvc == nsi->unknown_nsvc) + continue; + 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->nsi->ll == GPRS_NS_LL_UDP) + vty_out(vty, ", %15s:%u", + 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); + } +} + +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; +} + +#define NSE_CMD_STR "NS Entity\n" "NS Entity ID (NSEI)\n" + +DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd, + "nse <0-65535> nsvci <0-65534>", + NSE_CMD_STR + "NS Virtual Connection\n" + "NS Virtual Connection ID (NSVCI)\n" + ) +{ + uint16_t nsei = atoi(argv[0]); + uint16_t nsvci = atoi(argv[1]); + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_nsei(vty_nsi, nsei); + if (!nsvc) { + nsvc = nsvc_create(vty_nsi, nsvci); + nsvc->nsei = nsei; + } + nsvc->nsvci = nsvci; + /* All NSVCs that are explicitly configured by VTY are + * marked as persistent so we can write them to the config + * file at some later point */ + nsvc->persistent = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_nse_remoteip, cfg_nse_remoteip_cmd, + "nse <0-65535> remote-ip A.B.C.D", + NSE_CMD_STR + "Remote IP Address\n" + "Remote IP Address\n") +{ + uint16_t nsei = atoi(argv[0]); + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_nsei(vty_nsi, nsei); + if (!nsvc) { + vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); + return CMD_WARNING; + } + inet_aton(argv[1], &nsvc->ip.bts_addr.sin_addr); + + return CMD_SUCCESS; + +} + +DEFUN(cfg_nse_remoteport, cfg_nse_remoteport_cmd, + "nse <0-65535> remote-port <0-65535>", + NSE_CMD_STR + "Remote UDP Port\n" + "Remote UDP Port Number\n") +{ + uint16_t nsei = atoi(argv[0]); + uint16_t port = atoi(argv[1]); + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_nsei(vty_nsi, nsei); + if (!nsvc) { + vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); + return CMD_WARNING; + } + + nsvc->ip.bts_addr.sin_port = htons(port); + + return CMD_SUCCESS; +} + +DEFUN(cfg_nse_remoterole, cfg_nse_remoterole_cmd, + "nse <0-65535> remote-role (sgsn|bss)", + NSE_CMD_STR + "Remote NSE Role\n" + "Remote Peer is SGSN\n" + "Remote Peer is BSS\n") +{ + uint16_t nsei = atoi(argv[0]); + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_nsei(vty_nsi, nsei); + if (!nsvc) { + vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[1], "sgsn")) + nsvc->remote_end_is_sgsn = 1; + else + nsvc->remote_end_is_sgsn = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_nse, cfg_no_nse_cmd, + "no nse <0-65535>", + "Delete NS Entity\n" + "Delete " NSE_CMD_STR) +{ + uint16_t nsei = atoi(argv[0]); + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_nsei(vty_nsi, nsei); + if (!nsvc) { + vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); + return CMD_WARNING; + } + + nsvc_delete(nsvc); + + 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; +} + +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(CONFIG_NODE, &cfg_ns_cmd); + install_node(&ns_node, config_write_ns); + install_default(NS_NODE); + install_element(NS_NODE, &cfg_nse_nsvci_cmd); + install_element(NS_NODE, &cfg_nse_remoteip_cmd); + install_element(NS_NODE, &cfg_nse_remoteport_cmd); + install_element(NS_NODE, &cfg_nse_remoterole_cmd); + install_element(NS_NODE, &cfg_no_nse_cmd); + install_element(NS_NODE, &cfg_ns_timer_cmd); + + return 0; +} diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c new file mode 100644 index 000000000..ba4671955 --- /dev/null +++ b/openbsc/src/gprs/gprs_sgsn.c @@ -0,0 +1,96 @@ +/* GPRS SGSN functionality */ + +/* (C) 2009 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 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 <stdint.h> + +#include <osmocore/linuxlist.h> +#include <osmocore/talloc.h> +#include <osmocore/timer.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/debug.h> +#include <openbsc/gprs_sgsn.h> +#include <openbsc/gprs_ns.h> +#include <openbsc/gprs_bssgp.h> + +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(uint32_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(uint32_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(uint32_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/gprs/gprs_sndcp.c b/openbsc/src/gprs/gprs_sndcp.c new file mode 100644 index 000000000..0d1a39004 --- /dev/null +++ b/openbsc/src/gprs/gprs_sndcp.c @@ -0,0 +1,70 @@ +/* GPRS SNDCP protocol implementation as per 3GPP TS 04.65 */ + +/* (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 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 <errno.h> +#include <stdint.h> + +#include <osmocore/msgb.h> +#include <osmocore/linuxlist.h> +#include <osmocore/timer.h> +#include <osmocore/talloc.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/debug.h> +#include <openbsc/gprs_bssgp.h> +#include <openbsc/gprs_llc.h> + +/* Chapter 7.2: SN-PDU Formats */ +struct sndcp_common_hdr { + /* octet 1 */ + uint8_t nsapi:4; + uint8_t more:1; + uint8_t type:1; + uint8_t first:1; + uint8_t spare:1; + /* octet 2 */ + uint8_t pcomp; + uint8_t dcomp; +}; + +struct sndcp_udata_hdr { + /* octet 3 */ + uint8_t npdu_high:4; + uint8_t seg_nr:4; + /* octet 4 */ + uint8_t npdu_low; +}; + +/* Entry point for the LL-UNITDATA.indication */ +int sndcp_unitdata_ind(struct msgb *msg, uint8_t sapi, uint8_t *hdr, uint8_t len) +{ + struct sndcp_udata_hdr *suh; + uint16_t npdu; + + if (suh->type == 0) { + LOGP(DGPRS, LOGL_ERROR, "SN-DATA PDU at unitdata_ind() function\n"); + return -EINVAL; + } + + npdu = (suh->npdu_high << 8) | suh->npdu_low; +} + diff --git a/openbsc/src/gprs/gsm_04_08_gprs.c b/openbsc/src/gprs/gsm_04_08_gprs.c new file mode 100644 index 000000000..4a42113f0 --- /dev/null +++ b/openbsc/src/gprs/gsm_04_08_gprs.c @@ -0,0 +1,762 @@ +/* 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-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 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <openbsc/db.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/signal.h> +#include <osmocore/talloc.h> + +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/gsm_04_08.h> +#include <openbsc/gsm_04_08_gprs.h> +#include <openbsc/paging.h> +#include <openbsc/transaction.h> +#include <openbsc/gprs_bssgp.h> +#include <openbsc/gprs_llc.h> +#include <openbsc/gprs_sgsn.h> + +/* 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(uint8_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(uint8_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"; +} + +/* Send a message through the underlying layer */ +static int gsm48_gmm_sendmsg(struct msgb *msg, int command) +{ + /* caller needs to provide TLLI, BVCI and NSEI */ + return gprs_llc_tx_ui(msg, GPRS_SAPI_GMM, command); +} + +/* copy identifiers from old message to new message, this + * is required so lower layers can route it correctly */ +static void gmm_copy_id(struct msgb *msg, const struct msgb *old) +{ + msgb_tlli(msg) = msgb_tlli(old); + msgb_bvci(msg) = msgb_bvci(old); + msgb_nsei(msg) = msgb_nsei(old); +} + +static struct gsm48_qos default_qos = { + .delay_class = 4, /* best effort */ + .reliab_class = GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT, + .peak_tput = GSM48_QOS_PEAK_TPUT_32000bps, + .preced_class = GSM48_QOS_PC_NORMAL, + .mean_tput = GSM48_QOS_MEAN_TPUT_BEST_EFFORT, + .traf_class = GSM48_QOS_TC_INTERACTIVE, + .deliv_order = GSM48_QOS_DO_UNORDERED, + .deliv_err_sdu = GSM48_QOS_ERRSDU_YES, + .max_sdu_size = GSM48_QOS_MAXSDU_1520, + .max_bitrate_up = GSM48_QOS_MBRATE_63k, + .max_bitrate_down = GSM48_QOS_MBRATE_63k, + .resid_ber = GSM48_QOS_RBER_5e_2, + .sdu_err_ratio = GSM48_QOS_SERR_1e_2, + .handling_prio = 3, + .xfer_delay = 0x10, /* 200ms */ + .guar_bitrate_up = GSM48_QOS_MBRATE_0k, + .guar_bitrate_down = GSM48_QOS_MBRATE_0k, + .sig_ind = 0, /* not optimised for signalling */ + .max_bitrate_down_ext = 0, /* use octet 9 */ + .guar_bitrate_down_ext = 0, /* use octet 13 */ +}; + +/* 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; + struct gprs_ra_id ra_id; + + DEBUGP(DMM, "<- GPRS ATTACH ACCEPT\n"); + + gmm_copy_id(msg, old_msg); + + 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 */ + bssgp_parse_cell_id(&ra_id, msgb_bcid(old_msg)); + gsm48_construct_ra(aa->ra_id.digits, &ra_id); + + /* 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, uint8_t gmm_cause) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + DEBUGP(DMM, "<- GPRS ATTACH REJECT\n"); + + gmm_copy_id(msg, old_msg); + + 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, uint8_t id_type) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + DEBUGP(DMM, "-> GPRS IDENTITY REQUEST: mi_type=%02x\n", id_type); + + gmm_copy_id(msg, old_msg); + + 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 *) msgb_gmmh(msg); + uint8_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); + + bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); + ctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id); + if (!ctx) { + DEBUGP(DMM, "from unknown TLLI 0x%08x?!?\n", msgb_tlli(msg)); + 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 *) msgb_gmmh(msg); + uint8_t *cur = gh->data, *msnc, *mi, *old_ra_info; + uint8_t msnc_len, att_type, mi_len, mi_type; + uint16_t drx_par; + uint32_t tmsi; + char mi_string[GSM48_MI_SIZE]; + struct gprs_ra_id ra_id; + uint16_t cid; + 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. */ + + cid = bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); + + /* 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 = msgb_tlli(msg); + 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(msgb_tlli(msg), &ra_id); + /* FIXME: Start some timer */ + ctx->mm_state = GMM_COMMON_PROC_INIT; + ctx->tlli = msgb_tlli(msg); + } + break; + default: + return 0; + } + /* Update MM Context with currient RA and Cell ID */ + ctx->ra = ra_id; + ctx->cell_id = cid; + + /* 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; + struct gprs_ra_id ra_id; + + DEBUGP(DMM, "<- ROUTING AREA UPDATE ACCEPT\n"); + + gmm_copy_id(msg, old_msg); + + 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; + + bssgp_parse_cell_id(&ra_id, msgb_bcid(old_msg)); + gsm48_construct_ra(rua->ra_id.digits, &ra_id); + + /* 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, uint8_t cause) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + DEBUGP(DMM, "<- ROUTING AREA UPDATE REJECT\n"); + + gmm_copy_id(msg, old_msg); + + 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 *) msgb_gmmh(msg); + struct sgsn_mm_ctx *mmctx; + uint8_t *cur = gh->data; + struct gprs_ra_id old_ra_id; + uint8_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(msgb_tlli(msg), &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); + } + + /* Update the MM context with the new RA-ID */ + bssgp_parse_cell_id(&mmctx->ra, msgb_bcid(msg)); + /* Update the MM context with the new TLLI */ + mmctx->tlli = msgb_tlli(msg); + /* 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 *) msgb_gmmh(msg); + 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; +} + +static void msgb_put_pdp_addr_ipv4(struct msgb *msg, uint32_t ipaddr) +{ + uint8_t v[6]; + + v[0] = PDP_TYPE_ORG_IETF; + v[1] = PDP_TYPE_N_IETF_IPv4; + *(uint32_t *)(v+2) = htonl(ipaddr); + + msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v); +} + +static void msgb_put_pdp_addr_ppp(struct msgb *msg) +{ + uint8_t v[2]; + + v[0] = PDP_TYPE_ORG_ETSI; + v[1] = PDP_TYPE_N_ETSI_PPP; + + msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v); +} + +/* 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 *) msgb_gmmh(old_msg); + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_act_pdp_ctx_ack *act_ack; + struct gsm48_hdr *gh; + uint8_t transaction_id = ((old_gh->proto_discr >> 4) ^ 0x8); /* flip */ + + DEBUGP(DMM, "<- ACTIVATE PDP CONTEXT ACK\n"); + + gmm_copy_id(msg, old_msg); + + 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; + + /* Negotiated LLC SAPI */ + msgb_v_put(msg, req->req_llc_sapi); + /* copy QoS parameters from original request */ + msgb_lv_put(msg, sizeof(default_qos), (uint8_t *)&default_qos); + /* Radio priority 10.5.7.2 */ + msgb_v_put(msg, 4); + /* PDP address */ + msgb_put_pdp_addr_ipv4(msg, 0x01020304); + /* Optional: Protocol configuration options */ + /* Optional: Packet Flow Identifier */ + + 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 *) msgb_gmmh(old_msg); + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + uint8_t transaction_id = ((old_gh->proto_discr >> 4) ^ 0x8); /* flip */ + + DEBUGP(DMM, "<- DEACTIVATE PDP CONTEXT ACK\n"); + + gmm_copy_id(msg, old_msg); + + 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 *) msgb_gmmh(msg); + struct gsm48_act_pdp_ctx_req *act_req = (struct gsm48_act_pdp_ctx_req *) gh->data; + uint8_t *pdp_addr_lv = act_req->data; + uint8_t req_qos_len, req_pdpa_len; + uint8_t *req_qos, *req_pdpa; + struct tlv_parsed tp; + + DEBUGP(DMM, "ACTIVATE PDP CONTEXT REQ: "); + req_qos_len = act_req->data[0]; + req_qos = act_req->data + 1; /* 10.5.6.5 */ + req_pdpa_len = act_req->data[1 + req_qos_len]; + req_pdpa = act_req->data + 1 + req_qos_len + 1; /* 10.5.6.4 */ + + switch (req_pdpa[0] & 0xf) { + case 0x0: + DEBUGPC(DMM, "ETSI "); + break; + case 0x1: + DEBUGPC(DMM, "IETF "); + break; + case 0xf: + DEBUGPC(DMM, "Empty "); + break; + } + + switch (req_pdpa[1]) { + case 0x21: + DEBUGPC(DMM, "IPv4 "); + if (req_pdpa_len >= 6) { + struct in_addr ia; + ia.s_addr = ntohl(*((uint32_t *) (req_pdpa+2))); + DEBUGPC(DMM, "%s ", inet_ntoa(ia)); + } + break; + case 0x57: + DEBUGPC(DMM, "IPv6 "); + if (req_pdpa_len >= 18) { + /* FIXME: print IPv6 address */ + } + break; + default: + DEBUGPC(DMM, "0x%02x ", req_pdpa[1]); + break; + } + + /* FIXME: parse TLV for AP name and protocol config options */ + if (TLVP_PRESENT(&tp, GSM48_IE_GSM_APN)) {} + if (TLVP_PRESENT(&tp, GSM48_IE_GSM_PROTO_CONF_OPT)) {} + + 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 *) msgb_gmmh(msg); + + 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 *) msgb_gmmh(msg); + 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 *) msgb_gmmh(msg); + uint8_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; +} diff --git a/openbsc/src/gprs/osmo_gbproxy.cfg b/openbsc/src/gprs/osmo_gbproxy.cfg new file mode 100644 index 000000000..d51b04a11 --- /dev/null +++ b/openbsc/src/gprs/osmo_gbproxy.cfg @@ -0,0 +1,13 @@ +! +! OpenBSC configuration saved from vty +! ! +! +line vty + no login +! +gbproxy + nsip bss local port 23000 + nsip sgsn remote ip 127.0.0.1 + nsip sgsn remote port 7777 + nsip sgsn nsei 101 + nsip sgsn nsvci 101 diff --git a/openbsc/src/gprs/osmo_sgsn.cfg b/openbsc/src/gprs/osmo_sgsn.cfg new file mode 100644 index 000000000..f39e8536f --- /dev/null +++ b/openbsc/src/gprs/osmo_sgsn.cfg @@ -0,0 +1,9 @@ +! +! OpenBSC configuration saved from vty +! ! +! +line vty + no login +! +sgsn + nsip local port 23000 diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c new file mode 100644 index 000000000..b355fb58d --- /dev/null +++ b/openbsc/src/gprs/sgsn_main.c @@ -0,0 +1,183 @@ +/* GPRS SGSN Implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by On Waves + * 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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include <signal.h> +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <osmocore/talloc.h> +#include <osmocore/select.h> +#include <osmocore/rate_ctr.h> + +#include <openbsc/signal.h> +#include <openbsc/debug.h> +#include <openbsc/telnet_interface.h> +#include <openbsc/vty.h> +#include <openbsc/sgsn.h> +#include <openbsc/gprs_ns.h> +#include <openbsc/gprs_bssgp.h> + +#include "../../bscconfig.h" + +/* this is here for the vty... it will never be called */ +void subscr_put() { abort(); } + +#define _GNU_SOURCE +#include <getopt.h> + +void *tall_bsc_ctx; + +struct gprs_ns_inst *sgsn_nsi; + +const char *openbsc_version = "Osmocom NSIP Proxy " PACKAGE_VERSION; +const char *openbsc_copyright = + "Copyright (C) 2010 Harald Welte and On-Waves\n" + "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\n" + "Dieter Spaar, Andreas Eversberg, Holger Freyther\n\n" + "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n"; + +static char *config_file = "osmo_sgsn.cfg"; +static struct sgsn_config sgcfg; + +/* call-back function for the NS protocol */ +static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, + struct msgb *msg, u_int16_t bvci) +{ + int rc = 0; + + switch (event) { + case GPRS_NS_EVT_UNIT_DATA: + /* hand the message into the BSSGP implementation */ + rc = gprs_bssgp_rcvmsg(msg); + break; + default: + LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event); + if (msg) + talloc_free(msg); + rc = -EIO; + break; + } + return rc; +} + +static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGINT: + dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL); + sleep(1); + exit(0); + break; + case SIGABRT: + /* in case of abort, we want to obtain a talloc report + * and then return to the caller, who will abort the process */ + case SIGUSR1: + talloc_report(tall_vty_ctx, stderr); + talloc_report_full(tall_bsc_ctx, stderr); + break; + case SIGUSR2: + talloc_report_full(tall_vty_ctx, stderr); + break; + default: + break; + } +} + +/* NSI that BSSGP uses when transmitting on NS */ +extern struct gprs_ns_inst *bssgp_nsi; +extern void *tall_msgb_ctx; + +int main(int argc, char **argv) +{ + struct gsm_network dummy_network; + struct log_target *stderr_target; + struct sockaddr_in sin; + int rc; + + tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn"); + tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); + + signal(SIGINT, &signal_handler); + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); + signal(SIGPIPE, SIG_IGN); + + log_init(&log_info); + stderr_target = log_target_create_stderr(); + log_add_target(stderr_target); + log_set_all_filter(stderr_target, 1); + + rate_ctr_init(tall_bsc_ctx); + telnet_init(&dummy_network, 4245); + + sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb); + if (!sgsn_nsi) { + LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); + exit(1); + } + bssgp_nsi = sgcfg.nsi = sgsn_nsi; + gprs_ns_vty_init(bssgp_nsi); + /* FIXME: register signal handler for SS_NS */ + + rc = sgsn_parse_config(config_file, &sgcfg); + if (rc < 0) { + LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n"); + exit(2); + } + + nsip_listen(sgsn_nsi, sgcfg.nsip_listen_port); + + while (1) { + rc = bsc_select_main(0); + if (rc < 0) + exit(3); + } + + exit(0); +} + +struct gsm_network; +int bsc_vty_init(struct gsm_network *dummy) +{ + cmd_init(1); + vty_init(); + + openbsc_vty_add_cmds(); + sgsn_vty_init(); + return 0; +} + diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c new file mode 100644 index 000000000..ec18fcbf9 --- /dev/null +++ b/openbsc/src/gprs/sgsn_vty.c @@ -0,0 +1,146 @@ +/* + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by On-Waves + * 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 <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <osmocore/talloc.h> + +#include <openbsc/debug.h> +#include <openbsc/sgsn.h> +#include <openbsc/gprs_ns.h> + +#include <vty/command.h> +#include <vty/vty.h> + +static struct sgsn_config *g_cfg = NULL; + +static struct cmd_node sgsn_node = { + SGSN_NODE, + "%s(sgsn)#", + 1, +}; + +static int config_write_sgsn(struct vty *vty) +{ + struct in_addr ia; + + vty_out(vty, "sgsn%s", VTY_NEWLINE); + + if (g_cfg->nsip_listen_ip) { + ia.s_addr = htonl(g_cfg->nsip_listen_ip); + vty_out(vty, " nsip local ip %s%s", inet_ntoa(ia), + VTY_NEWLINE); + } + vty_out(vty, " nsip local port %u%s", g_cfg->nsip_listen_port, + VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn", + SHOW_STR "Display information about the SGSN") +{ + /* FIXME: iterate over list of NS-VC's and display their state */ + struct gprs_ns_inst *nsi = g_cfg->nsi; + struct gprs_nsvc *nsvc; + + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + vty_out(vty, "NSEI %5u, NS-VC %5u, %s-mode, %s %s%s", + nsvc->nsei, nsvc->nsvci, + nsvc->remote_end_is_sgsn ? "BSS" : "SGSN", + nsvc->state & NSE_S_ALIVE ? "ALIVE" : "DEAD", + nsvc->state & NSE_S_BLOCKED ? "BLOCKED" : "UNBLOCKED", + VTY_NEWLINE); + if (nsvc->nsi->ll == GPRS_NS_LL_UDP) + vty_out(vty, " remote peer %s:%u%s", + inet_ntoa(nsvc->ip.bts_addr.sin_addr), + ntohs(nsvc->ip.bts_addr.sin_port), VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_sgsn, + cfg_sgsn_cmd, + "sgsn", + "Configure the SGSN") +{ + vty->node = SGSN_NODE; + return CMD_SUCCESS; +} + + +DEFUN(cfg_nsip_local_ip, + cfg_nsip_local_ip_cmd, + "nsip local ip A.B.C.D", + "Set the IP address on which we listen for BSS connects") +{ + struct in_addr ia; + + inet_aton(argv[0], &ia); + g_cfg->nsip_listen_ip = ntohl(ia.s_addr); + + return CMD_SUCCESS; +} + +DEFUN(cfg_nsip_local_port, + cfg_nsip_local_port_cmd, + "nsip local port <0-65534>", + "Set the UDP port on which we listen for BSS connects") +{ + unsigned int port = atoi(argv[0]); + + g_cfg->nsip_listen_port = port; + return CMD_SUCCESS; +} + + + + +int sgsn_vty_init(void) +{ + install_element(VIEW_NODE, &show_sgsn_cmd); + + install_element(CONFIG_NODE, &cfg_sgsn_cmd); + install_node(&sgsn_node, config_write_sgsn); + install_default(SGSN_NODE); + install_element(SGSN_NODE, &cfg_nsip_local_ip_cmd); + install_element(SGSN_NODE, &cfg_nsip_local_port_cmd); + + return 0; +} + +int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg) +{ + int rc; + + g_cfg = cfg; + rc = vty_read_config_file(config_file); + if (rc < 0) { + fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); + return rc; + } + + return 0; +} diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c index b770b52fc..1b3ed2537 100644 --- a/openbsc/src/gsm_04_08_utils.c +++ b/openbsc/src/gsm_04_08_utils.c @@ -36,19 +36,10 @@ #include <openbsc/paging.h> #include <openbsc/signal.h> -#define GSM48_ALLOC_SIZE 1024 -#define GSM48_ALLOC_HEADROOM 128 - /* should ip.access BTS use direct RTP streams between each other (1), * or should OpenBSC always act as RTP relay/proxy in between (0) ? */ int ipacc_rtp_direct = 1; -struct msgb *gsm48_msgb_alloc(void) -{ - return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM, - "GSM 04.08"); -} - int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data; @@ -288,7 +279,7 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr) sig_data.bts = msg->lchan->ts->trx->bts; sig_data.lchan = msg->lchan; - bts->network->stats.paging.completed++; + counter_inc(bts->network->stats.paging.completed); dispatch_signal(SS_PAGING, S_PAGING_SUCCEEDED, &sig_data); diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c index 511ad47e7..3492f1f3b 100644 --- a/openbsc/src/gsm_04_11.c +++ b/openbsc/src/gsm_04_11.c @@ -675,7 +675,7 @@ static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans, GSM411_RP_CAUSE_INV_MAND_INF); return -EIO; } - msg->smsh = tpdu; + msg->l4h = tpdu; DEBUGP(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len)); diff --git a/openbsc/src/gsm_04_80.c b/openbsc/src/gsm_04_80.c index 2112e3f99..ef10b1702 100644 --- a/openbsc/src/gsm_04_80.c +++ b/openbsc/src/gsm_04_80.c @@ -257,7 +257,6 @@ int gsm0480_send_ussd_response(const struct msgb *in_msg, const char *response_t if (((strlen(response_text) * 7) % 8) != 0) response_len += 1; - msg->bts_link = in_msg->bts_link; msg->lchan = in_msg->lchan; /* First put the payload text into the message */ @@ -304,7 +303,6 @@ int gsm0480_send_ussd_reject(const struct msgb *in_msg, struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; - msg->bts_link = in_msg->bts_link; msg->lchan = in_msg->lchan; /* First insert the problem code */ diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c index 176367dc7..a4b321266 100644 --- a/openbsc/src/gsm_data.c +++ b/openbsc/src/gsm_data.c @@ -171,6 +171,10 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) return trx; } +static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 }; +static const uint8_t bts_cell_timer_default[] = + { 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 }; + struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type, u_int8_t tsc, u_int8_t bsic) { @@ -212,6 +216,10 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type, bts->gprs.nsvc[i].bts = bts; bts->gprs.nsvc[i].id = i; } + memcpy(&bts->gprs.nse.timer, bts_nse_timer_default, + sizeof(bts->gprs.nse.timer)); + memcpy(&bts->gprs.cell.timer, bts_cell_timer_default, + sizeof(bts->gprs.cell.timer)); /* create our primary TRX */ bts->c0 = gsm_bts_trx_alloc(bts); @@ -221,6 +229,8 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type, } bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4; + bts->rach_b_thresh = -1; + bts->rach_ldavg_slots = -1; llist_add_tail(&bts->list, &net->bts_list); return bts; @@ -280,6 +290,10 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c net->stats.call.dialled = counter_alloc("net.call.dialled"); net->stats.call.alerted = counter_alloc("net.call.alerted"); net->stats.call.connected = counter_alloc("net.call.connected"); + net->stats.chan.rf_fail = counter_alloc("net.chan.rf_fail"); + net->stats.chan.rll_err = counter_alloc("net.chan.rll_err"); + net->stats.bts.oml_fail = counter_alloc("net.bts.oml_fail"); + net->stats.bts.rsl_fail = counter_alloc("net.bts.rsl_fail"); net->mncc_recv = mncc_recv; @@ -436,33 +450,6 @@ const char *gsm_auth_policy_name(enum gsm_auth_policy policy) return get_value_string(auth_policy_names, policy); } -/* this should not be here but in gsm_04_08... but that creates - in turn a dependency nightmare (abis_nm depending on 04_08, ...) */ -static int gsm48_construct_ra(u_int8_t *buf, const struct gprs_ra_id *raid) -{ - u_int16_t mcc = raid->mcc; - u_int16_t mnc = raid->mnc; - - buf[0] = ((mcc / 100) % 10) | (((mcc / 10) % 10) << 4); - buf[1] = (mcc % 10); - - /* I wonder who came up with the stupidity of encoding the MNC - * differently depending on how many digits its decimal number has! */ - if (mnc < 100) { - buf[1] |= 0xf0; - buf[2] = ((mnc / 10) % 10) | ((mnc % 10) << 4); - } else { - buf[1] |= (mnc % 10) << 4; - buf[2] = ((mnc / 100) % 10) | (((mcc / 10) % 10) << 4); - } - - *(u_int16_t *)(buf+3) = htons(raid->lac); - - buf[5] = raid->rac; - - return 6; -} - void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts) { raid->mcc = bts->network->country_code; @@ -498,6 +485,23 @@ const char *rrlp_mode_name(enum rrlp_mode mode) return get_value_string(rrlp_mode_names, mode); } +static const struct value_string bts_gprs_mode_names[] = { + { BTS_GPRS_NONE, "none" }, + { BTS_GPRS_GPRS, "gprs" }, + { BTS_GPRS_EGPRS, "egprs" }, + { 0, NULL } +}; + +enum bts_gprs_mode bts_gprs_mode_parse(const char *arg) +{ + return get_string_value(bts_gprs_mode_names, arg); +} + +const char *bts_gprs_mode_name(enum bts_gprs_mode mode) +{ + return get_value_string(bts_gprs_mode_names, mode); +} + struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan) { struct gsm_meas_rep *meas_rep; diff --git a/openbsc/src/handover_logic.c b/openbsc/src/handover_logic.c index 7fb0b13e1..b2ffe4616 100644 --- a/openbsc/src/handover_logic.c +++ b/openbsc/src/handover_logic.c @@ -134,6 +134,7 @@ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts) return rc; } + rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); llist_add(&ho->list, &bsc_handovers); /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */ @@ -229,7 +230,7 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) /* update lchan pointer of transaction */ trans_lchan_change(&ho->old_lchan->conn, &new_lchan->conn); - ho->old_lchan->state = LCHAN_S_INACTIVE; + rsl_lchan_set_state(ho->old_lchan, LCHAN_S_INACTIVE); lchan_auto_release(ho->old_lchan); /* do something to re-route the actual speech frames ! */ diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c index 323540f48..721cadd23 100644 --- a/openbsc/src/input/ipaccess.c +++ b/openbsc/src/input/ipaccess.c @@ -1,6 +1,8 @@ /* OpenBSC Abis input driver for ip.access */ /* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves * * All Rights Reserved * @@ -57,7 +59,7 @@ struct ia_e1_handle { static struct ia_e1_handle *e1h; -#define TS1_ALLOC_SIZE 300 +#define TS1_ALLOC_SIZE 900 static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG }; static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK }; @@ -234,6 +236,8 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg, } DEBUGP(DINP, "Identified BTS %u/%u/%u\n", site_id, bts_id, trx_id); if (bfd->priv_nr == PRIV_OML) { + /* drop any old oml connection */ + ipaccess_drop_oml(bts); bts->oml_link = e1inp_sign_link_create(&line->ts[PRIV_OML - 1], E1INP_SIGN_OML, bts->c0, bts->oml_tei, 0); @@ -241,7 +245,18 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg, struct e1inp_ts *e1i_ts; struct bsc_fd *newbfd; struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_id); - + + /* drop any old rsl connection */ + ipaccess_drop_rsl(trx); + + if (!bts->oml_link) { + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + talloc_free(bfd); + return 0; + } + bfd->data = line = bts->oml_link->ts->line; e1i_ts = &line->ts[PRIV_RSL + trx_id - 1]; newbfd = &e1i_ts->driver.ipaccess.fd; @@ -251,19 +266,13 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg, E1INP_SIGN_RSL, trx, trx->rsl_tei, 0); - if (newbfd->fd >= 0) { - LOGP(DINP, LOGL_ERROR, "BTS is still registered. Closing old connection.\n"); - bsc_unregister_fd(newbfd); - close(newbfd->fd); - newbfd->fd = -1; - } - /* get rid of our old temporary bfd */ memcpy(newbfd, bfd, sizeof(*newbfd)); newbfd->priv_nr = PRIV_RSL + trx_id; bsc_unregister_fd(bfd); - bsc_register_fd(newbfd); + bfd->fd = -1; talloc_free(bfd); + bsc_register_fd(newbfd); } break; } @@ -328,6 +337,103 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error) return msg; } +int ipaccess_drop_oml(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + struct e1inp_ts *ts; + struct e1inp_line *line; + struct bsc_fd *bfd; + + if (!bts || !bts->oml_link) + return -1; + + /* send OML down */ + ts = bts->oml_link->ts; + line = ts->line; + e1inp_event(ts, EVT_E1_TEI_DN, bts->oml_link->tei, bts->oml_link->sapi); + + bfd = &ts->driver.ipaccess.fd; + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + + /* clean up OML and RSL */ + e1inp_sign_link_destroy(bts->oml_link); + bts->oml_link = NULL; + bts->ip_access.flags = 0; + + /* drop all RSL connections too */ + llist_for_each_entry(trx, &bts->trx_list, list) + ipaccess_drop_rsl(trx); + + /* kill the E1 line now... as we have no one left to use it */ + talloc_free(line); + + return -1; +} + +static int ipaccess_drop(struct e1inp_ts *ts, struct bsc_fd *bfd) +{ + struct e1inp_sign_link *link; + int bts_nr; + + if (!ts) { + /* + * If we don't have a TS this means that this is a RSL + * connection but we are not past the authentication + * handling yet. So we can safely delete this bfd and + * wait for a reconnect. + */ + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + talloc_free(bfd); + return -1; + } + + /* attempt to find a signalling link */ + if (ts->type == E1INP_TS_TYPE_SIGN) { + llist_for_each_entry(link, &ts->sign.sign_links, list) { + bts_nr = link->trx->bts->bts_nr; + /* we have issues just reconnecting RLS so we drop OML */ + ipaccess_drop_oml(link->trx->bts); + return bts_nr; + } + } + + /* error case */ + LOGP(DINP, LOGL_ERROR, "Failed to find a signalling link for ts: %p\n", ts); + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + return -1; +} + +int ipaccess_drop_rsl(struct gsm_bts_trx *trx) +{ + struct bsc_fd *bfd; + struct e1inp_ts *ts; + + if (!trx || !trx->rsl_link) + return -1; + + /* send RSL down */ + ts = trx->rsl_link->ts; + e1inp_event(ts, EVT_E1_TEI_DN, trx->rsl_link->tei, trx->rsl_link->sapi); + + /* close the socket */ + bfd = &ts->driver.ipaccess.fd; + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + + /* destroy */ + e1inp_sign_link_destroy(trx->rsl_link); + trx->rsl_link = NULL; + + return -1; +} + static int handle_ts1_read(struct bsc_fd *bfd) { struct e1inp_line *line = bfd->data; @@ -341,18 +447,12 @@ static int handle_ts1_read(struct bsc_fd *bfd) msg = ipaccess_read_msg(bfd, &error); if (!msg) { if (error == 0) { - link = e1inp_lookup_sign_link(e1i_ts, IPAC_PROTO_OML, 0); - if (link) { - link->trx->bts->ip_access.flags = 0; + int ret = ipaccess_drop(e1i_ts, bfd); + if (ret >= 0) LOGP(DINP, LOGL_NOTICE, "BTS %u disappeared, dead socket\n", - link->trx->bts->nr); - } else + ret); + else LOGP(DINP, LOGL_NOTICE, "unknown BTS disappeared, dead socket\n"); - e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL); - e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML); - bsc_unregister_fd(bfd); - close(bfd->fd); - bfd->fd = -1; } return error; } @@ -362,13 +462,8 @@ static int handle_ts1_read(struct bsc_fd *bfd) hh = (struct ipaccess_head *) msg->data; if (hh->proto == IPAC_PROTO_IPACCESS) { ret = ipaccess_rcvmsg(line, msg, bfd); - if (ret < 0) { - e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL); - e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML); - bsc_unregister_fd(bfd); - close(bfd->fd); - bfd->fd = -1; - } + if (ret < 0) + ipaccess_drop(e1i_ts, bfd); msgb_free(msg); return ret; } @@ -475,7 +570,9 @@ static int handle_ts1_write(struct bsc_fd *bfd) /* set tx delay timer for next event */ e1i_ts->sign.tx_timer.cb = timeout_ts1_write; e1i_ts->sign.tx_timer.data = e1i_ts; - bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 100); + + /* Reducing this might break the nanoBTS 900 init. */ + bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 100000); return ret; } @@ -505,7 +602,6 @@ static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what) return rc; } - struct e1inp_driver ipaccess_driver = { .name = "ip.access", .want_write = ts_want_write, @@ -611,53 +707,6 @@ 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, - int (*cb)(struct bsc_fd *fd, unsigned int what)) -{ - struct sockaddr_in addr; - int ret, on = 1; - - bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - bfd->cb = cb; - bfd->when = BSC_FD_READ; - //bfd->data = line; - - if (bfd->fd < 0) { - LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n"); - return -EIO; - } - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = INADDR_ANY; - - setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); - if (ret < 0) { - LOGP(DINP, LOGL_ERROR, "could not bind l2 socket %s\n", - strerror(errno)); - close(bfd->fd); - return -EIO; - } - - ret = listen(bfd->fd, 1); - if (ret < 0) { - perror("listen"); - close(bfd->fd); - return ret; - } - - ret = bsc_register_fd(bfd); - if (ret < 0) { - perror("register_listen_fd"); - close(bfd->fd); - return ret; - } - return 0; -} - /* Actively connect to a BTS. Currently used by ipaccess-config.c */ int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa) { @@ -714,12 +763,16 @@ 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; return ret; } diff --git a/openbsc/src/ipaccess/Makefile.am b/openbsc/src/ipaccess/Makefile.am new file mode 100644 index 000000000..533932142 --- /dev/null +++ b/openbsc/src/ipaccess/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) + +sbin_PROGRAMS = ipaccess-find ipaccess-config ipaccess-proxy + +ipaccess_find_SOURCES = ipaccess-find.c + +ipaccess_config_SOURCES = ipaccess-config.c ipaccess-firmware.c +ipaccess_config_LDADD = $(top_builddir)/src/libbsc.a $(top_builddir)/src/libmsc.a \ + $(top_builddir)/src/libbsc.a $(top_builddir)/src/libvty.a -ldl -ldbi $(LIBCRYPT) + +ipaccess_proxy_SOURCES = ipaccess-proxy.c ../debug.c diff --git a/openbsc/src/ipaccess/ipaccess-config.c b/openbsc/src/ipaccess/ipaccess-config.c index 670e3f11a..f27e51fdd 100644 --- a/openbsc/src/ipaccess/ipaccess-config.c +++ b/openbsc/src/ipaccess/ipaccess-config.c @@ -1,8 +1,8 @@ /* ip.access nanoBTS configuration tool */ /* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * (C) 2009 by Holger Hans Peter Freyther - * (C) 2009 by On Waves + * (C) 2009,2010 by Holger Hans Peter Freyther + * (C) 2009,2010 by On Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify @@ -59,6 +59,7 @@ static int sw_load_state = 0; static int oml_state = 0; static int dump_files = 0; static char *firmware_analysis = NULL; +static int found_trx = 0; struct sw_load { u_int8_t file_id[255]; @@ -91,23 +92,23 @@ static int ipacc_msg_nack(u_int8_t mt) return 0; } -static int ipacc_msg_ack(u_int8_t mt, struct gsm_bts *bts) +static void check_restart_or_exit(struct gsm_bts_trx *trx) +{ + if (restart) { + abis_nm_ipaccess_restart(trx); + } else { + exit(0); + } +} + +static int ipacc_msg_ack(u_int8_t mt, struct gsm_bts_trx *trx) { if (sw_load_state == 1) { fprintf(stderr, "The new software is activaed.\n"); - - if (restart) { - abis_nm_ipaccess_restart(bts); - } else { - exit(0); - } + check_restart_or_exit(trx); } else if (oml_state == 1) { fprintf(stderr, "Set the primary OML IP.\n"); - if (restart) { - abis_nm_ipaccess_restart(bts); - } else { - exit(0); - } + check_restart_or_exit(trx); } return 0; @@ -202,7 +203,7 @@ static int nm_sig_cb(unsigned int subsys, unsigned int signal, return ipacc_msg_nack(ipacc_data->msg_type); case S_NM_IPACC_ACK: ipacc_data = signal_data; - return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->bts); + return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->trx); case S_NM_TEST_REP: return test_rep(signal_data); case S_NM_IPACC_RESTART_ACK: @@ -227,12 +228,12 @@ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg, void *data, void *param) { struct msgb *msg; - struct gsm_bts *bts; + struct gsm_bts_trx *trx; if (hook != GSM_HOOK_NM_SWLOAD) return 0; - bts = (struct gsm_bts *) data; + trx = (struct gsm_bts_trx *) data; switch (event) { case NM_MT_LOAD_INIT_ACK: @@ -271,7 +272,7 @@ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg, msg->l2h[1] = msgb_l3len(msg) >> 8; msg->l2h[2] = msgb_l3len(msg) & 0xff; printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg)); - abis_nm_ipaccess_set_nvattr(bts->c0, msg->l2h, msgb_l2len(msg)); + abis_nm_ipaccess_set_nvattr(trx, msg->l2h, msgb_l2len(msg)); msgb_free(msg); break; case NM_MT_LOAD_END_NACK: @@ -285,7 +286,7 @@ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg, case NM_MT_ACTIVATE_SW_ACK: break; case NM_MT_LOAD_SEG_ACK: - percent = abis_nm_software_load_status(bts); + percent = abis_nm_software_load_status(trx->bts); if (percent > percent_old) printf("Software Download Progress: %d%%\n", percent); percent_old = percent; @@ -298,13 +299,13 @@ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg, return 0; } -static void bootstrap_om(struct gsm_bts *bts) +static void bootstrap_om(struct gsm_bts_trx *trx) { int len; static u_int8_t buf[1024]; u_int8_t *cur = buf; - printf("OML link established\n"); + printf("OML link established using TRX %d\n", trx->nr); if (unit_id) { len = strlen(unit_id); @@ -316,7 +317,7 @@ static void bootstrap_om(struct gsm_bts *bts) memcpy(buf+3, unit_id, len); buf[3+len] = 0; printf("setting Unit ID to '%s'\n", unit_id); - abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len+1); + abis_nm_ipaccess_set_nvattr(trx, buf, 3+len+1); } if (prim_oml_ip) { struct in_addr ia; @@ -340,7 +341,7 @@ static void bootstrap_om(struct gsm_bts *bts) *cur++ = 0; printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia)); oml_state = 1; - abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len); + abis_nm_ipaccess_set_nvattr(trx, buf, 3+len); } if (nv_mask) { len = 4; @@ -354,12 +355,12 @@ static void bootstrap_om(struct gsm_bts *bts) *cur++ = nv_mask >> 8; printf("setting NV Flags/Mask to 0x%04x/0x%04x\n", nv_flags, nv_mask); - abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len); + abis_nm_ipaccess_set_nvattr(trx, buf, 3+len); } if (restart && !prim_oml_ip && !software) { printf("restarting BTS\n"); - abis_nm_ipaccess_restart(bts); + abis_nm_ipaccess_restart(trx); } } @@ -370,7 +371,6 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx) case EVT_E1_TEI_UP: switch (type) { case E1INP_SIGN_OML: - bootstrap_om(trx->bts); break; case E1INP_SIGN_RSL: /* FIXME */ @@ -389,22 +389,29 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx) } int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, - struct gsm_nm_state *old_state, struct gsm_nm_state *new_state) + struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, + struct abis_om_obj_inst *obj_inst) { - if (evt == EVT_STATECHG_OPER && + if (obj_class == NM_OC_BASEB_TRANSC) { + if (!found_trx && obj_inst->trx_nr != 0xff) { + struct gsm_bts_trx *trx = container_of(obj, struct gsm_bts_trx, bb_transc); + bootstrap_om(trx); + found_trx = 1; + } + } else if (evt == EVT_STATECHG_OPER && obj_class == NM_OC_RADIO_CARRIER && new_state->availability == 3) { struct gsm_bts_trx *trx = obj; if (net_listen_testnr) { u_int8_t phys_config[] = { 0x02, 0x0a, 0x00, 0x01, 0x02 }; - abis_nm_perform_test(trx->bts, 2, 0, 0, 0xff, + abis_nm_perform_test(trx->bts, 2, 0, trx->nr, 0xff, net_listen_testnr, 1, phys_config, sizeof(phys_config)); } else if (software) { int rc; printf("Attempting software upload with '%s'\n", software); - rc = abis_nm_software_load(trx->bts, software, 19, 0, swload_cbfn, trx->bts); + rc = abis_nm_software_load(trx->bts, trx->nr, software, 19, 0, swload_cbfn, trx); if (rc < 0) { fprintf(stderr, "Failed to start software load\n"); exit(-3); @@ -639,6 +646,7 @@ int main(int argc, char **argv) { "software", 1, 0, 'd' }, { "firmware", 1, 0, 'f' }, { "write-firmware", 0, 0, 'w' }, + { 0, 0, 0, 0 }, }; c = getopt_long(argc, argv, "u:o:rn:l:hs:d:f:w", long_options, diff --git a/openbsc/src/mgcp/mgcp_network.c b/openbsc/src/mgcp/mgcp_network.c index e9c067ebc..1f51233d3 100644 --- a/openbsc/src/mgcp/mgcp_network.c +++ b/openbsc/src/mgcp/mgcp_network.c @@ -151,7 +151,7 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what) */ #warning "Slight spec violation. With connection mode recvonly we should attempt to forward." dest = memcmp(&addr.sin_addr, &endp->remote, sizeof(addr.sin_addr)) == 0 && - (endp->net_rtp == addr.sin_port || endp->net_rtcp == addr.sin_port) + (endp->net_rtp == addr.sin_port || endp->net_rtcp == addr.sin_port) ? DEST_BTS : DEST_NETWORK; proto = fd == &endp->local_rtp ? PROTO_RTP : PROTO_RTCP; diff --git a/openbsc/src/mgcp/mgcp_vty.c b/openbsc/src/mgcp/mgcp_vty.c index b05888284..38d3daf5f 100644 --- a/openbsc/src/mgcp/mgcp_vty.c +++ b/openbsc/src/mgcp/mgcp_vty.c @@ -286,8 +286,8 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg) /* * This application supports two modes. - * 1.) a true MGCP gateway with support for AUEP, CRCX, MDCX, DLCX - * 2.) plain forwarding of RTP packets on the endpoints. + * 1.) a true MGCP gateway with support for AUEP, CRCX, MDCX, DLCX + * 2.) plain forwarding of RTP packets on the endpoints. * both modes are mutual exclusive */ if (g_cfg->forward_ip) { diff --git a/openbsc/src/openbsc.cfg.nanobts b/openbsc/src/openbsc.cfg.nanobts index a1ceaec79..da0ba74e1 100644 --- a/openbsc/src/openbsc.cfg.nanobts +++ b/openbsc/src/openbsc.cfg.nanobts @@ -1,6 +1,6 @@ ! ! OpenBSC configuration saved from vty -! +! ! password foo ! line vty @@ -11,17 +11,52 @@ network mobile network code 1 short name OpenBSC long name OpenBSC + auth policy closed + location updating reject cause 13 + encryption a5 0 + neci 0 + rrlp mode none + mm info 1 + handover 0 + handover window rxlev averaging 10 + handover window rxqual averaging 1 + handover window rxlev neighbor averaging 10 + handover power budget interval 6 + handover power budget hysteresis 3 + handover maximum distance 9999 timer t3101 10 + timer t3103 0 + timer t3105 0 + timer t3107 0 + timer t3109 0 + timer t3111 0 timer t3113 60 + timer t3115 0 + timer t3117 0 + timer t3119 0 + timer t3141 0 bts 0 type nanobts - ip.access unit_id 1801 0 - band GSM1800 + band DCS1800 + cell_identity 0 location_area_code 1 training_sequence_code 7 base_station_id_code 63 + ms max power 15 + cell reselection hysteresis 4 + rxlev access min 0 + channel allocator ascending + rach tx integer 9 + rach max transmission 7 + ip.access unit_id 1801 0 + oml ip.access stream_id 255 + gprs mode none trx 0 + rf_locked 0 arfcn 514 + nominal power 23 + max_power_red 20 + rsl e1 tei 0 timeslot 0 phys_chan_config CCCH+SDCCH4 timeslot 1 diff --git a/openbsc/src/openbsc.cfg.nanobts.multitrx b/openbsc/src/openbsc.cfg.nanobts.multitrx new file mode 100644 index 000000000..6e27ff514 --- /dev/null +++ b/openbsc/src/openbsc.cfg.nanobts.multitrx @@ -0,0 +1,97 @@ +! +! OpenBSC configuration saved from vty +! ! +password foo +! +line vty + no login +! +network + network country code 1 + mobile network code 1 + short name OpenBSC + long name OpenBSC + auth policy closed + location updating reject cause 13 + encryption a5 0 + neci 0 + rrlp mode none + mm info 0 + handover 0 + handover window rxlev averaging 10 + handover window rxqual averaging 1 + handover window rxlev neighbor averaging 10 + handover power budget interval 6 + handover power budget hysteresis 3 + handover maximum distance 9999 + timer t3101 10 + timer t3103 0 + timer t3105 0 + timer t3107 0 + timer t3109 0 + timer t3111 0 + timer t3113 60 + timer t3115 0 + timer t3117 0 + timer t3119 0 + timer t3141 0 + bts 0 + type nanobts + band DCS1800 + cell_identity 0 + location_area_code 1 + training_sequence_code 7 + base_station_id_code 63 + ms max power 15 + cell reselection hysteresis 4 + rxlev access min 0 + channel allocator ascending + rach tx integer 9 + rach max transmission 7 + ip.access unit_id 1800 0 + oml ip.access stream_id 255 + gprs mode none + trx 0 + rf_locked 0 + arfcn 871 + nominal power 23 + max_power_red 0 + rsl e1 tei 0 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + timeslot 1 + phys_chan_config SDCCH8 + timeslot 2 + phys_chan_config TCH/F + timeslot 3 + phys_chan_config TCH/F + timeslot 4 + phys_chan_config TCH/F + timeslot 5 + phys_chan_config TCH/F + timeslot 6 + phys_chan_config TCH/F + timeslot 7 + phys_chan_config TCH/F + trx 1 + rf_locked 0 + arfcn 873 + nominal power 23 + max_power_red 0 + rsl e1 tei 0 + timeslot 0 + phys_chan_config SDCCH8 + timeslot 1 + phys_chan_config TCH/F + timeslot 2 + phys_chan_config TCH/F + timeslot 3 + phys_chan_config TCH/F + timeslot 4 + phys_chan_config TCH/F + timeslot 5 + phys_chan_config TCH/F + timeslot 6 + phys_chan_config TCH/F + timeslot 7 + phys_chan_config TCH/F diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c index 7c3750d66..12ed90341 100644 --- a/openbsc/src/paging.c +++ b/openbsc/src/paging.c @@ -45,6 +45,7 @@ #include <openbsc/signal.h> #include <openbsc/abis_rsl.h> #include <openbsc/gsm_data.h> +#include <openbsc/chan_alloc.h> void *tall_paging_ctx; @@ -70,14 +71,6 @@ static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber * static void paging_remove_request(struct gsm_bts_paging_state *paging_bts, struct gsm_paging_request *to_be_deleted) { - /* Update the last_request if that is necessary */ - if (to_be_deleted == paging_bts->last_request) { - paging_bts->last_request = - (struct gsm_paging_request *)paging_bts->last_request->entry.next; - if (&to_be_deleted->entry == &paging_bts->pending_requests) - paging_bts->last_request = NULL; - } - bsc_del_timer(&to_be_deleted->T3113); llist_del(&to_be_deleted->entry); subscr_put(to_be_deleted->subscr); @@ -90,7 +83,7 @@ static void page_ms(struct gsm_paging_request *request) unsigned int mi_len; unsigned int page_group; - DEBUGP(DPAG, "Going to send paging commands: imsi: '%s' tmsi: '0x%x'\n", + LOGP(DPAG, LOGL_INFO, "Going to send paging commands: imsi: '%s' tmsi: '0x%x'\n", request->subscr->imsi, request->subscr->tmsi); if (request->subscr->tmsi == GSM_RESERVED_TMSI) @@ -103,14 +96,6 @@ static void page_ms(struct gsm_paging_request *request) request->chan_type); } -static void paging_move_to_next(struct gsm_bts_paging_state *paging_bts) -{ - paging_bts->last_request = - (struct gsm_paging_request *)paging_bts->last_request->entry.next; - if (&paging_bts->last_request->entry == &paging_bts->pending_requests) - paging_bts->last_request = NULL; -} - /* * This is kicked by the periodic PAGING LOAD Indicator * coming from abis_rsl.c @@ -128,17 +113,26 @@ static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_b * return then. */ if (llist_empty(&paging_bts->pending_requests)) { - paging_bts->last_request = NULL; /* since the list is empty, no need to reschedule the timer */ return; } - if (!paging_bts->last_request) - paging_bts->last_request = - (struct gsm_paging_request *)paging_bts->pending_requests.next; + /* + * In case the BTS does not provide us with load indication just fill + * up our slots for this round. We should be able to page 20 subscribers + * every two seconds. So we will just give the BTS some extra credit. + * We will have to see how often we run out of this credit, so we might + * need a low watermark and then add credit or give 20 every run when + * the bts sets an option for that. + */ + if (paging_bts->available_slots == 0) { + LOGP(DPAG, LOGL_NOTICE, "No slots available on bts nr %d\n", + paging_bts->bts->nr); + paging_bts->available_slots = 20; + } - assert(paging_bts->last_request); - initial_request = paging_bts->last_request; + initial_request = llist_entry(paging_bts->pending_requests.next, + struct gsm_paging_request, entry); current_request = initial_request; do { @@ -146,21 +140,17 @@ static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_b page_ms(current_request); paging_bts->available_slots--; - /* - * move to the next item. We might wrap around - * this means last_request will be NULL and we just - * call paging_page_to_next again. It it guranteed - * that the list is not empty. - */ - paging_move_to_next(paging_bts); - if (!paging_bts->last_request) - paging_bts->last_request = - (struct gsm_paging_request *)paging_bts->pending_requests.next; - current_request = paging_bts->last_request; + /* take the current and add it to the back */ + llist_del(¤t_request->entry); + llist_add_tail(¤t_request->entry, &paging_bts->pending_requests); + + /* take the next request */ + current_request = llist_entry(paging_bts->pending_requests.next, + struct gsm_paging_request, entry); } while (paging_bts->available_slots > 0 && initial_request != current_request); - bsc_schedule_timer(&paging_bts->work_timer, 1, 0); + bsc_schedule_timer(&paging_bts->work_timer, 2, 0); } static void paging_worker(void *data) @@ -178,7 +168,7 @@ void paging_init(struct gsm_bts *bts) bts->paging.work_timer.data = &bts->paging; /* Large number, until we get a proper message */ - bts->paging.available_slots = 100; + bts->paging.available_slots = 20; } static int paging_pending_request(struct gsm_bts_paging_state *bts, @@ -200,7 +190,7 @@ static void paging_T3113_expired(void *data) void *cbfn_param; gsm_cbfn *cbfn; - DEBUGP(DPAG, "T3113 expired for request %p (%s)\n", + LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n", req, req->subscr->imsi); sig_data.subscr = req->subscr; @@ -208,11 +198,11 @@ static void paging_T3113_expired(void *data) sig_data.lchan = NULL; /* must be destroyed before calling cbfn, to prevent double free */ + counter_inc(req->bts->network->stats.paging.expired); cbfn_param = req->cbfn_param; cbfn = req->cbfn; paging_remove_request(&req->bts->paging, req); - counter_inc(req->bts->network->stats.paging.expired); dispatch_signal(SS_PAGING, S_PAGING_EXPIRED, &sig_data); if (cbfn) @@ -227,11 +217,11 @@ static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr, struct gsm_paging_request *req; if (paging_pending_request(bts_entry, subscr)) { - DEBUGP(DPAG, "Paging request already pending\n"); + LOGP(DPAG, LOGL_INFO, "Paging request already pending for %s\n", subscr->imsi); return -EEXIST; } - DEBUGP(DPAG, "Start paging of subscriber %llu on bts %d.\n", + LOGP(DPAG, LOGL_DEBUG, "Start paging of subscriber %llu on bts %d.\n", subscr->id, bts->nr); req = talloc_zero(tall_paging_ctx, struct gsm_paging_request); req->subscr = subscr_get(subscr); @@ -245,7 +235,7 @@ static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr, llist_add_tail(&req->entry, &bts_entry->pending_requests); if (!bsc_timer_pending(&bts_entry->work_timer)) - bsc_schedule_timer(&bts_entry->work_timer, 1, 0); + bsc_schedule_timer(&bts_entry->work_timer, 2, 0); return 0; } @@ -296,11 +286,11 @@ static void _paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *sub entry) { if (req->subscr == subscr) { if (lchan && req->cbfn) { - DEBUGP(DPAG, "Stop paging on bts %d, calling cbfn.\n", bts->nr); + LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d, calling cbfn.\n", bts->nr); req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, NULL, lchan, req->cbfn_param); } else - DEBUGP(DPAG, "Stop paging on bts %d silently.\n", bts->nr); + LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d silently.\n", bts->nr); paging_remove_request(&bts->paging, req); break; } @@ -328,7 +318,7 @@ void paging_request_stop(struct gsm_bts *_bts, struct gsm_subscriber *subscr, break; /* Stop paging */ - if (bts != _bts) + if (bts != _bts) _paging_request_stop(bts, subscr, NULL); } while (1); } diff --git a/openbsc/src/rest_octets.c b/openbsc/src/rest_octets.c index 16996cec2..039d2c83a 100644 --- a/openbsc/src/rest_octets.c +++ b/openbsc/src/rest_octets.c @@ -133,6 +133,7 @@ static int append_lsa_params(struct bitvec *bv, const struct gsm48_lsa_params *lsa_params) { /* FIXME */ + return -1; } /* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ @@ -318,8 +319,31 @@ static int append_gprs_cell_opt(struct bitvec *bv, /* hard-code no PAN_{DEC,INC,MAX} */ bitvec_set_bit(bv, 0); - /* no extension information (EDGE) */ - bitvec_set_bit(bv, 0); + if (!gco->ext_info_present) { + /* no extension information */ + bitvec_set_bit(bv, 0); + } else { + /* extension information */ + bitvec_set_bit(bv, 1); + if (!gco->ext_info.egprs_supported) { + /* 6bit length of extension */ + bitvec_set_uint(bv, (1 + 3)-1, 6); + /* EGPRS supported in the cell */ + bitvec_set_bit(bv, 0); + } else { + /* 6bit length of extension */ + bitvec_set_uint(bv, (1 + 5 + 3)-1, 6); + /* EGPRS supported in the cell */ + bitvec_set_bit(bv, 1); + /* 1bit EGPRS PACKET CHANNEL REQUEST */ + bitvec_set_bit(bv, gco->ext_info.use_egprs_p_ch_req); + /* 4bit BEP PERIOD */ + bitvec_set_uint(bv, gco->ext_info.bep_period, 4); + } + bitvec_set_bit(bv, gco->ext_info.pfc_supported); + bitvec_set_bit(bv, gco->ext_info.dtm_supported); + bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination); + } return 0; } @@ -334,7 +358,7 @@ static void append_gprs_pwr_ctrl_pars(struct bitvec *bv, bitvec_set_uint(bv, pcp->n_avg_i, 4); } -/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */ +/* Generate SI13 Rest Octests (04.08 Chapter 10.5.2.37b) */ int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13) { struct bitvec bv; @@ -390,6 +414,11 @@ int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13) break; } } + /* 3GPP TS 44.018 Release 6 / 10.5.2.37b */ + bitvec_set_bit(&bv, H); /* added Release 99 */ + /* claim our SGSN is compatible with Release 99, as EDGE and EGPRS + * was only added in this Release */ + bitvec_set_bit(&bv, 1); } bitvec_spare_padding(&bv, (bv.data_len*8)-1); return bv.data_len; diff --git a/openbsc/src/rtp_proxy.c b/openbsc/src/rtp_proxy.c index 375204e97..924173dd2 100644 --- a/openbsc/src/rtp_proxy.c +++ b/openbsc/src/rtp_proxy.c @@ -91,9 +91,6 @@ struct rtp_x_hdr { #define RTP_VERSION 2 -#define RTP_PT_GSM_FULL 3 -#define RTP_PT_GSM_EFR 97 - /* decode an rtp frame and create a new buffer with payload */ static int rtp_decode(struct msgb *msg, u_int32_t callref, struct msgb **data) { diff --git a/openbsc/src/sccp/sccp.c b/openbsc/src/sccp/sccp.c index e0fd02e0e..de18614c3 100644 --- a/openbsc/src/sccp/sccp.c +++ b/openbsc/src/sccp/sccp.c @@ -81,7 +81,7 @@ static struct sccp_data_callback *_find_ssn(u_int8_t ssn) /* need to add one */ cb = talloc_zero(tall_sccp_ctx, struct sccp_data_callback); if (!cb) { - DEBUGP(DSCCP, "Failed to allocate sccp callback.\n"); + LOGP(DSCCP, LOGL_ERROR, "Failed to allocate sccp callback.\n"); return NULL; } @@ -108,13 +108,13 @@ static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb u_int8_t length; if (room <= 0) { - DEBUGP(DSCCP, "Not enough room for an address: %u\n", room); + LOGP(DSCCP, LOGL_ERROR, "Not enough room for an address: %u\n", room); return -1; } length = msgb->l2h[offset]; if (room <= length) { - DEBUGP(DSCCP, "Not enough room for optional data %u %u\n", room, length); + LOGP(DSCCP, LOGL_ERROR, "Not enough room for optional data %u %u\n", room, length); return -1; } @@ -122,7 +122,7 @@ static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb party = (struct sccp_called_party_address *)(msgb->l2h + offset + 1); if (party->point_code_indicator) { if (length <= read + 2) { - DEBUGP(DSCCP, "POI does not fit %u\n", length); + LOGP(DSCCP, LOGL_ERROR, "POI does not fit %u\n", length); return -1; } @@ -133,7 +133,7 @@ static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb if (party->ssn_indicator) { if (length <= read + 1) { - DEBUGP(DSCCP, "SSN does not fit %u\n", length); + LOGP(DSCCP, LOGL_ERROR, "SSN does not fit %u\n", length); return -1; } @@ -142,7 +142,7 @@ static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb } if (party->global_title_indicator) { - DEBUGP(DSCCP, "GTI not supported %u\n", *(u_int8_t *)party); + LOGP(DSCCP, LOGL_ERROR, "GTI not supported %u\n", *(u_int8_t *)party); return -1; } @@ -156,7 +156,8 @@ static int check_address(struct sccp_address *addr) if (addr->address.ssn_indicator != 1 || addr->address.global_title_indicator == 1 || addr->address.routing_indicator != 1) { - DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n", + LOGP(DSCCP, LOGL_ERROR, + "Invalid called address according to 08.06: 0x%x 0x%x\n", *(u_int8_t *)&addr->address, addr->ssn); return -1; } @@ -176,7 +177,7 @@ static int _sccp_parse_optional_data(const int offset, return 0; if (read + 1 >= room) { - DEBUGP(DSCCP, "no place for length\n"); + LOGP(DSCCP, LOGL_ERROR, "no place for length\n"); return 0; } @@ -185,7 +186,8 @@ static int _sccp_parse_optional_data(const int offset, if (room <= read) { - DEBUGP(DSCCP, "no space for the data: type: %d read: %d room: %d l2: %d\n", + LOGP(DSCCP, LOGL_ERROR, + "no space for the data: type: %d read: %d room: %d l2: %d\n", type, read, room, msgb_l2len(msgb)); return 0; } @@ -214,7 +216,7 @@ int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result * /* header check */ if (msgb_l2len(msgb) < header_size) { - DEBUGP(DSCCP, "msgb < header_size %u %u\n", + LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n", msgb_l2len(msgb), header_size); return -1; } @@ -224,7 +226,7 @@ int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result * return -1; if (check_address(&result->called) != 0) { - DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n", + LOGP(DSCCP, LOGL_ERROR, "Invalid called address according to 08.06: 0x%x 0x%x\n", *(u_int8_t *)&result->called.address, result->called.ssn); return -1; } @@ -236,7 +238,7 @@ int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result * */ memset(&optional_data, 0, sizeof(optional_data)); if (_sccp_parse_optional_data(optional_offset + req->optional_start, msgb, &optional_data) != 0) { - DEBUGP(DSCCP, "parsing of optional data failed.\n"); + LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n"); return -1; } @@ -260,14 +262,14 @@ int _sccp_parse_connection_released(struct msgb *msgb, struct sccp_parse_result /* we don't have enough size for the struct */ if (msgb_l2len(msgb) < header_size) { - DEBUGP(DSCCP, "msgb > header_size %u %u\n", + LOGP(DSCCP, LOGL_ERROR, "msgb > header_size %u %u\n", msgb_l2len(msgb), header_size); return -1; } memset(&optional_data, 0, sizeof(optional_data)); if (_sccp_parse_optional_data(optional_offset + rls->optional_start, msgb, &optional_data) != 0) { - DEBUGP(DSCCP, "parsing of optional data failed.\n"); + LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n"); return -1; } @@ -295,7 +297,7 @@ int _sccp_parse_connection_refused(struct msgb *msgb, struct sccp_parse_result * /* header check */ if (msgb_l2len(msgb) < header_size) { - DEBUGP(DSCCP, "msgb < header_size %u %u\n", + LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n", msgb_l2len(msgb), header_size); return -1; } @@ -306,7 +308,7 @@ int _sccp_parse_connection_refused(struct msgb *msgb, struct sccp_parse_result * memset(&optional_data, 0, sizeof(optional_data)); if (_sccp_parse_optional_data(optional_offset + ref->optional_start, msgb, &optional_data) != 0) { - DEBUGP(DSCCP, "parsing of optional data failed.\n"); + LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n"); return -1; } @@ -333,7 +335,7 @@ int _sccp_parse_connection_confirm(struct msgb *msgb, struct sccp_parse_result * /* header check */ if (msgb_l2len(msgb) < header_size) { - DEBUGP(DSCCP, "msgb < header_size %u %u\n", + LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n", msgb_l2len(msgb), header_size); return -1; } @@ -344,7 +346,7 @@ int _sccp_parse_connection_confirm(struct msgb *msgb, struct sccp_parse_result * memset(&optional_data, 0, sizeof(optional_data)); if (_sccp_parse_optional_data(optional_offset + con->optional_start, msgb, &optional_data) != 0) { - DEBUGP(DSCCP, "parsing of optional data failed.\n"); + LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n"); return -1; } @@ -366,7 +368,7 @@ int _sccp_parse_connection_release_complete(struct msgb *msgb, struct sccp_parse /* header check */ if (msgb_l2len(msgb) < header_size) { - DEBUGP(DSCCP, "msgb < header_size %u %u\n", + LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n", msgb_l2len(msgb), header_size); return -1; } @@ -387,13 +389,13 @@ int _sccp_parse_connection_dt1(struct msgb *msgb, struct sccp_parse_result *resu /* we don't have enough size for the struct */ if (msgb_l2len(msgb) < header_size) { - DEBUGP(DSCCP, "msgb > header_size %u %u\n", + LOGP(DSCCP, LOGL_ERROR, "msgb > header_size %u %u\n", msgb_l2len(msgb), header_size); return -1; } if (dt1->segmenting != 0) { - DEBUGP(DSCCP, "This packet has segmenting, not supported: %d\n", dt1->segmenting); + LOGP(DSCCP, LOGL_ERROR, "This packet has segmenting, not supported: %d\n", dt1->segmenting); return -1; } @@ -401,7 +403,7 @@ int _sccp_parse_connection_dt1(struct msgb *msgb, struct sccp_parse_result *resu /* some more size checks in here */ if (msgb_l2len(msgb) < variable_offset + dt1->variable_start + 1) { - DEBUGP(DSCCP, "Not enough space for variable start: %u %u\n", + LOGP(DSCCP, LOGL_ERROR, "Not enough space for variable start: %u %u\n", msgb_l2len(msgb), dt1->variable_start); return -1; } @@ -410,7 +412,7 @@ int _sccp_parse_connection_dt1(struct msgb *msgb, struct sccp_parse_result *resu msgb->l3h = &msgb->l2h[dt1->variable_start + variable_offset + 1]; if (msgb_l3len(msgb) < result->data_len) { - DEBUGP(DSCCP, "Not enough room for the payload: %u %u\n", + LOGP(DSCCP, LOGL_ERROR, "Not enough room for the payload: %u %u\n", msgb_l3len(msgb), result->data_len); return -1; } @@ -428,7 +430,7 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result) struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msgb->l2h; if (msgb_l2len(msgb) < header_size) { - DEBUGP(DSCCP, "msgb < header_size %u %u\n", + LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n", msgb_l2len(msgb), header_size); return -1; } @@ -438,7 +440,7 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result) return -1; if (check_address(&result->called) != 0) { - DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n", + LOGP(DSCCP, LOGL_ERROR, "Invalid called address according to 08.06: 0x%x 0x%x\n", *(u_int8_t *)&result->called.address, result->called.ssn); return -1; } @@ -447,13 +449,13 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result) return -1; if (check_address(&result->calling) != 0) { - DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n", + LOGP(DSCCP, LOGL_ERROR, "Invalid called address according to 08.06: 0x%x 0x%x\n", *(u_int8_t *)&result->called.address, result->called.ssn); } /* we don't have enough size for the data */ if (msgb_l2len(msgb) < data_offset + udt->variable_data + 1) { - DEBUGP(DSCCP, "msgb < header + offset %u %u %u\n", + LOGP(DSCCP, LOGL_ERROR, "msgb < header + offset %u %u %u\n", msgb_l2len(msgb), header_size, udt->variable_data); return -1; } @@ -463,7 +465,7 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result) result->data_len = msgb_l3len(msgb); if (msgb_l3len(msgb) != msgb->l3h[-1]) { - DEBUGP(DSCCP, "msgb is truncated is: %u should: %u\n", + LOGP(DSCCP, LOGL_ERROR, "msgb is truncated is: %u should: %u\n", msgb_l3len(msgb), msgb->l3h[-1]); return -1; } @@ -478,7 +480,7 @@ static int _sccp_parse_it(struct msgb *msgb, struct sccp_parse_result *result) struct sccp_data_it *it; if (msgb_l2len(msgb) < header_size) { - DEBUGP(DSCCP, "msgb < header_size %u %u\n", + LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n", msgb_l2len(msgb), header_size); return -1; } @@ -490,6 +492,23 @@ static int _sccp_parse_it(struct msgb *msgb, struct sccp_parse_result *result) return 0; } +static int _sccp_parse_err(struct msgb *msgb, struct sccp_parse_result *result) +{ + static const u_int32_t header_size = sizeof(struct sccp_proto_err); + + struct sccp_proto_err *err; + + if (msgb_l2len(msgb) < header_size) { + LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n", + msgb_l2len(msgb), header_size); + return -1; + } + + err = (struct sccp_proto_err *) msgb->l2h; + result->data_len = 0; + result->destination_local_reference = &err->destination_local_reference; + return 0; +} /* * Send UDT. Currently we have a fixed address... @@ -501,7 +520,7 @@ static int _sccp_send_data(int class, const struct sockaddr_sccp *in, u_int8_t *data; if (msgb_l3len(payload) > 256) { - DEBUGP(DSCCP, "The payload is too big for one udt\n"); + LOGP(DSCCP, LOGL_ERROR, "The payload is too big for one udt\n"); return -1; } @@ -546,7 +565,7 @@ static int _sccp_handle_read(struct msgb *msgb) cb = _find_ssn(result.called.ssn); if (!cb || !cb->read_cb) { - DEBUGP(DSCCP, "No routing for UDT for called SSN: %u\n", result.called.ssn); + LOGP(DSCCP, LOGL_ERROR, "No routing for UDT for called SSN: %u\n", result.called.ssn); return -1; } @@ -595,7 +614,7 @@ static int assign_source_local_reference(struct sccp_connection *connection) ++last_ref; /* do not use the reversed word and wrap around */ if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) { - DEBUGP(DSCCP, "Wrapped searching for a free code\n"); + LOGP(DSCCP, LOGL_DEBUG, "Wrapped searching for a free code\n"); last_ref = 0; ++wrapped; } @@ -606,7 +625,7 @@ static int assign_source_local_reference(struct sccp_connection *connection) } } while (wrapped != 2); - DEBUGP(DSCCP, "Finding a free reference failed\n"); + LOGP(DSCCP, LOGL_ERROR, "Finding a free reference failed\n"); return -1; } @@ -686,13 +705,13 @@ static int _sccp_send_connection_request(struct sccp_connection *connection, if (msg && (msgb_l3len(msg) < 3 || msgb_l3len(msg) > 130)) { - DEBUGP(DSCCP, "Invalid amount of data... %d\n", msgb_l3len(msg)); + LOGP(DSCCP, LOGL_ERROR, "Invalid amount of data... %d\n", msgb_l3len(msg)); return -1; } /* try to find a id */ if (assign_source_local_reference(connection) != 0) { - DEBUGP(DSCCP, "Assigning a local reference failed.\n"); + LOGP(DSCCP, LOGL_ERROR, "Assigning a local reference failed.\n"); _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_SETUP_ERROR); return -1; } @@ -744,7 +763,7 @@ static int _sccp_send_connection_data(struct sccp_connection *conn, struct msgb int extra_size; if (msgb_l3len(_data) < 2 || msgb_l3len(_data) > 256) { - DEBUGP(DSCCP, "data size too big, segmenting unimplemented.\n"); + LOGP(DSCCP, LOGL_ERROR, "data size too big, segmenting unimplemented.\n"); return -1; } @@ -840,14 +859,14 @@ static int _sccp_handle_connection_request(struct msgb *msgb) cb = _find_ssn(result.called.ssn); if (!cb || !cb->accept_cb) { - DEBUGP(DSCCP, "No routing for CR for called SSN: %u\n", result.called.ssn); + LOGP(DSCCP, LOGL_ERROR, "No routing for CR for called SSN: %u\n", result.called.ssn); return -1; } /* check if the system wants this connection */ connection = talloc_zero(tall_sccp_ctx, struct sccp_connection); if (!connection) { - DEBUGP(DSCCP, "Allocation failed\n"); + LOGP(DSCCP, LOGL_ERROR, "Allocation failed\n"); return -1; } @@ -859,7 +878,7 @@ static int _sccp_handle_connection_request(struct msgb *msgb) * one.... */ if (destination_local_reference_is_free(result.source_local_reference) != 0) { - DEBUGP(DSCCP, "Need to reject connection with existing reference\n"); + LOGP(DSCCP, LOGL_ERROR, "Need to reject connection with existing reference\n"); _sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE); talloc_free(connection); return -1; @@ -879,7 +898,7 @@ static int _sccp_handle_connection_request(struct msgb *msgb) llist_add_tail(&connection->list, &sccp_connections); if (_sccp_send_connection_confirm(connection) != 0) { - DEBUGP(DSCCP, "Sending confirm failed... no available source reference?\n"); + LOGP(DSCCP, LOGL_ERROR, "Sending confirm failed... no available source reference?\n"); _sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE); _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED); @@ -922,7 +941,7 @@ static int _sccp_handle_connection_release_complete(struct msgb *msgb) } - DEBUGP(DSCCP, "Release complete of unknown connection\n"); + LOGP(DSCCP, LOGL_ERROR, "Release complete of unknown connection\n"); return -1; found: @@ -950,7 +969,7 @@ static int _sccp_handle_connection_dt1(struct msgb *msgb) } } - DEBUGP(DSCCP, "No connection found for dt1 data\n"); + LOGP(DSCCP, LOGL_ERROR, "No connection found for dt1 data\n"); return -1; found: @@ -1009,7 +1028,7 @@ static int _sccp_handle_connection_released(struct msgb *msgb) } - DEBUGP(DSCCP, "Unknown connection was released.\n"); + LOGP(DSCCP, LOGL_ERROR, "Unknown connection was released.\n"); return -1; /* we have found a connection */ @@ -1021,7 +1040,7 @@ found: /* generate a response */ if (_sccp_send_connection_release_complete(conn) != 0) { - DEBUGP(DSCCP, "Sending release confirmed failed\n"); + LOGP(DSCCP, LOGL_ERROR, "Sending release confirmed failed\n"); return -1; } @@ -1046,7 +1065,7 @@ static int _sccp_handle_connection_refused(struct msgb *msgb) } } - DEBUGP(DSCCP, "Refused but no connection found\n"); + LOGP(DSCCP, LOGL_ERROR, "Refused but no connection found\n"); return -1; found: @@ -1079,7 +1098,7 @@ static int _sccp_handle_connection_confirm(struct msgb *msgb) } } - DEBUGP(DSCCP, "Confirmed but no connection found\n"); + LOGP(DSCCP, LOGL_ERROR, "Confirmed but no connection found\n"); return -1; found: @@ -1108,7 +1127,7 @@ int sccp_system_init(void (*outgoing)(struct msgb *data, void *ctx), void *ctx) int sccp_system_incoming(struct msgb *msgb) { if (msgb_l2len(msgb) < 1 ) { - DEBUGP(DSCCP, "Too short packet\n"); + LOGP(DSCCP, LOGL_ERROR, "Too short packet\n"); return -1; } @@ -1137,7 +1156,7 @@ int sccp_system_incoming(struct msgb *msgb) return _sccp_handle_read(msgb); break; default: - DEBUGP(DSCCP, "unimplemented msg type: %d\n", type); + LOGP(DSCCP, LOGL_ERROR, "unimplemented msg type: %d\n", type); }; return -1; @@ -1148,7 +1167,7 @@ int sccp_connection_write(struct sccp_connection *connection, struct msgb *data) { if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) { - DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n", + LOGP(DSCCP, LOGL_ERROR, "sccp_connection_write: Wrong connection state: %p %d\n", connection, connection->connection_state); return -1; } @@ -1165,7 +1184,7 @@ int sccp_connection_send_it(struct sccp_connection *connection) { if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) { - DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n", + LOGP(DSCCP, LOGL_ERROR, "sccp_connection_write: Wrong connection state: %p %d\n", connection, connection->connection_state); return -1; } @@ -1178,7 +1197,7 @@ int sccp_connection_close(struct sccp_connection *connection, int cause) { if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) { - DEBUGPC(DSCCP, "Can not close the connection. It was never opened: %p %d\n", + LOGP(DSCCP, LOGL_ERROR, "Can not close the connection. It was never opened: %p %d\n", connection, connection->connection_state); return -1; } @@ -1190,7 +1209,7 @@ int sccp_connection_free(struct sccp_connection *connection) { if (connection->connection_state > SCCP_CONNECTION_STATE_NONE && connection->connection_state < SCCP_CONNECTION_STATE_RELEASE_COMPLETE) { - DEBUGP(DSCCP, "The connection needs to be released before it is freed"); + LOGP(DSCCP, LOGL_ERROR, "The connection needs to be released before it is freed"); return -1; } @@ -1318,6 +1337,9 @@ int sccp_parse_header(struct msgb *msg, struct sccp_parse_result *result) case SCCP_MSG_TYPE_IT: return _sccp_parse_it(msg, result); break; + case SCCP_MSG_TYPE_ERR: + return _sccp_parse_err(msg, result); + break; }; LOGP(DSCCP, LOGL_ERROR, "Unimplemented MSG Type: 0x%x\n", type); diff --git a/openbsc/src/socket.c b/openbsc/src/socket.c new file mode 100644 index 000000000..3ed4d425a --- /dev/null +++ b/openbsc/src/socket.c @@ -0,0 +1,94 @@ +/* OpenBSC sokcet code, taken from Abis input driver for ip.access */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * + * 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 <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> + +#include <osmocore/select.h> +#include <osmocore/tlv.h> +#include <osmocore/msgb.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <osmocore/talloc.h> + +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, type, proto); + bfd->cb = cb; + bfd->when = BSC_FD_READ; + //bfd->data = line; + + if (bfd->fd < 0) { + LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n"); + return -EIO; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not bind l2 socket %s\n", + strerror(errno)); + close(bfd->fd); + return -EIO; + } + + if (proto != IPPROTO_UDP) { + ret = listen(bfd->fd, 1); + if (ret < 0) { + perror("listen"); + return ret; + } + } + + ret = bsc_register_fd(bfd); + if (ret < 0) { + perror("register_listen_fd"); + close(bfd->fd); + return ret; + } + return 0; +} diff --git a/openbsc/src/system_information.c b/openbsc/src/system_information.c index 3f9d60954..3bd833a93 100644 --- a/openbsc/src/system_information.c +++ b/openbsc/src/system_information.c @@ -402,6 +402,16 @@ static struct gsm48_si13_info si13_default = { .t3192 = 500, .drx_timer_max = 3, .bs_cv_max = 15, + .ext_info_present = 0, + .ext_info = { + /* The values below are just guesses ! */ + .egprs_supported = 0, + .use_egprs_p_ch_req = 1, + .bep_period = 4, + .pfc_supported = 0, + .dtm_supported = 0, + .bss_paging_coordination = 0, + }, }, .pwr_ctrl_pars = { .alpha = 10, /* a = 1.0 */ @@ -448,7 +458,18 @@ static int generate_si13(u_int8_t *output, struct gsm_bts *bts) int gsm_generate_si(u_int8_t *output, struct gsm_bts *bts, int type) { - si_info.gprs_ind.present = bts->gprs.enabled; + switch (bts->gprs.mode) { + case BTS_GPRS_EGPRS: + si13_default.cell_opts.ext_info_present = 1; + si13_default.cell_opts.ext_info.egprs_supported = 1; + /* fallthrough */ + case BTS_GPRS_GPRS: + si_info.gprs_ind.present = 1; + break; + case BTS_GPRS_NONE: + si_info.gprs_ind.present = 0; + break; + } switch (type) { case RSL_SYSTEM_INFO_1: diff --git a/openbsc/src/vty/buffer.c b/openbsc/src/vty/buffer.c index 195d06209..0bc1760cc 100644 --- a/openbsc/src/vty/buffer.c +++ b/openbsc/src/vty/buffer.c @@ -65,11 +65,11 @@ struct buffer_data { #define BUFFER_DATA_FREE(D) talloc_free((D)) /* Make new buffer. */ -struct buffer *buffer_new(size_t size) +struct buffer *buffer_new(void *ctx, size_t size) { struct buffer *b; - b = talloc_zero(tall_vty_ctx, struct buffer); + b = talloc_zero(ctx, struct buffer); if (size) b->size = size; @@ -138,7 +138,7 @@ static struct buffer_data *buffer_add(struct buffer *b) { struct buffer_data *d; - d = _talloc_zero(tall_vty_ctx, + d = _talloc_zero(b, offsetof(struct buffer_data, data[b->size]), "buffer_add"); if (!d) diff --git a/openbsc/src/vty/command.c b/openbsc/src/vty/command.c index a38ed0424..a1130b68e 100644 --- a/openbsc/src/vty/command.c +++ b/openbsc/src/vty/command.c @@ -478,6 +478,13 @@ void install_element(enum node_type ntype, struct cmd_element *cmd) cmd->cmdsize = cmd_cmdsize(cmd->strvec); } +/* Install a command into VIEW and ENABLE node */ +void install_element_ve(struct cmd_element *cmd) +{ + install_element(VIEW_NODE, cmd); + install_element(ENABLE_NODE, cmd); +} + #ifdef VTY_CRYPT_PW static unsigned char itoa64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; @@ -2311,7 +2318,7 @@ DEFUN(disable, } /* Down vty node level. */ -DEFUN(config_exit, +gDEFUN(config_exit, config_exit_cmd, "exit", "Exit current mode and down to previous mode\n") { switch (vty->node) { @@ -2363,6 +2370,9 @@ DEFUN(config_exit, vty->node = CONFIG_NODE; break; case MGCP_NODE: + case GBPROXY_NODE: + case SGSN_NODE: + case NS_NODE: vty->node = CONFIG_NODE; vty->index = NULL; default: @@ -2372,11 +2382,11 @@ DEFUN(config_exit, } /* quit is alias of exit. */ -ALIAS(config_exit, +gALIAS(config_exit, config_quit_cmd, "quit", "Exit current mode and down to previous mode\n") /* End of configuration. */ - DEFUN(config_end, + gDEFUN(config_end, config_end_cmd, "end", "End current mode and change to enable mode.") { switch (vty->node) { @@ -2407,7 +2417,7 @@ DEFUN(show_version, } /* Help display function for all node. */ -DEFUN(config_help, +gDEFUN(config_help, config_help_cmd, "help", "Description of the interactive help system\n") { vty_out(vty, @@ -2428,7 +2438,7 @@ argument.%s\ } /* Help display function for all node. */ -DEFUN(config_list, config_list_cmd, "list", "Print command list\n") +gDEFUN(config_list, config_list_cmd, "list", "Print command list\n") { unsigned int i; struct cmd_node *cnode = vector_slot(cmdvec, vty->node); diff --git a/openbsc/src/vty/utils.c b/openbsc/src/vty/utils.c new file mode 100644 index 000000000..d8dfb8ef9 --- /dev/null +++ b/openbsc/src/vty/utils.c @@ -0,0 +1,50 @@ +/* utility routines for printing common objects in the Osmocom world */ + +/* (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 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 <stdint.h> +#include <inttypes.h> + +#include <osmocore/linuxlist.h> +#include <osmocore/talloc.h> +#include <osmocore/timer.h> +#include <osmocore/rate_ctr.h> + +#include <vty/vty.h> + +void vty_out_rate_ctr_group(struct vty *vty, const char *prefix, + struct rate_ctr_group *ctrg) +{ + unsigned int i; + + vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE); + for (i = 0; i < ctrg->desc->num_ctr; i++) { + struct rate_ctr *ctr = &ctrg->ctr[i]; + vty_out(vty, " %s%s: %8" PRIu64 " " + "(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s", + prefix, ctrg->desc->ctr_desc[i].description, ctr->current, + ctr->intv[RATE_CTR_INTV_SEC].rate, + ctr->intv[RATE_CTR_INTV_MIN].rate, + ctr->intv[RATE_CTR_INTV_HOUR].rate, + ctr->intv[RATE_CTR_INTV_DAY].rate, + VTY_NEWLINE); + }; +} diff --git a/openbsc/src/vty/vty.c b/openbsc/src/vty/vty.c index 0ac9530f5..08068560b 100644 --- a/openbsc/src/vty/vty.c +++ b/openbsc/src/vty/vty.c @@ -51,10 +51,10 @@ struct vty *vty_new() if (!new) goto out; - new->obuf = buffer_new(0); /* Use default buffer size. */ + new->obuf = buffer_new(new, 0); /* Use default buffer size. */ if (!new->obuf) goto out_new; - new->buf = _talloc_zero(tall_vty_ctx, VTY_BUFSIZ, "vty_new->buf"); + new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf"); if (!new->buf) goto out_obuf; @@ -170,8 +170,7 @@ void vty_close(struct vty *vty) /* Check configure. */ vty_config_unlock(vty); - /* FIXME: memory leak. We need to call telnet_close_client() but don't - * have bfd */ + /* VTY_CLOSED is handled by the telnet_interface */ vty_event(VTY_CLOSED, vty->fd, vty); /* OK free vty. */ @@ -211,7 +210,7 @@ int vty_out(struct vty *vty, const char *format, ...) else size = size * 2; - p = talloc_realloc_size(tall_vty_ctx, p, size); + p = talloc_realloc_size(vty, p, size); if (!p) return -1; @@ -358,7 +357,7 @@ static void vty_ensure(struct vty *vty, int length) { if (vty->max <= length) { vty->max *= 2; - vty->buf = talloc_realloc_size(tall_vty_ctx, vty->buf, vty->max); + vty->buf = talloc_realloc_size(vty, vty->buf, vty->max); // FIXME: check return } } @@ -459,7 +458,7 @@ static void vty_hist_add(struct vty *vty) /* Insert history entry. */ if (vty->hist[vty->hindex]) talloc_free(vty->hist[vty->hindex]); - vty->hist[vty->hindex] = talloc_strdup(tall_vty_ctx, vty->buf); + vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf); /* History index rotation. */ vty->hindex++; @@ -916,7 +915,7 @@ static void vty_complete_command(struct vty *vty) vty_backward_pure_word(vty); vty_insert_word_overwrite(vty, matched[0]); vty_self_insert(vty, ' '); - //talloc_free(matched[0]); + talloc_free(matched[0]); break; case CMD_COMPLETE_MATCH: vty_prompt(vty); @@ -924,8 +923,6 @@ static void vty_complete_command(struct vty *vty) vty_backward_pure_word(vty); vty_insert_word_overwrite(vty, matched[0]); talloc_free(matched[0]); - vector_only_index_free(matched); - return; break; case CMD_COMPLETE_LIST_MATCH: for (i = 0; matched[i] != NULL; i++) { @@ -966,7 +963,7 @@ vty_describe_fold(struct vty *vty, int cmd_width, return; } - buf = _talloc_zero(tall_vty_ctx, strlen(desc->str) + 1, "describe_fold"); + buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold"); if (!buf) return; diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c index 956bf1e78..279970649 100644 --- a/openbsc/src/vty_interface.c +++ b/openbsc/src/vty_interface.c @@ -39,9 +39,37 @@ #include <osmocore/talloc.h> #include <openbsc/telnet_interface.h> #include <openbsc/vty.h> +#include <openbsc/gprs_ns.h> static struct gsm_network *gsmnet; +/* FIXME: this should go to some common file */ +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 const struct value_string gprs_bssgp_cfg_strs[] = { + { 0, "blocking-timer" }, + { 1, "blocking-retries" }, + { 2, "unblocking-retries" }, + { 3, "reset-timer" }, + { 4, "reset-retries" }, + { 5, "suspend-timer" }, + { 6, "suspend-retries" }, + { 7, "resume-timer" }, + { 8, "resume-retries" }, + { 9, "capability-update-timer" }, + { 10, "capability-update-retries" }, + { 0, NULL } +}; + struct cmd_node net_node = { GSMNET_NODE, "%s(network)#", @@ -266,6 +294,9 @@ static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx) int i; vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE); + vty_out(vty, " rf_locked %u%s", + trx->nm_state.administrative == NM_STATE_LOCKED ? 1 : 0, + VTY_NEWLINE); vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE); vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE); vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE); @@ -276,10 +307,48 @@ static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx) config_write_ts_single(vty, &trx->ts[i]); } +static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts) +{ + unsigned int i; + vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode), + VTY_NEWLINE); + if (bts->gprs.mode == BTS_GPRS_NONE) + return; + + vty_out(vty, " gprs routing area %u%s", bts->gprs.rac, + VTY_NEWLINE); + vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci, + VTY_NEWLINE); + for (i = 0; i < ARRAY_SIZE(bts->gprs.cell.timer); i++) + vty_out(vty, " gprs cell timer %s %u%s", + get_value_string(gprs_bssgp_cfg_strs, i), + bts->gprs.cell.timer[i], VTY_NEWLINE); + vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei, + VTY_NEWLINE); + for (i = 0; i < ARRAY_SIZE(bts->gprs.nse.timer); i++) + vty_out(vty, " gprs ns timer %s %u%s", + get_value_string(gprs_ns_timer_strs, i), + bts->gprs.nse.timer[i], VTY_NEWLINE); + for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { + struct gsm_bts_gprs_nsvc *nsvc = + &bts->gprs.nsvc[i]; + struct in_addr ia; + + ia.s_addr = htonl(nsvc->remote_ip); + vty_out(vty, " gprs nsvc %u nsvci %u%s", i, + nsvc->nsvci, VTY_NEWLINE); + vty_out(vty, " gprs nsvc %u local udp port %u%s", i, + nsvc->local_port, VTY_NEWLINE); + vty_out(vty, " gprs nsvc %u remote udp port %u%s", i, + nsvc->remote_port, VTY_NEWLINE); + vty_out(vty, " gprs nsvc %u remote ip %s%s", i, + inet_ntoa(ia), VTY_NEWLINE); + } +} + static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) { struct gsm_bts_trx *trx; - int i; vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE); vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE); @@ -305,6 +374,13 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) vty_out(vty, " rach max transmission %u%s", rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), VTY_NEWLINE); + + if (bts->rach_b_thresh != -1) + vty_out(vty, " rach nm busy threshold %u%s", + bts->rach_b_thresh, VTY_NEWLINE); + if (bts->rach_ldavg_slots != -1) + vty_out(vty, " rach nm load average %u%s", + bts->rach_ldavg_slots, VTY_NEWLINE); if (bts->si_common.rach_control.cell_bar) vty_out(vty, " cell barred 1%s", VTY_NEWLINE); if ((bts->si_common.rach_control.t2 & 0x4) == 0) @@ -317,30 +393,7 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) config_write_e1_link(vty, &bts->oml_e1_link, " oml "); vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE); } - vty_out(vty, " gprs enabled %u%s", bts->gprs.enabled, VTY_NEWLINE); - if (bts->gprs.enabled) { - vty_out(vty, " gprs routing area %u%s", bts->gprs.rac, - VTY_NEWLINE); - vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci, - VTY_NEWLINE); - vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei, - VTY_NEWLINE); - for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { - struct gsm_bts_gprs_nsvc *nsvc = - &bts->gprs.nsvc[i]; - struct in_addr ia; - - ia.s_addr = htonl(nsvc->remote_ip); - vty_out(vty, " gprs nsvc %u nsvci %u%s", i, - nsvc->nsvci, VTY_NEWLINE); - vty_out(vty, " gprs nsvc %u local udp port %u%s", i, - nsvc->local_port, VTY_NEWLINE); - vty_out(vty, " gprs nsvc %u remote udp port %u%s", i, - nsvc->remote_port, VTY_NEWLINE); - vty_out(vty, " gprs nsvc %u remote ip %s%s", i, - inet_ntoa(ia), VTY_NEWLINE); - } - } + config_write_bts_gprs(vty, bts); llist_for_each_entry(trx, &bts->trx_list, list) config_write_trx_single(vty, trx); @@ -423,7 +476,9 @@ static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx) DEFUN(show_trx, show_trx_cmd, "show trx [bts_nr] [trx_nr]", - SHOW_STR "Display information about a TRX\n") + SHOW_STR "Display information about a TRX\n" + "BTS Number\n" + "TRX Number\n") { struct gsm_network *net = gsmnet; struct gsm_bts *bts = NULL; @@ -488,7 +543,8 @@ static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts) DEFUN(show_ts, show_ts_cmd, "show timeslot [bts_nr] [trx_nr] [ts_nr]", - SHOW_STR "Display information about a TS\n") + SHOW_STR "Display information about a TS\n" + "BTS Number\n" "TRX Number\n" "Timeslot Number\n") { struct gsm_network *net = gsmnet; struct gsm_bts *bts; @@ -542,10 +598,6 @@ DEFUN(show_ts, static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr) { - int rc; - struct gsm_auth_info ainfo; - struct gsm_auth_tuple atuple; - vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, subscr->authorized, VTY_NEWLINE); if (subscr->name) @@ -596,7 +648,7 @@ static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr, meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul"); } -static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan) +static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan) { int idx; @@ -631,37 +683,28 @@ static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan) meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " "); } -#if 0 -TODO: callref and remote callref of call must be resolved to get gsm_trans object -static void call_dump_vty(struct vty *vty, struct gsm_call *call) +static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan) { - vty_out(vty, "Call Type %u, State %u, Transaction ID %u%s", - call->type, call->state, call->transaction_id, VTY_NEWLINE); - - if (call->local_lchan) { - vty_out(vty, "Call Local Channel:%s", VTY_NEWLINE); - lchan_dump_vty(vty, call->local_lchan); - } else - vty_out(vty, "Call has no Local Channel%s", VTY_NEWLINE); + struct gsm_meas_rep *mr; + int idx; - if (call->remote_lchan) { - vty_out(vty, "Call Remote Channel:%s", VTY_NEWLINE); - lchan_dump_vty(vty, call->remote_lchan); - } else - vty_out(vty, "Call has no Remote Channel%s", VTY_NEWLINE); + /* we want to report the last measurement report */ + idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), + lchan->meas_rep_idx, 1); + mr = &lchan->meas_rep[idx]; - if (call->called_subscr) { - vty_out(vty, "Called Subscriber:%s", VTY_NEWLINE); - subscr_dump_vty(vty, call->called_subscr); - } else - vty_out(vty, "Call has no Called Subscriber%s", VTY_NEWLINE); + vty_out(vty, "Lchan: %u Timeslot: %u TRX: %u BTS: %u Type: %s - L1 MS Power: %u dBm " + "RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s", + lchan->nr, lchan->ts->nr, lchan->ts->trx->nr, + lchan->ts->trx->bts->nr, gsm_lchant_name(lchan->type), + mr->ms_l1.pwr, + rxlev2dbm(mr->dl.full.rx_lev), + rxlev2dbm(mr->ul.full.rx_lev), + VTY_NEWLINE); } -#endif -DEFUN(show_lchan, - show_lchan_cmd, - "show lchan [bts_nr] [trx_nr] [ts_nr] [lchan_nr]", - SHOW_STR "Display information about a logical channel\n") +static int lchan_summary(struct vty *vty, int argc, const char **argv, + void (*dump_cb)(struct vty *, struct gsm_lchan *)) { struct gsm_network *net = gsmnet; struct gsm_bts *bts; @@ -706,7 +749,7 @@ DEFUN(show_lchan, return CMD_WARNING; } lchan = &ts->lchan[lchan_nr]; - lchan_dump_vty(vty, lchan); + dump_cb(vty, lchan); return CMD_SUCCESS; } for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { @@ -720,7 +763,7 @@ DEFUN(show_lchan, lchan = &ts->lchan[lchan_nr]; if (lchan->type == GSM_LCHAN_NONE) continue; - lchan_dump_vty(vty, lchan); + dump_cb(vty, lchan); } } } @@ -729,6 +772,28 @@ DEFUN(show_lchan, return CMD_SUCCESS; } + +DEFUN(show_lchan, + show_lchan_cmd, + "show lchan [bts_nr] [trx_nr] [ts_nr] [lchan_nr]", + SHOW_STR "Display information about a logical channel\n" + "BTS Number\n" "TRX Number\n" "Timeslot Number\n" + "Logical Channel Number\n") + +{ + return lchan_summary(vty, argc, argv, lchan_dump_full_vty); +} + +DEFUN(show_lchan_summary, + show_lchan_summary_cmd, + "show lchan summary [bts_nr] [trx_nr] [ts_nr] [lchan_nr]", + SHOW_STR "Display information about a logical channel\n" + "BTS Number\n" "TRX Number\n" "Timeslot Number\n" + "Logical Channel Number\n") +{ + return lchan_summary(vty, argc, argv, lchan_dump_short_vty); +} + static void e1drv_dump_vty(struct vty *vty, struct e1inp_driver *drv) { vty_out(vty, "E1 Input Driver %s%s", drv->name, VTY_NEWLINE); @@ -757,7 +822,8 @@ static void e1line_dump_vty(struct vty *vty, struct e1inp_line *line) DEFUN(show_e1line, show_e1line_cmd, "show e1_line [line_nr]", - SHOW_STR "Display information about a E1 line\n") + SHOW_STR "Display information about a E1 line\n" + "E1 Line Number\n") { struct e1inp_line *line; @@ -790,7 +856,8 @@ static void e1ts_dump_vty(struct vty *vty, struct e1inp_ts *ts) DEFUN(show_e1ts, show_e1ts_cmd, "show e1_timeslot [line_nr] [ts_nr]", - SHOW_STR "Display information about a E1 timeslot\n") + SHOW_STR "Display information about a E1 timeslot\n" + "E1 Line Number\n" "E1 Timeslot Number\n") { struct e1inp_line *line = NULL; struct e1inp_ts *ts; @@ -854,7 +921,8 @@ static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts) DEFUN(show_paging, show_paging_cmd, "show paging [bts_nr]", - SHOW_STR "Display information about paging reuqests of a BTS\n") + SHOW_STR "Display information about paging reuqests of a BTS\n" + "BTS Number\n") { struct gsm_network *net = gsmnet; struct gsm_bts *bts; @@ -881,50 +949,11 @@ DEFUN(show_paging, return CMD_SUCCESS; } -DEFUN(show_stats, - show_stats_cmd, - "show statistics", - SHOW_STR "Display network statistics\n") -{ - struct gsm_network *net = gsmnet; - - vty_out(vty, "Channel Requests : %lu total, %lu no channel%s", - counter_get(net->stats.chreq.total), - counter_get(net->stats.chreq.no_channel), VTY_NEWLINE); - vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s", - counter_get(net->stats.loc_upd_type.attach), - counter_get(net->stats.loc_upd_type.normal), - counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE); - vty_out(vty, "IMSI Detach Indications : %lu%s", - counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE); - vty_out(vty, "Location Update Response: %lu accept, %lu reject%s", - counter_get(net->stats.loc_upd_resp.accept), - counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE); - vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s", - counter_get(net->stats.paging.attempted), - counter_get(net->stats.paging.completed), - counter_get(net->stats.paging.expired), VTY_NEWLINE); - vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, " - "%lu completed, %lu failed%s", - counter_get(net->stats.handover.attempted), - counter_get(net->stats.handover.no_channel), - counter_get(net->stats.handover.timeout), - counter_get(net->stats.handover.completed), - counter_get(net->stats.handover.failed), VTY_NEWLINE); - vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s", - counter_get(net->stats.sms.submitted), - counter_get(net->stats.sms.no_receiver), VTY_NEWLINE); - vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s", - counter_get(net->stats.sms.delivered), - counter_get(net->stats.sms.rp_err_mem), - counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE); - return CMD_SUCCESS; -} +#define NETWORK_STR "Configure the GSM network\n" DEFUN(cfg_net, cfg_net_cmd, - "network", - "Configure the GSM network") + "network", NETWORK_STR) { vty->index = gsmnet; vty->node = GSMNET_NODE; @@ -982,7 +1011,11 @@ DEFUN(cfg_net_name_long, DEFUN(cfg_net_auth_policy, cfg_net_auth_policy_cmd, "auth policy (closed|accept-all|token)", - "Set the GSM network authentication policy\n") + "Authentication (not cryptographic)\n" + "Set the GSM network authentication policy\n" + "Require the MS to be activated in HLR\n" + "Accept all MS, whether in HLR or not\n" + "Use SMS-token based authentication\n") { enum gsm_auth_policy policy = gsm_auth_policy_parse(argv[0]); @@ -1022,7 +1055,12 @@ DEFUN(cfg_net_neci, DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd, "rrlp mode (none|ms-based|ms-preferred|ass-preferred)", - "Set the Radio Resource Location Protocol Mode") + "Radio Resource Location Protocol\n" + "Set the Radio Resource Location Protocol Mode\n" + "Don't send RRLP request\n" + "Request MS-based location\n" + "Request any location, prefer MS-based\n" + "Request any location, prefer MS-assisted\n") { gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]); @@ -1038,9 +1076,13 @@ DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd, return CMD_SUCCESS; } +#define HANDOVER_STR "Handover Options\n" + DEFUN(cfg_net_handover, cfg_net_handover_cmd, "handover (0|1)", - "Whether or not to use in-call handover") + HANDOVER_STR + "Don't perform in-call handover\n" + "Perform in-call handover\n") { int enable = atoi(argv[0]); @@ -1055,8 +1097,14 @@ DEFUN(cfg_net_handover, cfg_net_handover_cmd, return CMD_SUCCESS; } +#define HO_WIN_STR HANDOVER_STR "Measurement Window\n" +#define HO_WIN_RXLEV_STR HO_WIN_STR "Received Level Averaging\n" +#define HO_WIN_RXQUAL_STR HO_WIN_STR "Received Quality Averaging\n" +#define HO_PBUDGET_STR HANDOVER_STR "Power Budget\n" + DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd, "handover window rxlev averaging <1-10>", + HO_WIN_RXLEV_STR "How many RxLev measurements are used for averaging") { gsmnet->handover.win_rxlev_avg = atoi(argv[0]); @@ -1065,6 +1113,7 @@ DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd, DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd, "handover window rxqual averaging <1-10>", + HO_WIN_RXQUAL_STR "How many RxQual measurements are used for averaging") { gsmnet->handover.win_rxqual_avg = atoi(argv[0]); @@ -1073,6 +1122,7 @@ DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd, DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd, "handover window rxlev neighbor averaging <1-10>", + HO_WIN_RXLEV_STR "How many RxQual measurements are used for averaging") { gsmnet->handover.win_rxlev_avg_neigh = atoi(argv[0]); @@ -1081,6 +1131,7 @@ DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd, DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd, "handover power budget interval <1-99>", + HO_PBUDGET_STR "How often to check if we have a better cell (SACCH frames)") { gsmnet->handover.pwr_interval = atoi(argv[0]); @@ -1089,6 +1140,7 @@ DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd, DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd, "handover power budget hysteresis <0-999>", + HO_PBUDGET_STR "How many dB does a neighbor to be stronger to become a HO candidate") { gsmnet->handover.pwr_hysteresis = atoi(argv[0]); @@ -1097,6 +1149,7 @@ DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd, DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd, "handover maximum distance <0-9999>", + HANDOVER_STR "How big is the maximum timing advance before HO is forced") { gsmnet->handover.max_distance = atoi(argv[0]); @@ -1107,6 +1160,7 @@ DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd, DEFUN(cfg_net_T##number, \ cfg_net_T##number##_cmd, \ "timer t" #number " <0-65535>", \ + "Configure GSM Timers\n" \ doc) \ { \ int value = atoi(argv[0]); \ @@ -1138,7 +1192,8 @@ DECLARE_TIMER(3141, "Currently not used.") DEFUN(cfg_bts, cfg_bts_cmd, "bts BTS_NR", - "Select a BTS to configure\n") + "Select a BTS to configure\n" + "BTS Number\n") { int bts_nr = atoi(argv[0]); struct gsm_bts *bts; @@ -1301,9 +1356,13 @@ DEFUN(cfg_bts_unit_id, return CMD_SUCCESS; } +#define OML_STR "Organization & Maintenance Link\n" +#define IPA_STR "ip.access Specific Options\n" + DEFUN(cfg_bts_stream_id, cfg_bts_stream_id_cmd, "oml ip.access stream_id <0-255>", + OML_STR IPA_STR "Set the ip.access Stream ID of the OML link of this BTS\n") { struct gsm_bts *bts = vty->index; @@ -1319,10 +1378,12 @@ DEFUN(cfg_bts_stream_id, return CMD_SUCCESS; } +#define OML_E1_STR OML_STR "E1 Line\n" DEFUN(cfg_bts_oml_e1, cfg_bts_oml_e1_cmd, "oml e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", + OML_E1_STR "E1 interface to be used for OML\n") { struct gsm_bts *bts = vty->index; @@ -1336,6 +1397,7 @@ DEFUN(cfg_bts_oml_e1, DEFUN(cfg_bts_oml_e1_tei, cfg_bts_oml_e1_tei_cmd, "oml e1 tei <0-63>", + OML_E1_STR "Set the TEI to be used for OML") { struct gsm_bts *bts = vty->index; @@ -1347,7 +1409,9 @@ DEFUN(cfg_bts_oml_e1_tei, DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd, "channel allocator (ascending|descending)", - "Should the channel allocator allocate in reverse TRX order?") + "Channnel Allocator\n" "Channel Allocator\n" + "Allocate Timeslots and Transceivers in ascending order\n" + "Allocate Timeslots and Transceivers in descending order\n") { struct gsm_bts *bts = vty->index; @@ -1359,9 +1423,12 @@ DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd, return CMD_SUCCESS; } +#define RACH_STR "Random Access Control Channel\n" + DEFUN(cfg_bts_rach_tx_integer, cfg_bts_rach_tx_integer_cmd, "rach tx integer <0-15>", + RACH_STR "Set the raw tx integer value in RACH Control parameters IE") { struct gsm_bts *bts = vty->index; @@ -1372,6 +1439,7 @@ DEFUN(cfg_bts_rach_tx_integer, DEFUN(cfg_bts_rach_max_trans, cfg_bts_rach_max_trans_cmd, "rach max transmission (1|2|4|7)", + RACH_STR "Set the maximum number of RACH burst transmissions") { struct gsm_bts *bts = vty->index; @@ -1379,6 +1447,30 @@ DEFUN(cfg_bts_rach_max_trans, return CMD_SUCCESS; } +#define NM_STR "Network Management\n" + +DEFUN(cfg_bts_rach_nm_b_thresh, + cfg_bts_rach_nm_b_thresh_cmd, + "rach nm busy threshold <0-255>", + RACH_STR NM_STR + "Set the NM Busy Threshold in dB") +{ + struct gsm_bts *bts = vty->index; + bts->rach_b_thresh = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_rach_nm_ldavg, + cfg_bts_rach_nm_ldavg_cmd, + "rach nm load average <0-65535>", + RACH_STR NM_STR + "Set the NM Loadaverage Slots value") +{ + struct gsm_bts *bts = vty->index; + bts->rach_ldavg_slots = atoi(argv[0]); + return CMD_SUCCESS; +} + DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd, "cell barred (0|1)", "Should this cell be barred from access?") @@ -1448,13 +1540,17 @@ DEFUN(cfg_bts_per_loc_upd, cfg_bts_per_loc_upd_cmd, return CMD_SUCCESS; } +#define GPRS_TEXT "GPRS Packet Network\n" + DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd, - "gprs cell bvci <0-65535>", + "gprs cell bvci <2-65535>", + GPRS_TEXT + "GPRS Cell Settings\n" "GPRS BSSGP VC Identifier") { struct gsm_bts *bts = vty->index; - if (!bts->gprs.enabled) { + if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } @@ -1466,11 +1562,12 @@ DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd, DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd, "gprs nsei <0-65535>", + GPRS_TEXT "GPRS NS Entity Identifier") { struct gsm_bts *bts = vty->index; - if (!bts->gprs.enabled) { + if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } @@ -1480,15 +1577,19 @@ DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd, return CMD_SUCCESS; } +#define NSVC_TEXT "Network Service Virtual Connection (NS-VC)\n" \ + "NSVC Logical Number\n" DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd, "gprs nsvc <0-1> nsvci <0-65535>", + GPRS_TEXT NSVC_TEXT + "NS Virtual Connection Identifier\n" "GPRS NS VC Identifier") { struct gsm_bts *bts = vty->index; int idx = atoi(argv[0]); - if (!bts->gprs.enabled) { + if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } @@ -1500,12 +1601,13 @@ DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd, DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd, "gprs nsvc <0-1> local udp port <0-65535>", + GPRS_TEXT NSVC_TEXT "GPRS NS Local UDP Port") { struct gsm_bts *bts = vty->index; int idx = atoi(argv[0]); - if (!bts->gprs.enabled) { + if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } @@ -1517,12 +1619,13 @@ DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd, DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd, "gprs nsvc <0-1> remote udp port <0-65535>", + GPRS_TEXT NSVC_TEXT "GPRS NS Remote UDP Port") { struct gsm_bts *bts = vty->index; int idx = atoi(argv[0]); - if (!bts->gprs.enabled) { + if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } @@ -1534,13 +1637,14 @@ DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd, DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd, "gprs nsvc <0-1> remote ip A.B.C.D", + GPRS_TEXT NSVC_TEXT "GPRS NS Remote IP Address") { struct gsm_bts *bts = vty->index; int idx = atoi(argv[0]); struct in_addr ia; - if (!bts->gprs.enabled) { + if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } @@ -1551,13 +1655,63 @@ DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd, return CMD_SUCCESS; } +DEFUN(cfg_bts_gprs_ns_timer, cfg_bts_gprs_ns_timer_cmd, + "gprs ns timer " NS_TIMERS " <0-255>", + GPRS_TEXT "Network Service\n" + "Network Service Timer\n" + NS_TIMERS_HELP "Timer Value\n") +{ + struct gsm_bts *bts = vty->index; + int idx = get_string_value(gprs_ns_timer_strs, argv[0]); + int val = atoi(argv[1]); + + if (bts->gprs.mode == BTS_GPRS_NONE) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.nse.timer)) + return CMD_WARNING; + + bts->gprs.nse.timer[idx] = val; + + return CMD_SUCCESS; +} + +#define BSSGP_TIMERS "(blocking-timer|blocking-retries|unblocking-retries|reset-timer|reset-retries|suspend-timer|suspend-retries|resume-timer|resume-retries|capability-update-timer|capability-update-retries)" +#define BSSGP_TIMERS_HELP "" + +DEFUN(cfg_bts_gprs_cell_timer, cfg_bts_gprs_cell_timer_cmd, + "gprs cell timer " BSSGP_TIMERS " <0-255>", + GPRS_TEXT "Cell / BSSGP\n" + "Cell/BSSGP Timer\n" + BSSGP_TIMERS_HELP "Timer Value\n") +{ + struct gsm_bts *bts = vty->index; + int idx = get_string_value(gprs_bssgp_cfg_strs, argv[0]); + int val = atoi(argv[1]); + + if (bts->gprs.mode == BTS_GPRS_NONE) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.cell.timer)) + return CMD_WARNING; + + bts->gprs.cell.timer[idx] = val; + + return CMD_SUCCESS; +} + DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd, "gprs routing area <0-255>", + GPRS_TEXT "GPRS Routing Area Code") { struct gsm_bts *bts = vty->index; - if (!bts->gprs.enabled) { + if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } @@ -1567,22 +1721,28 @@ DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd, return CMD_SUCCESS; } -DEFUN(cfg_bts_gprs_enabled, cfg_bts_gprs_enabled_cmd, - "gprs enabled <0-1>", - "GPRS Enabled on this BTS") +DEFUN(cfg_bts_gprs_mode, cfg_bts_gprs_mode_cmd, + "gprs mode (none|gprs|egprs)", + GPRS_TEXT + "GPRS Mode for this BTS\n" + "GPRS Disabled on this BTS\n" + "GPRS Enabled on this BTS\n" + "EGPRS (EDGE) Enabled on this BTS\n") { struct gsm_bts *bts = vty->index; - bts->gprs.enabled = atoi(argv[0]); + bts->gprs.mode = bts_gprs_mode_parse(argv[0]); return CMD_SUCCESS; } +#define TRX_TEXT "Radio Transceiver\n" /* per TRX configuration */ DEFUN(cfg_trx, cfg_trx_cmd, "trx TRX_NR", + TRX_TEXT "Select a TRX to configure") { int trx_nr = atoi(argv[0]); @@ -1756,6 +1916,8 @@ DEFUN(cfg_ts_e1_subslot, return CMD_SUCCESS; } +extern int bsc_vty_init_extra(struct gsm_network *net); + int bsc_vty_init(struct gsm_network *net) { gsmnet = net; @@ -1763,18 +1925,18 @@ int bsc_vty_init(struct gsm_network *net) cmd_init(1); vty_init(); - install_element(VIEW_NODE, &show_net_cmd); - install_element(VIEW_NODE, &show_bts_cmd); - install_element(VIEW_NODE, &show_trx_cmd); - install_element(VIEW_NODE, &show_ts_cmd); - install_element(VIEW_NODE, &show_lchan_cmd); + install_element_ve(&show_net_cmd); + install_element_ve(&show_bts_cmd); + install_element_ve(&show_trx_cmd); + install_element_ve(&show_ts_cmd); + install_element_ve(&show_lchan_cmd); + install_element_ve(&show_lchan_summary_cmd); - install_element(VIEW_NODE, &show_e1drv_cmd); - install_element(VIEW_NODE, &show_e1line_cmd); - install_element(VIEW_NODE, &show_e1ts_cmd); + install_element_ve(&show_e1drv_cmd); + install_element_ve(&show_e1line_cmd); + install_element_ve(&show_e1ts_cmd); - install_element(VIEW_NODE, &show_paging_cmd); - install_element(VIEW_NODE, &show_stats_cmd); + install_element_ve(&show_paging_cmd); openbsc_vty_add_cmds(); @@ -1826,15 +1988,19 @@ int bsc_vty_init(struct gsm_network *net) install_element(BTS_NODE, &cfg_bts_challoc_cmd); install_element(BTS_NODE, &cfg_bts_rach_tx_integer_cmd); install_element(BTS_NODE, &cfg_bts_rach_max_trans_cmd); + install_element(BTS_NODE, &cfg_bts_rach_nm_b_thresh_cmd); + install_element(BTS_NODE, &cfg_bts_rach_nm_ldavg_cmd); install_element(BTS_NODE, &cfg_bts_cell_barred_cmd); install_element(BTS_NODE, &cfg_bts_rach_ec_allowed_cmd); install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd); install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd); install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd); install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_enabled_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_mode_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd); install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd); install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd); install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd); install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd); install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd); diff --git a/openbsc/src/vty_interface_cmds.c b/openbsc/src/vty_interface_cmds.c index d4945840e..dd5e108ab 100644 --- a/openbsc/src/vty_interface_cmds.c +++ b/openbsc/src/vty_interface_cmds.c @@ -30,6 +30,8 @@ #include <stdlib.h> +#define LOGGING_STR "Configure log message to this terminal\n" + static void _vty_output(struct log_target *tgt, const char *line) { struct vty *vty = tgt->tgt_vty.vty; @@ -55,6 +57,7 @@ struct log_target *log_target_create_vty(struct vty *vty) DEFUN(enable_logging, enable_logging_cmd, "logging enable", + LOGGING_STR "Enables logging to this vty\n") { struct telnet_connection *conn; @@ -76,6 +79,7 @@ DEFUN(enable_logging, DEFUN(logging_fltr_imsi, logging_fltr_imsi_cmd, "logging filter imsi IMSI", + LOGGING_STR "Print all messages related to a IMSI\n") { struct telnet_connection *conn; @@ -93,6 +97,7 @@ DEFUN(logging_fltr_imsi, DEFUN(logging_fltr_all, logging_fltr_all_cmd, "logging filter all <0-1>", + LOGGING_STR "Print all messages to the console\n") { struct telnet_connection *conn; @@ -110,6 +115,7 @@ DEFUN(logging_fltr_all, DEFUN(logging_use_clr, logging_use_clr_cmd, "logging color <0-1>", + LOGGING_STR "Use color for printing messages\n") { struct telnet_connection *conn; @@ -127,6 +133,7 @@ DEFUN(logging_use_clr, DEFUN(logging_prnt_timestamp, logging_prnt_timestamp_cmd, "logging timestamp <0-1>", + LOGGING_STR "Print the timestamp of each message\n") { struct telnet_connection *conn; @@ -142,12 +149,48 @@ 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|ns|bssgp|all)" +#define CATEGORIES_HELP \ + "A-bis Radio Link Layer (RLL)\n" \ + "Layer3 Call Control (CC)\n" \ + "Layer3 Mobility Management (MM)\n" \ + "Layer3 Radio Resource (RR)\n" \ + "A-bis Radio Signalling Link (RSL)\n" \ + "A-bis Network Management / O&M (NM/OML)\n" \ + "Layer3 Short Messagaging Service (SMS)\n" \ + "Paging Subsystem\n" \ + "MNCC API for Call Control application\n" \ + "A-bis Input Subsystem\n" \ + "A-bis Input Driver for Signalling\n" \ + "A-bis Input Driver for B-Channel (voice data)\n" \ + "A-bis B-Channel / Sub-channel Multiplexer\n" \ + "Radio Measurements\n" \ + "SCCP\n" \ + "Mobile Switching Center\n" \ + "Media Gateway Control Protocol\n" \ + "Hand-over\n" \ + "Database Layer\n" \ + "Reference Counting\n" \ + "GPRS Core\n" \ + "GPRS Network Service (NS)\n" \ + "GPRS BSS Gateway Protocol (BSSGP)\n" \ + "Global setting for all subsytems\n" + #define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)" +#define LEVELS_HELP \ + "Log simply everything\n" \ + "Log debug messages and higher levels\n" \ + "Log informational messages and higher levels\n" \ + "Log noticable messages and higher levels\n" \ + "Log error messages and higher levels\n" \ + "Log only fatal messages\n" DEFUN(logging_level, logging_level_cmd, "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS, - "Set the log level for a specified category\n") + LOGGING_STR + "Set the log level for a specified category\n" + CATEGORIES_HELP + LEVELS_HELP) { struct telnet_connection *conn; int category = log_parse_category(argv[0]); @@ -159,13 +202,19 @@ DEFUN(logging_level, return CMD_WARNING; } - if (category < 0) { - vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE); + if (level < 0) { + vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } - if (level < 0) { - vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE); + /* Check for special case where we want to set global log level */ + if (!strcmp(argv[0], "all")) { + log_set_log_level(conn->dbg, level); + return CMD_SUCCESS; + } + + if (category < 0) { + vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } @@ -178,6 +227,7 @@ DEFUN(logging_level, DEFUN(logging_set_category_mask, logging_set_category_mask_cmd, "logging set log mask MASK", + LOGGING_STR "Decide which categories to output.\n") { struct telnet_connection *conn; @@ -192,10 +242,11 @@ DEFUN(logging_set_category_mask, return CMD_SUCCESS; } -DEFUN(logging_set_log_level, - logging_set_log_level_cmd, - "logging set log level <0-8>", - "Set the global log level. The value 0 implies no filtering.\n") +DEFUN(diable_logging, + disable_logging_cmd, + "logging disable", + LOGGING_STR + "Disables logging to this vty\n") { struct telnet_connection *conn; @@ -205,14 +256,42 @@ DEFUN(logging_set_log_level, return CMD_WARNING; } - log_set_log_level(conn->dbg, atoi(argv[0])); + log_del_target(conn->dbg); + talloc_free(conn->dbg); + conn->dbg = NULL; return CMD_SUCCESS; } -DEFUN(diable_logging, - disable_logging_cmd, - "logging disable", - "Disables logging to this vty\n") +static void vty_print_logtarget(struct vty *vty, const struct log_info *info, + const struct log_target *tgt) +{ + unsigned int i; + + vty_out(vty, " Global Loglevel: %s%s", + log_level_str(tgt->loglevel), VTY_NEWLINE); + vty_out(vty, " Use color: %s, Print Timestamp: %s%s", + tgt->use_color ? "On" : "Off", + tgt->print_timestamp ? "On" : "Off", VTY_NEWLINE); + + vty_out(vty, " Log Level specific information:%s", VTY_NEWLINE); + + for (i = 0; i < info->num_cat; i++) { + const struct log_category *cat = &tgt->categories[i]; + vty_out(vty, " %-10s %-10s %-8s %s%s", + info->cat[i].name+1, log_level_str(cat->loglevel), + cat->enabled ? "Enabled" : "Disabled", + info->cat[i].description, + VTY_NEWLINE); + } +} + +#define SHOW_LOG_STR "Show current logging configuration\n" + +DEFUN(show_logging_vty, + show_logging_vty_cmd, + "show logging vty", + SHOW_STR SHOW_LOG_STR + "Show current logging configuration for this vty\n") { struct telnet_connection *conn; @@ -221,23 +300,37 @@ DEFUN(diable_logging, vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); return CMD_WARNING; } + vty_print_logtarget(vty, &log_info, conn->dbg); - log_del_target(conn->dbg); - talloc_free(conn->dbg); - conn->dbg = NULL; return CMD_SUCCESS; } -void openbsc_vty_add_cmds() +void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net) { - install_element(VIEW_NODE, &enable_logging_cmd); - install_element(VIEW_NODE, &disable_logging_cmd); - install_element(VIEW_NODE, &logging_fltr_imsi_cmd); - install_element(VIEW_NODE, &logging_fltr_all_cmd); - install_element(VIEW_NODE, &logging_use_clr_cmd); - install_element(VIEW_NODE, &logging_prnt_timestamp_cmd); - install_element(VIEW_NODE, &logging_set_category_mask_cmd); - install_element(VIEW_NODE, &logging_level_cmd); - install_element(VIEW_NODE, &logging_set_log_level_cmd); + vty_out(vty, "Channel Requests : %lu total, %lu no channel%s", + counter_get(net->stats.chreq.total), + counter_get(net->stats.chreq.no_channel), VTY_NEWLINE); + vty_out(vty, "Channel Failures : %lu rf_failures, %lu rll failures%s", + counter_get(net->stats.chan.rf_fail), + counter_get(net->stats.chan.rll_err), VTY_NEWLINE); + vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s", + counter_get(net->stats.paging.attempted), + counter_get(net->stats.paging.completed), + counter_get(net->stats.paging.expired), VTY_NEWLINE); + vty_out(vty, "BTS failures : %lu OML, %lu RSL%s", + counter_get(net->stats.bts.oml_fail), + counter_get(net->stats.bts.rsl_fail), VTY_NEWLINE); +} +void openbsc_vty_add_cmds() +{ + install_element_ve(&enable_logging_cmd); + install_element_ve(&disable_logging_cmd); + install_element_ve(&logging_fltr_imsi_cmd); + install_element_ve(&logging_fltr_all_cmd); + install_element_ve(&logging_use_clr_cmd); + install_element_ve(&logging_prnt_timestamp_cmd); + install_element_ve(&logging_set_category_mask_cmd); + install_element_ve(&logging_level_cmd); + install_element_ve(&show_logging_vty_cmd); } diff --git a/openbsc/src/vty_interface_layer3.c b/openbsc/src/vty_interface_layer3.c index b824c3db6..1b2adbbc7 100644 --- a/openbsc/src/vty_interface_layer3.c +++ b/openbsc/src/vty_interface_layer3.c @@ -41,6 +41,7 @@ #include <osmocore/talloc.h> #include <openbsc/signal.h> #include <openbsc/debug.h> +#include <openbsc/vty.h> static struct gsm_network *gsmnet; @@ -57,7 +58,7 @@ static int dummy_config_write(struct vty *v) static struct buffer *argv_to_buffer(int argc, const char *argv[], int base) { - struct buffer *b = buffer_new(1024); + struct buffer *b = buffer_new(NULL, 1024); int i; if (!b) @@ -502,21 +503,57 @@ static int scall_cbfn(unsigned int subsys, unsigned int signal, return 0; } +DEFUN(show_stats, + show_stats_cmd, + "show statistics", + SHOW_STR "Display network statistics\n") +{ + struct gsm_network *net = gsmnet; + + openbsc_vty_print_statistics(vty, net); + vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s", + counter_get(net->stats.loc_upd_type.attach), + counter_get(net->stats.loc_upd_type.normal), + counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE); + vty_out(vty, "IMSI Detach Indications : %lu%s", + counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE); + vty_out(vty, "Location Update Response: %lu accept, %lu reject%s", + counter_get(net->stats.loc_upd_resp.accept), + counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE); + vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, " + "%lu completed, %lu failed%s", + counter_get(net->stats.handover.attempted), + counter_get(net->stats.handover.no_channel), + counter_get(net->stats.handover.timeout), + counter_get(net->stats.handover.completed), + counter_get(net->stats.handover.failed), VTY_NEWLINE); + vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s", + counter_get(net->stats.sms.submitted), + counter_get(net->stats.sms.no_receiver), VTY_NEWLINE); + vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s", + counter_get(net->stats.sms.delivered), + counter_get(net->stats.sms.rp_err_mem), + counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE); + return CMD_SUCCESS; +} + + int bsc_vty_init_extra(struct gsm_network *net) { gsmnet = net; register_signal_handler(SS_SCALL, scall_cbfn, NULL); - install_element(VIEW_NODE, &show_subscr_cmd); - install_element(VIEW_NODE, &show_subscr_cache_cmd); + install_element_ve(&show_subscr_cmd); + install_element_ve(&show_subscr_cache_cmd); - install_element(VIEW_NODE, &sms_send_pend_cmd); + install_element_ve(&sms_send_pend_cmd); - install_element(VIEW_NODE, &subscriber_send_sms_cmd); - install_element(VIEW_NODE, &subscriber_silent_sms_cmd); - install_element(VIEW_NODE, &subscriber_silent_call_start_cmd); - install_element(VIEW_NODE, &subscriber_silent_call_stop_cmd); + install_element_ve(&subscriber_send_sms_cmd); + install_element_ve(&subscriber_silent_sms_cmd); + install_element_ve(&subscriber_silent_call_start_cmd); + install_element_ve(&subscriber_silent_call_stop_cmd); + install_element_ve(&show_stats_cmd); install_element(CONFIG_NODE, &cfg_subscr_cmd); install_node(&subscr_node, dummy_config_write); diff --git a/openbsc/tests/sccp/sccp_test.c b/openbsc/tests/sccp/sccp_test.c index 0c2adc83f..31eb47f9f 100644 --- a/openbsc/tests/sccp/sccp_test.c +++ b/openbsc/tests/sccp/sccp_test.c @@ -264,6 +264,10 @@ static const u_int8_t it_test[] = { 0x10, 0x01, 0x07, 0x94, 0x01, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00 }; +static const u_int8_t proto_err[] = { +0x0f, 0x0c, 0x04, 0x00, 0x00, +}; + static const struct sccp_parse_header_result parse_result[] = { { .msg_type = SCCP_MSG_TYPE_IT, @@ -287,6 +291,21 @@ static const struct sccp_parse_header_result parse_result[] = { .input = it_test, .input_len = sizeof(it_test), }, + { + .msg_type = SCCP_MSG_TYPE_ERR, + .wanted_len = 0, + .src_ssn = -1, + .dst_ssn = -1, + .has_src_ref = 0, + .has_dst_ref = 1, + .dst_ref = { + .octet1 = 0x0c, + .octet2 = 0x04, + .octet3 = 0x00, + }, + .input = proto_err, + .input_len = sizeof(proto_err), + }, }; @@ -777,7 +796,7 @@ static void test_sccp_parsing(void) memset(&result, 0, sizeof(result)); if (sccp_parse_header(msg, &result) != 0) { - fprintf(stderr, "Failed to parse test: %d\n", current_test); + fprintf(stderr, "Failed to sccp parse test: %d\n", current_test); } else { if (parse_result[current_test].wanted_len != result.data_len) { fprintf(stderr, "Unexpected data length.\n"); diff --git a/wireshark/abis_oml.patch b/wireshark/abis_oml.patch index 9f06b4d82..40132110e 100644 --- a/wireshark/abis_oml.patch +++ b/wireshark/abis_oml.patch @@ -1,8 +1,21 @@ -Index: wireshark/epan/dissectors/Makefile.common -=================================================================== ---- wireshark.orig/epan/dissectors/Makefile.common -+++ wireshark/epan/dissectors/Makefile.common -@@ -474,6 +474,7 @@ +From 5857518be87641fdab45e593bc9fd5ef5595e619 Mon Sep 17 00:00:00 2001 +From: Holger Hans Peter Freyther <zecke@selfish.org> +Date: Mon, 19 Apr 2010 13:23:51 +0800 +Subject: [PATCH 1/2] Add the Abis OML patch. + +--- + epan/dissectors/Makefile.common | 1 + + epan/dissectors/packet-gsm_abis_oml.c | 1382 +++++++++++++++++++++++++++++++++ + epan/dissectors/packet-gsm_abis_oml.h | 787 +++++++++++++++++++ + 3 files changed, 2170 insertions(+), 0 deletions(-) + create mode 100644 epan/dissectors/packet-gsm_abis_oml.c + create mode 100644 epan/dissectors/packet-gsm_abis_oml.h + +diff --git a/epan/dissectors/Makefile.common b/epan/dissectors/Makefile.common +index dbc3726..98dcdc3 100644 +--- a/epan/dissectors/Makefile.common ++++ b/epan/dissectors/Makefile.common +@@ -481,6 +481,7 @@ DISSECTOR_SRC = \ packet-gsm_a_gm.c \ packet-gsm_a_rp.c \ packet-gsm_a_rr.c \ @@ -12,7 +25,7 @@ Index: wireshark/epan/dissectors/Makefile.common packet-gsm_bssmap_le.c \ diff --git a/epan/dissectors/packet-gsm_abis_oml.c b/epan/dissectors/packet-gsm_abis_oml.c new file mode 100644 -index 0000000..2de9dca +index 0000000..fa46ab5 --- /dev/null +++ b/epan/dissectors/packet-gsm_abis_oml.c @@ -0,0 +1,1382 @@ @@ -1398,11 +1411,12 @@ index 0000000..2de9dca + abis_oml_handle = create_dissector_handle(dissect_abis_oml, proto_abis_oml); + dissector_add("lapd.gsm.sapi", LAPD_GSM_SAPI_OM_PROC, abis_oml_handle); +} -Index: wireshark/epan/dissectors/packet-gsm_abis_oml.h -=================================================================== +diff --git a/epan/dissectors/packet-gsm_abis_oml.h b/epan/dissectors/packet-gsm_abis_oml.h +new file mode 100644 +index 0000000..d523e96 --- /dev/null -+++ wireshark/epan/dissectors/packet-gsm_abis_oml.h -@@ -0,0 +1,786 @@ ++++ b/epan/dissectors/packet-gsm_abis_oml.h +@@ -0,0 +1,787 @@ +/* GSM Network Management messages on the A-bis interface + * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ + @@ -2190,3 +2204,6 @@ Index: wireshark/epan/dissectors/packet-gsm_abis_oml.h +}; + +#endif /* _NM_H */ +-- +1.7.0.1 + diff --git a/wireshark/rsl-ipaccess.patch b/wireshark/rsl-ipaccess.patch index 36c09c57b..29220b87b 100644 --- a/wireshark/rsl-ipaccess.patch +++ b/wireshark/rsl-ipaccess.patch @@ -1,16 +1,25 @@ -Index: wireshark/epan/dissectors/packet-rsl.c -=================================================================== ---- wireshark.orig/epan/dissectors/packet-rsl.c 2009-10-21 23:03:41.000000000 +0200 -+++ wireshark/epan/dissectors/packet-rsl.c 2009-10-22 10:02:51.000000000 +0200 +From 8f35d623641dbba90e6186604c11e892bf515ecc Mon Sep 17 00:00:00 2001 +From: Holger Hans Peter Freyther <zecke@selfish.org> +Date: Mon, 19 Apr 2010 13:32:58 +0800 +Subject: [PATCH 2/2] RSL patch + +--- + epan/dissectors/packet-rsl.c | 522 +++++++++++++++++++++++++++++++++++++++++- + 1 files changed, 515 insertions(+), 7 deletions(-) + +diff --git a/epan/dissectors/packet-rsl.c b/epan/dissectors/packet-rsl.c +index b10a671..a455cf3 100644 +--- a/epan/dissectors/packet-rsl.c ++++ b/epan/dissectors/packet-rsl.c @@ -2,6 +2,7 @@ * Routines for Radio Signalling Link (RSL) dissection. * * Copyright 2007, Anders Broman <anders.broman@ericsson.com> + * Copyright 2009, Harald Welte <laforge@gnumonks.org> * - * $Id: packet-rsl.c 29944 2009-09-16 13:39:37Z morriss $ + * $Id$ * -@@ -44,6 +45,8 @@ +@@ -42,6 +43,8 @@ #include <epan/lapd_sapi.h> #include "packet-gsm_a_common.h" @@ -19,7 +28,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c /* Initialize the protocol and registered fields */ static int proto_rsl = -1; -@@ -117,6 +120,24 @@ +@@ -115,6 +118,24 @@ static int hf_rsl_emlpp_prio = -1; static int hf_rsl_rtd = -1; static int hf_rsl_delay_ind = -1; static int hf_rsl_tfo = -1; @@ -44,7 +53,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c /* Initialize the subtree pointers */ static int ett_rsl = -1; -@@ -174,6 +195,15 @@ +@@ -172,6 +193,15 @@ static int ett_ie_cause = -1; static int ett_ie_meas_res_no = -1; static int ett_ie_message_id = -1; static int ett_ie_sys_info_type = -1; @@ -60,7 +69,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c proto_tree *top_tree; dissector_handle_t gsm_a_ccch_handle; -@@ -209,8 +239,11 @@ +@@ -207,8 +237,11 @@ static const value_string rsl_msg_disc_vals[] = { { 0x06, "Common Channel Management messages" }, { 0x08, "TRX Management messages" }, { 0x16, "Location Services messages" }, @@ -72,7 +81,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c /* * 9.2 MESSAGE TYPE */ -@@ -277,6 +310,49 @@ +@@ -275,6 +308,49 @@ static const value_string rsl_msg_disc_vals[] = { /* 0 1 - - - - - - Location Services messages: */ #define RSL_MSG_LOC_INF 65 /* 8.7.1 */ @@ -90,16 +99,16 @@ Index: wireshark/epan/dissectors/packet-rsl.c +#define RSL_MSG_TYPE_IPAC_PDCH_DEACT_ACK 0x4c +#define RSL_MSG_TYPE_IPAC_PDCH_DEACT_NACK 0x4d + -+#define RSL_MSG_TYPE_IPAC_BIND 0x70 -+#define RSL_MSG_TYPE_IPAC_BIND_ACK 0x71 -+#define RSL_MSG_TYPE_IPAC_BIND_NACK 0x72 -+#define RSL_MSG_TYPE_IPAC_CONNECT 0x73 -+#define RSL_MSG_TYPE_IPAC_CONNECT_ACK 0x74 -+#define RSL_MSG_TYPE_IPAC_CONNECT_NACK 0x75 -+#define RSL_MSG_TYPE_IPAC_DISC_IND 0x76 -+#define RSL_MSG_TYPE_IPAC_DISC 0x77 -+#define RSL_MSG_TYPE_IPAC_DISC_ACK 0x78 -+#define RSL_MSG_TYPE_IPAC_DISC_NACK 0x79 ++#define RSL_MSG_TYPE_IPAC_CRCX 0x70 ++#define RSL_MSG_TYPE_IPAC_CRCX_ACK 0x71 ++#define RSL_MSG_TYPE_IPAC_CRCX_NACK 0x72 ++#define RSL_MSG_TYPE_IPAC_MDCX 0x73 ++#define RSL_MSG_TYPE_IPAC_MDCX_ACK 0x74 ++#define RSL_MSG_TYPE_IPAC_MDCX_NACK 0x75 ++#define RSL_MSG_TYPE_IPAC_DLCX_IND 0x76 ++#define RSL_MSG_TYPE_IPAC_DLCX 0x77 ++#define RSL_MSG_TYPE_IPAC_DLCX_ACK 0x78 ++#define RSL_MSG_TYPE_IPAC_DLCX_NACK 0x79 + +#define RSL_IE_IPAC_SRTP_CONFIG 0xe0 +#define RSL_IE_IPAC_PROXY_UDP 0xe1 @@ -122,7 +131,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c static const value_string rsl_msg_type_vals[] = { /* 0 0 0 0 - - - - Radio Link Layer Management messages: */ -@@ -339,6 +415,26 @@ +@@ -337,6 +413,26 @@ static const value_string rsl_msg_type_vals[] = { { 0x3f, "TFO MODification REQuest" }, /* 8.4.31 */ /* 0 1 - - - - - - Location Services messages: */ { 0x41, "Location Information" }, /* 8.7.1 */ @@ -149,7 +158,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c { 0, NULL } }; -@@ -372,10 +468,10 @@ static const value_string rsl_msg_type_vals[] = { +@@ -370,10 +466,10 @@ static const value_string rsl_msg_type_vals[] = { #define RSL_IE_MESSAGE_ID 28 #define RSL_IE_SYS_INFO_TYPE 30 @@ -164,7 +173,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c #define RSL_IE_FULL_IMM_ASS_INF 35 #define RSL_IE_SMSCB_INF 36 #define RSL_IE_FULL_MS_TIMING_OFFSET 37 -@@ -478,6 +574,24 @@ +@@ -476,6 +572,24 @@ static const value_string rsl_ie_type_vals[] = { Not used */ @@ -189,7 +198,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c { 0, NULL } }; -@@ -514,6 +628,96 @@ +@@ -512,6 +626,96 @@ static const value_string rsl_ch_no_Cbits_vals[] = { { 0, NULL } }; @@ -286,7 +295,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c /* 9.3.1 Channel number 9.3.1 M TV 2 */ static int dissect_rsl_ie_ch_no(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gboolean is_mandatory) -@@ -2044,7 +2248,6 @@ +@@ -2042,7 +2246,6 @@ dissect_rsl_ie_err_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int proto_item_set_len(ti, length+2); proto_tree_add_item(ie_tree, hf_rsl_ie_length, tvb, offset, 1, FALSE); @@ -294,7 +303,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c /* Received Message */ offset = dissct_rsl_msg(tvb, pinfo, ie_tree, offset); -@@ -2909,12 +3112,183 @@ +@@ -2907,12 +3110,184 @@ dissect_rsl_ie_tfo_transp_cont(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree } static int @@ -310,16 +319,16 @@ Index: wireshark/epan/dissectors/packet-rsl.c + +#if 0 + switch (msg_type) { -+ case RSL_MSG_TYPE_IPAC_BIND: -+ case RSL_MSG_TYPE_IPAC_BIND_ACK: -+ case RSL_MSG_TYPE_IPAC_BIND_NACK: -+ case RSL_MSG_TYPE_IPAC_CONNECT: -+ case RSL_MSG_TYPE_IPAC_CONNECT_ACK: -+ case RSL_MSG_TYPE_IPAC_CONNECT_NACK: -+ case RSL_MSG_TYPE_IPAC_DISC_IND: -+ case RSL_MSG_TYPE_IPAC_DISC: -+ case RSL_MSG_TYPE_IPAC_DISC_ACK: -+ case RSL_MSG_TYPE_IPAC_DISC_NACK: ++ case RSL_MSG_TYPE_IPAC_CRCX: ++ case RSL_MSG_TYPE_IPAC_CRCX_ACK: ++ case RSL_MSG_TYPE_IPAC_CRCX_NACK: ++ case RSL_MSG_TYPE_IPAC_MDCX: ++ case RSL_MSG_TYPE_IPAC_MDCX_ACK: ++ case RSL_MSG_TYPE_IPAC_MDCX_NACK: ++ case RSL_MSG_TYPE_IPAC_DLCX_IND: ++ case RSL_MSG_TYPE_IPAC_DLCX: ++ case RSL_MSG_TYPE_IPAC_DLCX_ACK: ++ case RSL_MSG_TYPE_IPAC_DLCX_NACK: + case RSL_MSG_TYPE_IPAC_PDCH_ACT: + case RSL_MSG_TYPE_IPAC_PDCH_ACT_ACK: + case RSL_MSG_TYPE_IPAC_PDCH_ACT_NACK: @@ -449,7 +458,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c + } + + switch (msg_type) { -+ case RSL_MSG_TYPE_IPAC_BIND_ACK: ++ case RSL_MSG_TYPE_IPAC_CRCX_ACK: + /* Notify the RTP and RTCP dissectors about a new RTP stream */ + src_addr.type = AT_IPv4; + src_addr.len = 4; @@ -480,7 +489,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c offset++; switch (msg_type){ -@@ -3482,6 +3856,18 @@ +@@ -3480,6 +3855,18 @@ dissct_rsl_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset) /* LLP APDU 9.3.58 M LV 2-N */ offset = dissect_rsl_ie_llp_apdu(tvb, pinfo, tree, offset, TRUE); break; @@ -499,7 +508,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c default: break; } -@@ -3489,6 +3875,40 @@ +@@ -3487,6 +3874,40 @@ dissct_rsl_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset) return offset; } @@ -540,7 +549,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c static void dissect_rsl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { -@@ -3516,7 +3936,6 @@ +@@ -3514,7 +3935,6 @@ dissect_rsl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) /* 9.1 Message discriminator */ proto_tree_add_item(rsl_tree, hf_rsl_msg_dsc, tvb, offset, 1, FALSE); proto_tree_add_item(rsl_tree, hf_rsl_T_bit, tvb, offset, 1, FALSE); @@ -548,7 +557,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c offset = dissct_rsl_msg(tvb, pinfo, rsl_tree, offset); -@@ -3886,6 +4305,86 @@ +@@ -3884,6 +4304,86 @@ void proto_register_rsl(void) FT_UINT8, BASE_DEC, VALS(rsl_emlpp_prio_vals), 0x03, NULL, HFILL } }, @@ -635,7 +644,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c }; static gint *ett[] = { &ett_rsl, -@@ -3943,6 +4442,14 @@ +@@ -3941,6 +4441,14 @@ void proto_register_rsl(void) &ett_ie_meas_res_no, &ett_ie_message_id, &ett_ie_sys_info_type, @@ -650,3 +659,6 @@ Index: wireshark/epan/dissectors/packet-rsl.c }; /* Register the protocol name and description */ +-- +1.7.0.1 + |