Merge branch 'master' into on-waves/mgcp
This commit is contained in:
commit
7373109abc
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
|
@ -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 */
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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 */
|
||||
|
|
|
@ -29,6 +29,9 @@ enum {
|
|||
DHO,
|
||||
DDB,
|
||||
DREF,
|
||||
DGPRS,
|
||||
DNS,
|
||||
DBSSGP,
|
||||
Debug_LastEntry,
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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
|
|
@ -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 */
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,9 @@
|
|||
!
|
||||
! OpenBSC configuration saved from vty
|
||||
! !
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
sgsn
|
||||
nsip local port 23000
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ! */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
struct gsm_meas_rep *mr;
|
||||
int idx;
|
||||
|
||||
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);
|
||||
/* 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->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);
|
||||
|
||||
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);
|
||||
|
|
|
@ -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,26 +242,10 @@ 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")
|
||||
{
|
||||
struct telnet_connection *conn;
|
||||
|
||||
conn = (struct telnet_connection *) vty->priv;
|
||||
if (!conn->dbg) {
|
||||
vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
log_set_log_level(conn->dbg, atoi(argv[0]));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(diable_logging,
|
||||
disable_logging_cmd,
|
||||
"logging disable",
|
||||
LOGGING_STR
|
||||
"Disables logging to this vty\n")
|
||||
{
|
||||
struct telnet_connection *conn;
|
||||
|
@ -228,16 +262,75 @@ DEFUN(diable_logging,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
conn = (struct telnet_connection *) vty->priv;
|
||||
if (!conn->dbg) {
|
||||
vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
vty_print_logtarget(vty, &log_info, conn->dbg);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net)
|
||||
{
|
||||
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(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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue