aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <zecke@selfish.org>2010-05-14 02:42:15 +0800
committerHolger Hans Peter Freyther <zecke@selfish.org>2010-05-14 02:42:15 +0800
commit7373109abc9829c49eabf26e951a0ec6647666ac (patch)
treef508489f955c15fc36376374072cfb8ce6a9e6de
parentc1cb5eb38d5199de9049c59dc080051deba7aa53 (diff)
parentcf5cc5bb5b472f470eb9584a32dee3c38413f93a (diff)
Merge branch 'master' into on-waves/mgcp
-rw-r--r--libosmocore/.gitignore5
-rw-r--r--libosmocore/include/osmocore/Makefile.am2
-rw-r--r--libosmocore/include/osmocore/gsm0808.h41
-rw-r--r--libosmocore/include/osmocore/gsm48.h17
-rw-r--r--libosmocore/include/osmocore/gsm_utils.h19
-rw-r--r--libosmocore/include/osmocore/logging.h5
-rw-r--r--libosmocore/include/osmocore/msgb.h20
-rw-r--r--libosmocore/include/osmocore/protocol/Makefile.am2
-rw-r--r--libosmocore/include/osmocore/protocol/gsm_04_08.h10
-rw-r--r--libosmocore/include/osmocore/protocol/gsm_08_08.h303
-rw-r--r--libosmocore/include/osmocore/rate_ctr.h81
-rw-r--r--libosmocore/include/osmocore/write_queue.h1
-rw-r--r--libosmocore/src/Makefile.am2
-rw-r--r--libosmocore/src/gsm0808.c295
-rw-r--r--libosmocore/src/gsm48.c94
-rw-r--r--libosmocore/src/gsm48_ie.c2
-rw-r--r--libosmocore/src/gsm_utils.c15
-rw-r--r--libosmocore/src/logging.c78
-rw-r--r--libosmocore/src/msgb.c5
-rw-r--r--libosmocore/src/rate_ctr.c122
-rw-r--r--libosmocore/src/select.c3
-rw-r--r--libosmocore/src/write_queue.c3
-rw-r--r--openbsc/README2
-rw-r--r--openbsc/configure.in4
-rw-r--r--openbsc/include/openbsc/Makefile.am4
-rw-r--r--openbsc/include/openbsc/abis_nm.h7
-rw-r--r--openbsc/include/openbsc/abis_rsl.h3
-rw-r--r--openbsc/include/openbsc/chan_alloc.h1
-rw-r--r--openbsc/include/openbsc/crc24.h8
-rw-r--r--openbsc/include/openbsc/db.h3
-rw-r--r--openbsc/include/openbsc/debug.h3
-rw-r--r--openbsc/include/openbsc/gb_proxy.h42
-rw-r--r--openbsc/include/openbsc/gprs_bssgp.h163
-rw-r--r--openbsc/include/openbsc/gprs_llc.h32
-rw-r--r--openbsc/include/openbsc/gprs_ns.h215
-rw-r--r--openbsc/include/openbsc/gprs_sgsn.h114
-rw-r--r--openbsc/include/openbsc/gsm_04_08.h10
-rw-r--r--openbsc/include/openbsc/gsm_04_08_gprs.h350
-rw-r--r--openbsc/include/openbsc/gsm_data.h74
-rw-r--r--openbsc/include/openbsc/ipaccess.h2
-rw-r--r--openbsc/include/openbsc/rest_octets.h11
-rw-r--r--openbsc/include/openbsc/rtp_proxy.h6
-rw-r--r--openbsc/include/openbsc/sgsn.h30
-rw-r--r--openbsc/include/openbsc/signal.h18
-rw-r--r--openbsc/include/openbsc/vty.h4
-rw-r--r--openbsc/include/sccp/sccp_types.h6
-rw-r--r--openbsc/include/vty/buffer.h2
-rw-r--r--openbsc/include/vty/command.h25
-rw-r--r--openbsc/src/Makefile.am26
-rw-r--r--openbsc/src/abis_nm.c92
-rw-r--r--openbsc/src/abis_rsl.c94
-rw-r--r--openbsc/src/bs11_config.c5
-rw-r--r--openbsc/src/bsc_init.c108
-rw-r--r--openbsc/src/chan_alloc.c15
-rw-r--r--openbsc/src/db.c63
-rw-r--r--openbsc/src/debug.c45
-rw-r--r--openbsc/src/e1_input.c10
-rw-r--r--openbsc/src/gprs/Makefile.am18
-rw-r--r--openbsc/src/gprs/crc24.c69
-rw-r--r--openbsc/src/gprs/gb_proxy.c580
-rw-r--r--openbsc/src/gprs/gb_proxy_main.c188
-rw-r--r--openbsc/src/gprs/gb_proxy_vty.c183
-rw-r--r--openbsc/src/gprs/gprs_bssgp.c448
-rw-r--r--openbsc/src/gprs/gprs_bssgp_util.c119
-rw-r--r--openbsc/src/gprs/gprs_llc.c549
-rw-r--r--openbsc/src/gprs/gprs_ns.c926
-rw-r--r--openbsc/src/gprs/gprs_ns_vty.c292
-rw-r--r--openbsc/src/gprs/gprs_sgsn.c96
-rw-r--r--openbsc/src/gprs/gprs_sndcp.c70
-rw-r--r--openbsc/src/gprs/gsm_04_08_gprs.c762
-rw-r--r--openbsc/src/gprs/osmo_gbproxy.cfg13
-rw-r--r--openbsc/src/gprs/osmo_sgsn.cfg9
-rw-r--r--openbsc/src/gprs/sgsn_main.c183
-rw-r--r--openbsc/src/gprs/sgsn_vty.c146
-rw-r--r--openbsc/src/gsm_04_08_utils.c11
-rw-r--r--openbsc/src/gsm_04_11.c2
-rw-r--r--openbsc/src/gsm_04_80.c2
-rw-r--r--openbsc/src/gsm_data.c58
-rw-r--r--openbsc/src/handover_logic.c3
-rw-r--r--openbsc/src/input/ipaccess.c209
-rw-r--r--openbsc/src/ipaccess/Makefile.am13
-rw-r--r--openbsc/src/ipaccess/ipaccess-config.c68
-rw-r--r--openbsc/src/mgcp/mgcp_network.c2
-rw-r--r--openbsc/src/mgcp/mgcp_vty.c4
-rw-r--r--openbsc/src/openbsc.cfg.nanobts41
-rw-r--r--openbsc/src/openbsc.cfg.nanobts.multitrx97
-rw-r--r--openbsc/src/paging.c78
-rw-r--r--openbsc/src/rest_octets.c35
-rw-r--r--openbsc/src/rtp_proxy.c3
-rw-r--r--openbsc/src/sccp/sccp.c126
-rw-r--r--openbsc/src/socket.c94
-rw-r--r--openbsc/src/system_information.c23
-rw-r--r--openbsc/src/vty/buffer.c6
-rw-r--r--openbsc/src/vty/command.c20
-rw-r--r--openbsc/src/vty/utils.c50
-rw-r--r--openbsc/src/vty/vty.c19
-rw-r--r--openbsc/src/vty_interface.c430
-rw-r--r--openbsc/src/vty_interface_cmds.c149
-rw-r--r--openbsc/src/vty_interface_layer3.c53
-rw-r--r--openbsc/tests/sccp/sccp_test.c21
-rw-r--r--wireshark/abis_oml.patch37
-rw-r--r--wireshark/rsl-ipaccess.patch96
102 files changed, 8482 insertions, 641 deletions
diff --git a/libosmocore/.gitignore b/libosmocore/.gitignore
index d61cdc589..06904fa09 100644
--- a/libosmocore/.gitignore
+++ b/libosmocore/.gitignore
@@ -7,6 +7,7 @@ Makefile.in
*.la
*.pc
aclocal.m4
+m4/*.m4
autom4te.cache
config.h*
config.sub
@@ -23,3 +24,7 @@ libtool
.tarball-version
.version
+
+tests/sms/sms_test
+tests/timer/timer_test
+
diff --git a/libosmocore/include/osmocore/Makefile.am b/libosmocore/include/osmocore/Makefile.am
index 1c3a33f33..fde010271 100644
--- a/libosmocore/include/osmocore/Makefile.am
+++ b/libosmocore/include/osmocore/Makefile.am
@@ -1,7 +1,7 @@
osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h \
tlv.h bitvec.h comp128.h statistics.h gsm_utils.h utils.h \
gsmtap.h write_queue.h rsl.h gsm48.h rxlev_stat.h mncc.h \
- gsm48_ie.h logging.h
+ gsm48_ie.h logging.h gsm0808.h rate_ctr.h
if ENABLE_TALLOC
osmocore_HEADERS += talloc.h
diff --git a/libosmocore/include/osmocore/gsm0808.h b/libosmocore/include/osmocore/gsm0808.h
new file mode 100644
index 000000000..a40713f77
--- /dev/null
+++ b/libosmocore/include/osmocore/gsm0808.h
@@ -0,0 +1,41 @@
+/* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009,2010 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#ifndef OSMOCORE_GSM0808_H
+#define OSMOCORE_GSM0808_H
+
+#include "tlv.h"
+
+struct msgb;
+
+struct msgb *gsm0808_create_layer3(struct msgb *msg, uint16_t netcode, uint16_t countrycode, int lac, int ci);
+struct msgb *gsm0808_create_reset(void);
+struct msgb *gsm0808_create_clear_complete(void);
+struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id);
+struct msgb *gsm0808_create_cipher_reject(uint8_t cause);
+struct msgb *gsm0808_create_classmark_update(const uint8_t *classmark, uint8_t length);
+struct msgb *gsm0808_create_sapi_reject(uint8_t link_id);
+struct msgb *gsm0808_create_assignment_completed(struct gsm_lchan *lchan, uint8_t rr_cause,
+ uint8_t chosen_channel, uint8_t encr_alg_id,
+ uint8_t speech_mode);
+struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause);
+
+const struct tlv_definition *gsm0808_att_tlvdef();
+
+#endif
diff --git a/libosmocore/include/osmocore/gsm48.h b/libosmocore/include/osmocore/gsm48.h
index 1e963573c..be9fbbd14 100644
--- a/libosmocore/include/osmocore/gsm48.h
+++ b/libosmocore/include/osmocore/gsm48.h
@@ -1,9 +1,18 @@
#ifndef _OSMOCORE_GSM48_H
+#define _OSMOCORE_GSM48_H
#include <osmocore/tlv.h>
#include <osmocore/protocol/gsm_04_08.h>
#include <osmocore/gsm48_ie.h>
+/* A parsed GPRS routing area */
+struct gprs_ra_id {
+ uint16_t mnc;
+ uint16_t mcc;
+ uint16_t lac;
+ uint8_t rac;
+};
+
extern const struct tlv_definition gsm48_att_tlvdef;
const char *gsm48_cc_state_name(uint8_t state);
const char *gsm48_cc_msg_name(uint8_t msgtype);
@@ -14,4 +23,12 @@ void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc,
int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi);
int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi);
+/* Convert Mobile Identity (10.5.1.4) to string */
+int gsm48_mi_to_string(char *string, const int str_len,
+ const uint8_t *mi, const int mi_len);
+
+/* Parse Routeing Area Identifier */
+void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf);
+int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid);
+
#endif
diff --git a/libosmocore/include/osmocore/gsm_utils.h b/libosmocore/include/osmocore/gsm_utils.h
index c87e967bd..51e9f2e6a 100644
--- a/libosmocore/include/osmocore/gsm_utils.h
+++ b/libosmocore/include/osmocore/gsm_utils.h
@@ -27,6 +27,13 @@
#include <stdint.h>
+#define ADD_MODULO(sum, delta, modulo) do { \
+ if ((sum += delta) >= modulo) \
+ sum -= modulo; \
+ } while (0)
+
+#define GSM_MAX_FN (26*51*2048)
+
struct gsm_time {
uint32_t fn; /* FN count */
uint16_t t1; /* FN div (26*51) */
@@ -80,5 +87,17 @@ void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn);
/* Convert from GSM time to frame number */
uint32_t gsm_gsmtime2fn(struct gsm_time *time);
+/* GSM TS 03.03 Chapter 2.6 */
+enum gprs_tlli_tyoe {
+ TLLI_LOCAL,
+ TLLI_FOREIGN,
+ TLLI_RANDOM,
+ TLLI_AUXILIARY,
+ TLLI_RESERVED,
+};
+
+/* TS 03.03 Chapter 2.6 */
+int gprs_tlli_type(uint32_t tlli);
+
void generate_backtrace();
#endif
diff --git a/libosmocore/include/osmocore/logging.h b/libosmocore/include/osmocore/logging.h
index 93f18a07b..2e82959a8 100644
--- a/libosmocore/include/osmocore/logging.h
+++ b/libosmocore/include/osmocore/logging.h
@@ -117,6 +117,7 @@ void log_set_print_timestamp(struct log_target *target, int);
void log_set_log_level(struct log_target *target, int log_level);
void log_parse_category_mask(struct log_target *target, const char* mask);
int log_parse_level(const char *lvl);
+const char *log_level_str(unsigned int lvl);
int log_parse_category(const char *category);
void log_set_category_filter(struct log_target *target, int category,
int enable, int level);
@@ -127,4 +128,8 @@ struct log_target *log_target_create_stderr(void);
void log_add_target(struct log_target *target);
void log_del_target(struct log_target *target);
+/* Gernerate command argument strings for VTY use */
+const char *log_vty_category_string(struct log_info *info);
+const char *log_vty_level_string(struct log_info *info);
+
#endif /* _OSMOCORE_LOGGING_H */
diff --git a/libosmocore/include/osmocore/msgb.h b/libosmocore/include/osmocore/msgb.h
index 31db71942..2841dc56e 100644
--- a/libosmocore/include/osmocore/msgb.h
+++ b/libosmocore/include/osmocore/msgb.h
@@ -23,15 +23,11 @@
#include <stdint.h>
#include "linuxlist.h"
-struct bts_link;
-
struct msgb {
struct llist_head list;
- /* ptr to the physical E1 link to the BTS(s) */
- struct gsm_bts_link *bts_link;
-
/* Part of which TRX logical channel we were received / transmitted */
+ /* FIXME: move them into the control buffer */
struct gsm_bts_trx *trx;
struct gsm_lchan *lchan;
@@ -41,17 +37,11 @@ struct msgb {
unsigned char *l2h;
/* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */
unsigned char *l3h;
-
/* the layer 4 header */
- union {
- unsigned char *smsh;
- unsigned char *llch;
- unsigned char *l4h;
- };
+ unsigned char *l4h;
- /* the layer 5 header, GPRS: GMM header */
- unsigned char *gmmh;
- uint32_t tlli;
+ /* the 'control buffer', large enough to contain 5 pointers */
+ unsigned long cb[5];
uint16_t data_len;
uint16_t len;
@@ -71,7 +61,7 @@ extern void msgb_reset(struct msgb *m);
#define msgb_l1(m) ((void *)(m->l1h))
#define msgb_l2(m) ((void *)(m->l2h))
#define msgb_l3(m) ((void *)(m->l3h))
-#define msgb_sms(m) ((void *)(m->smsh))
+#define msgb_sms(m) ((void *)(m->l4h))
static inline unsigned int msgb_l1len(const struct msgb *msgb)
{
diff --git a/libosmocore/include/osmocore/protocol/Makefile.am b/libosmocore/include/osmocore/protocol/Makefile.am
index 6d8883e61..557950ec8 100644
--- a/libosmocore/include/osmocore/protocol/Makefile.am
+++ b/libosmocore/include/osmocore/protocol/Makefile.am
@@ -1,3 +1,3 @@
-osmocore_proto_HEADERS = gsm_04_08.h gsm_04_11.h gsm_04_80.h gsm_08_58.h gsm_12_21.h
+osmocore_proto_HEADERS = gsm_04_08.h gsm_04_11.h gsm_04_80.h gsm_08_58.h gsm_12_21.h gsm_08_08.h
osmocore_protodir = $(includedir)/osmocore/protocol
diff --git a/libosmocore/include/osmocore/protocol/gsm_04_08.h b/libosmocore/include/osmocore/protocol/gsm_04_08.h
index 801b9b54c..1a112a085 100644
--- a/libosmocore/include/osmocore/protocol/gsm_04_08.h
+++ b/libosmocore/include/osmocore/protocol/gsm_04_08.h
@@ -685,6 +685,7 @@ enum chreq_type {
/* Chapter 5.1.2.2 */
#define GSM_CSTATE_NULL 0
#define GSM_CSTATE_INITIATED 1
+#define GSM_CSTATE_MM_CONNECTION_PEND 2 /* see 10.5.4.6 */
#define GSM_CSTATE_MO_CALL_PROC 3
#define GSM_CSTATE_CALL_DELIVERED 4
#define GSM_CSTATE_CALL_PRESENT 6
@@ -734,10 +735,17 @@ enum gsm48_bcap_rrq {
GSM48_BCAP_RRQ_DUAL_FR = 3,
};
-
#define GSM48_TMSI_LEN 5
#define GSM48_MID_TMSI_LEN (GSM48_TMSI_LEN + 2)
#define GSM48_MI_SIZE 32
+/* Chapter 10.4.4.15 */
+struct gsm48_ra_id {
+ uint8_t digits[3]; /* MCC + MNC BCD digits */
+ uint16_t lac; /* Location Area Code */
+ uint8_t rac; /* Routing Area Code */
+} __attribute__ ((packed));
+
+
#endif /* PROTO_GSM_04_08_H */
diff --git a/libosmocore/include/osmocore/protocol/gsm_08_08.h b/libosmocore/include/osmocore/protocol/gsm_08_08.h
new file mode 100644
index 000000000..6b8f93595
--- /dev/null
+++ b/libosmocore/include/osmocore/protocol/gsm_08_08.h
@@ -0,0 +1,303 @@
+/* From GSM08.08 */
+
+#ifndef GSM_0808_H
+#define GSM_0808_H
+
+#include <stdlib.h>
+
+/*
+ * this is from GSM 03.03 CGI but is copied in GSM 08.08
+ * in § 3.2.2.27 for Cell Identifier List
+ */
+enum CELL_IDENT {
+ CELL_IDENT_WHOLE_GLOBAL = 0,
+ CELL_IDENT_LAC_AND_CI = 1,
+ CELL_IDENT_CI = 2,
+ CELL_IDENT_NO_CELL = 3,
+ CELL_IDENT_LAI_AND_LAC = 4,
+ CELL_IDENT_LAC = 5,
+ CELL_IDENT_BSS = 6,
+ CELL_IDENT_UTRAN_PLMN_LAC_RNC = 8,
+ CELL_IDENT_UTRAN_RNC = 9,
+ CELL_IDENT_UTRAN_LAC_RNC = 10,
+};
+
+
+/* GSM 08.06 § 6.3 */
+enum BSSAP_MSG_TYPE {
+ BSSAP_MSG_BSS_MANAGEMENT = 0x0,
+ BSSAP_MSG_DTAP = 0x1,
+};
+
+struct bssmap_header {
+ uint8_t type;
+ uint8_t length;
+} __attribute__((packed));
+
+struct dtap_header {
+ uint8_t type;
+ uint8_t link_id;
+ uint8_t length;
+} __attribute__((packed));
+
+
+enum BSS_MAP_MSG_TYPE {
+ BSS_MAP_MSG_RESERVED_0 = 0,
+
+ /* ASSIGNMENT MESSAGES */
+ BSS_MAP_MSG_ASSIGMENT_RQST = 1,
+ BSS_MAP_MSG_ASSIGMENT_COMPLETE = 2,
+ BSS_MAP_MSG_ASSIGMENT_FAILURE = 3,
+
+ /* HANDOVER MESSAGES */
+ BSS_MAP_MSG_HANDOVER_RQST = 16,
+ BSS_MAP_MSG_HANDOVER_REQUIRED = 17,
+ BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE= 18,
+ BSS_MAP_MSG_HANDOVER_CMD = 19,
+ BSS_MAP_MSG_HANDOVER_COMPLETE = 20,
+ BSS_MAP_MSG_HANDOVER_SUCCEEDED = 21,
+ BSS_MAP_MSG_HANDOVER_FAILURE = 22,
+ BSS_MAP_MSG_HANDOVER_PERFORMED = 23,
+ BSS_MAP_MSG_HANDOVER_CANDIDATE_ENQUIRE = 24,
+ BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE = 25,
+ BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT = 26,
+ BSS_MAP_MSG_HANDOVER_DETECT = 27,
+
+ /* RELEASE MESSAGES */
+ BSS_MAP_MSG_CLEAR_CMD = 32,
+ BSS_MAP_MSG_CLEAR_COMPLETE = 33,
+ BSS_MAP_MSG_CLEAR_RQST = 34,
+ BSS_MAP_MSG_RESERVED_1 = 35,
+ BSS_MAP_MSG_RESERVED_2 = 36,
+ BSS_MAP_MSG_SAPI_N_REJECT = 37,
+ BSS_MAP_MSG_CONFUSION = 38,
+
+ /* OTHER CONNECTION RELATED MESSAGES */
+ BSS_MAP_MSG_SUSPEND = 40,
+ BSS_MAP_MSG_RESUME = 41,
+ BSS_MAP_MSG_CONNECTION_ORIENTED_INFORMATION = 42,
+ BSS_MAP_MSG_PERFORM_LOCATION_RQST = 43,
+ BSS_MAP_MSG_LSA_INFORMATION = 44,
+ BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE = 45,
+ BSS_MAP_MSG_PERFORM_LOCATION_ABORT = 46,
+ BSS_MAP_MSG_COMMON_ID = 47,
+
+ /* GENERAL MESSAGES */
+ BSS_MAP_MSG_RESET = 48,
+ BSS_MAP_MSG_RESET_ACKNOWLEDGE = 49,
+ BSS_MAP_MSG_OVERLOAD = 50,
+ BSS_MAP_MSG_RESERVED_3 = 51,
+ BSS_MAP_MSG_RESET_CIRCUIT = 52,
+ BSS_MAP_MSG_RESET_CIRCUIT_ACKNOWLEDGE = 53,
+ BSS_MAP_MSG_MSC_INVOKE_TRACE = 54,
+ BSS_MAP_MSG_BSS_INVOKE_TRACE = 55,
+ BSS_MAP_MSG_CONNECTIONLESS_INFORMATION = 58,
+
+ /* TERRESTRIAL RESOURCE MESSAGES */
+ BSS_MAP_MSG_BLOCK = 64,
+ BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE = 65,
+ BSS_MAP_MSG_UNBLOCK = 66,
+ BSS_MAP_MSG_UNBLOCKING_ACKNOWLEDGE = 67,
+ BSS_MAP_MSG_CIRCUIT_GROUP_BLOCK = 68,
+ BSS_MAP_MSG_CIRCUIT_GROUP_BLOCKING_ACKNOWLEDGE = 69,
+ BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCK = 70,
+ BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCKING_ACKNOWLEDGE = 71,
+ BSS_MAP_MSG_UNEQUIPPED_CIRCUIT = 72,
+ BSS_MAP_MSG_CHANGE_CIRCUIT = 78,
+ BSS_MAP_MSG_CHANGE_CIRCUIT_ACKNOWLEDGE = 79,
+
+ /* RADIO RESOURCE MESSAGES */
+ BSS_MAP_MSG_RESOURCE_RQST = 80,
+ BSS_MAP_MSG_RESOURCE_INDICATION = 81,
+ BSS_MAP_MSG_PAGING = 82,
+ BSS_MAP_MSG_CIPHER_MODE_CMD = 83,
+ BSS_MAP_MSG_CLASSMARK_UPDATE = 84,
+ BSS_MAP_MSG_CIPHER_MODE_COMPLETE = 85,
+ BSS_MAP_MSG_QUEUING_INDICATION = 86,
+ BSS_MAP_MSG_COMPLETE_LAYER_3 = 87,
+ BSS_MAP_MSG_CLASSMARK_RQST = 88,
+ BSS_MAP_MSG_CIPHER_MODE_REJECT = 89,
+ BSS_MAP_MSG_LOAD_INDICATION = 90,
+
+ /* VGCS/VBS */
+ BSS_MAP_MSG_VGCS_VBS_SETUP = 4,
+ BSS_MAP_MSG_VGCS_VBS_SETUP_ACK = 5,
+ BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE = 6,
+ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST = 7,
+ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT = 28,
+ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE = 29,
+ BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION = 30,
+ BSS_MAP_MSG_UPLINK_RQST = 31,
+ BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE = 39,
+ BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION = 73,
+ BSS_MAP_MSG_UPLINK_RELEASE_INDICATION = 74,
+ BSS_MAP_MSG_UPLINK_REJECT_CMD = 75,
+ BSS_MAP_MSG_UPLINK_RELEASE_CMD = 76,
+ BSS_MAP_MSG_UPLINK_SEIZED_CMD = 77,
+};
+
+enum GSM0808_IE_CODING {
+ GSM0808_IE_CIRCUIT_IDENTITY_CODE = 1,
+ GSM0808_IE_RESERVED_0 = 2,
+ GSM0808_IE_RESOURCE_AVAILABLE = 3,
+ GSM0808_IE_CAUSE = 4,
+ GSM0808_IE_CELL_IDENTIFIER = 5,
+ GSM0808_IE_PRIORITY = 6,
+ GSM0808_IE_LAYER_3_HEADER_INFORMATION = 7,
+ GSM0808_IE_IMSI = 8,
+ GSM0808_IE_TMSI = 9,
+ GSM0808_IE_ENCRYPTION_INFORMATION = 10,
+ GSM0808_IE_CHANNEL_TYPE = 11,
+ GSM0808_IE_PERIODICITY = 12,
+ GSM0808_IE_EXTENDED_RESOURCE_INDICATOR = 13,
+ GSM0808_IE_NUMBER_OF_MSS = 14,
+ GSM0808_IE_RESERVED_1 = 15,
+ GSM0808_IE_RESERVED_2 = 16,
+ GSM0808_IE_RESERVED_3 = 17,
+ GSM0808_IE_CLASSMARK_INFORMATION_T2 = 18,
+ GSM0808_IE_CLASSMARK_INFORMATION_T3 = 19,
+ GSM0808_IE_INTERFERENCE_BAND_TO_USE = 20,
+ GSM0808_IE_RR_CAUSE = 21,
+ GSM0808_IE_RESERVED_4 = 22,
+ GSM0808_IE_LAYER_3_INFORMATION = 23,
+ GSM0808_IE_DLCI = 24,
+ GSM0808_IE_DOWNLINK_DTX_FLAG = 25,
+ GSM0808_IE_CELL_IDENTIFIER_LIST = 26,
+ GSM0808_IE_RESPONSE_RQST = 27,
+ GSM0808_IE_RESOURCE_INDICATION_METHOD = 28,
+ GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1 = 29,
+ GSM0808_IE_CIRCUIT_IDENTITY_CODE_LIST = 30,
+ GSM0808_IE_DIAGNOSTIC = 31,
+ GSM0808_IE_LAYER_3_MESSAGE_CONTENTS = 32,
+ GSM0808_IE_CHOSEN_CHANNEL = 33,
+ GSM0808_IE_TOTAL_RESOURCE_ACCESSIBLE = 34,
+ GSM0808_IE_CIPHER_RESPONSE_MODE = 35,
+ GSM0808_IE_CHANNEL_NEEDED = 36,
+ GSM0808_IE_TRACE_TYPE = 37,
+ GSM0808_IE_TRIGGERID = 38,
+ GSM0808_IE_TRACE_REFERENCE = 39,
+ GSM0808_IE_TRANSACTIONID = 40,
+ GSM0808_IE_MOBILE_IDENTITY = 41,
+ GSM0808_IE_OMCID = 42,
+ GSM0808_IE_FORWARD_INDICATOR = 43,
+ GSM0808_IE_CHOSEN_ENCR_ALG = 44,
+ GSM0808_IE_CIRCUIT_POOL = 45,
+ GSM0808_IE_CIRCUIT_POOL_LIST = 46,
+ GSM0808_IE_TIME_INDICATION = 47,
+ GSM0808_IE_RESOURCE_SITUATION = 48,
+ GSM0808_IE_CURRENT_CHANNEL_TYPE_1 = 49,
+ GSM0808_IE_QUEUEING_INDICATOR = 50,
+ GSM0808_IE_SPEECH_VERSION = 64,
+ GSM0808_IE_ASSIGNMENT_REQUIREMENT = 51,
+ GSM0808_IE_TALKER_FLAG = 53,
+ GSM0808_IE_CONNECTION_RELEASE_RQSTED = 54,
+ GSM0808_IE_GROUP_CALL_REFERENCE = 55,
+ GSM0808_IE_EMLPP_PRIORITY = 56,
+ GSM0808_IE_CONFIG_EVO_INDI = 57,
+ GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION = 58,
+ GSM0808_IE_LSA_IDENTIFIER = 59,
+ GSM0808_IE_LSA_IDENTIFIER_LIST = 60,
+ GSM0808_IE_LSA_INFORMATION = 61,
+ GSM0808_IE_LCS_QOS = 62,
+ GSM0808_IE_LSA_ACCESS_CTRL_SUPPR = 63,
+ GSM0808_IE_LCS_PRIORITY = 67,
+ GSM0808_IE_LOCATION_TYPE = 68,
+ GSM0808_IE_LOCATION_ESTIMATE = 69,
+ GSM0808_IE_POSITIONING_DATA = 70,
+ GSM0808_IE_LCS_CAUSE = 71,
+ GSM0808_IE_LCS_CLIENT_TYPE = 72,
+ GSM0808_IE_APDU = 73,
+ GSM0808_IE_NETWORK_ELEMENT_IDENTITY = 74,
+ GSM0808_IE_GPS_ASSISTANCE_DATA = 75,
+ GSM0808_IE_DECIPHERING_KEYS = 76,
+ GSM0808_IE_RETURN_ERROR_RQST = 77,
+ GSM0808_IE_RETURN_ERROR_CAUSE = 78,
+ GSM0808_IE_SEGMENTATION = 79,
+ GSM0808_IE_SERVICE_HANDOVER = 80,
+ GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS = 81,
+ GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000= 82,
+ GSM0808_IE_RESERVED_5 = 65,
+ GSM0808_IE_RESERVED_6 = 66,
+};
+
+enum gsm0808_cause {
+ GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE = 0,
+ GSM0808_CAUSE_RADIO_INTERFACE_FAILURE = 1,
+ GSM0808_CAUSE_UPLINK_QUALITY = 2,
+ GSM0808_CAUSE_UPLINK_STRENGTH = 3,
+ GSM0808_CAUSE_DOWNLINK_QUALITY = 4,
+ GSM0808_CAUSE_DOWNLINK_STRENGTH = 5,
+ GSM0808_CAUSE_DISTANCE = 6,
+ GSM0808_CAUSE_O_AND_M_INTERVENTION = 7,
+ GSM0808_CAUSE_RESPONSE_TO_MSC_INVOCATION = 8,
+ GSM0808_CAUSE_CALL_CONTROL = 9,
+ GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION = 10,
+ GSM0808_CAUSE_HANDOVER_SUCCESSFUL = 11,
+ GSM0808_CAUSE_BETTER_CELL = 12,
+ GSM0808_CAUSE_DIRECTED_RETRY = 13,
+ GSM0808_CAUSE_JOINED_GROUP_CALL_CHANNEL = 14,
+ GSM0808_CAUSE_TRAFFIC = 15,
+ GSM0808_CAUSE_EQUIPMENT_FAILURE = 32,
+ GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE = 33,
+ GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE = 34,
+ GSM0808_CAUSE_CCCH_OVERLOAD = 35,
+ GSM0808_CAUSE_PROCESSOR_OVERLOAD = 36,
+ GSM0808_CAUSE_BSS_NOT_EQUIPPED = 37,
+ GSM0808_CAUSE_MS_NOT_EQUIPPED = 38,
+ GSM0808_CAUSE_INVALID_CELL = 39,
+ GSM0808_CAUSE_TRAFFIC_LOAD = 40,
+ GSM0808_CAUSE_PREEMPTION = 41,
+ GSM0808_CAUSE_RQSTED_TRANSCODING_RATE_ADAPTION_UNAVAILABLE = 48,
+ GSM0808_CAUSE_CIRCUIT_POOL_MISMATCH = 49,
+ GSM0808_CAUSE_SWITCH_CIRCUIT_POOL = 50,
+ GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE = 51,
+ GSM0808_CAUSE_LSA_NOT_ALLOWED = 52,
+ GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED = 64,
+ GSM0808_CAUSE_TERRESTRIAL_CIRCUIT_ALREADY_ALLOCATED = 80,
+ GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS = 81,
+ GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING = 82,
+ GSM0808_CAUSE_INCORRECT_VALUE = 83,
+ GSM0808_CAUSE_UNKNOWN_MESSAGE_TYPE = 84,
+ GSM0808_CAUSE_UNKNOWN_INFORMATION_ELEMENT = 85,
+ GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC = 96,
+};
+
+/* GSM 08.08 3.2.2.11 Channel Type */
+enum gsm0808_chan_indicator {
+ GSM0808_CHAN_SPEECH = 1,
+ GSM0808_CHAN_DATA = 2,
+ GSM0808_CHAN_SIGN = 3,
+};
+
+enum gsm0808_chan_rate_type_data {
+ GSM0808_DATA_FULL_BM = 0x8,
+ GSM0808_DATA_HALF_LM = 0x9,
+ GSM0808_DATA_FULL_RPREF = 0xa,
+ GSM0808_DATA_HALF_PREF = 0xb,
+ GSM0808_DATA_FULL_PREF_NO_CHANGE = 0x1a,
+ GSM0808_DATA_HALF_PREF_NO_CHANGE = 0x1b,
+ GSM0808_DATA_MULTI_MASK = 0x20,
+ GSM0808_DATA_MULTI_MASK_NO_CHANGE = 0x30,
+};
+
+enum gsm0808_chan_rate_type_speech {
+ GSM0808_SPEECH_FULL_BM = 0x8,
+ GSM0808_SPEECH_HALF_LM = 0x9,
+ GSM0808_SPEECH_FULL_PREF= 0xa,
+ GSM0808_SPEECH_HALF_PREF= 0xb,
+ GSM0808_SPEECH_FULL_PREF_NO_CHANGE = 0x1a,
+ GSM0808_SPEECH_HALF_PREF_NO_CHANGE = 0x1b,
+ GSM0808_SPEECH_PERM = 0xf,
+ GSM0808_SPEECH_PERM_NO_CHANGE = 0x1f,
+};
+
+enum gsm0808_permitted_speech {
+ GSM0808_PERM_FR1 = 0x01,
+ GSM0808_PERM_FR2 = 0x11,
+ GSM0808_PERM_FR3 = 0x21,
+ GSM0808_PERM_HR1 = GSM0808_PERM_FR1 | 0x4,
+ GSM0808_PERM_HR2 = GSM0808_PERM_FR2 | 0x4,
+ GSM0808_PERM_HR3 = GSM0808_PERM_FR3 | 0x4,
+};
+
+#endif
diff --git a/libosmocore/include/osmocore/rate_ctr.h b/libosmocore/include/osmocore/rate_ctr.h
new file mode 100644
index 000000000..88c9de5a6
--- /dev/null
+++ b/libosmocore/include/osmocore/rate_ctr.h
@@ -0,0 +1,81 @@
+#ifndef _RATE_CTR_H
+#define _RATE_CTR_H
+
+#include <stdint.h>
+
+#include <osmocore/linuxlist.h>
+
+#define RATE_CTR_INTV_NUM 4
+
+enum rate_ctr_intv {
+ RATE_CTR_INTV_SEC,
+ RATE_CTR_INTV_MIN,
+ RATE_CTR_INTV_HOUR,
+ RATE_CTR_INTV_DAY,
+};
+
+/* for each of the intervals, we keep the following values */
+struct rate_ctr_per_intv {
+ uint64_t last;
+ uint64_t rate;
+};
+
+/* for each actual value, we keep the following data */
+struct rate_ctr {
+ uint64_t current;
+ struct rate_ctr_per_intv intv[RATE_CTR_INTV_NUM];
+};
+
+struct rate_ctr_desc {
+ const char *name;
+ const char *description;
+};
+
+/* Describe a counter group class */
+struct rate_ctr_group_desc {
+ /* The prefix to the name of all counters in this group */
+ char *group_name_prefix;
+ /* The human-readable description of the group */
+ char *group_description;
+ /* The number of counters in this group */
+ unsigned int num_ctr;
+ /* Pointer to array of counter names */
+ struct rate_ctr_desc *ctr_desc;
+};
+
+/* One instance of a counter group class */
+struct rate_ctr_group {
+ /* Linked list of all counter groups in the system */
+ struct llist_head list;
+ /* Pointer to the counter group class */
+ const struct rate_ctr_group_desc *desc;
+ /* The index of this ctr_group within its class */
+ unsigned int idx;
+ /* Actual counter structures below */
+ struct rate_ctr ctr[0];
+};
+
+/* Allocate a new group of counters according to description */
+struct rate_ctr_group *rate_ctr_group_alloc(void *ctx,
+ const struct rate_ctr_group_desc *desc,
+ unsigned int idx);
+
+/* Free the memory for the specified group of counters */
+void rate_ctr_group_free(struct rate_ctr_group *grp);
+
+/* Add a number to the counter */
+void rate_ctr_add(struct rate_ctr *ctr, int inc);
+
+/* Increment the counter by 1 */
+static inline void rate_ctr_inc(struct rate_ctr *ctr)
+{
+ rate_ctr_add(ctr, 1);
+}
+
+/* Initialize the counter module */
+int rate_ctr_init(void *tall_ctx);
+
+struct vty;
+void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
+ struct rate_ctr_group *ctrg);
+#endif /* RATE_CTR_H */
diff --git a/libosmocore/include/osmocore/write_queue.h b/libosmocore/include/osmocore/write_queue.h
index 64d4159a0..ef244c32a 100644
--- a/libosmocore/include/osmocore/write_queue.h
+++ b/libosmocore/include/osmocore/write_queue.h
@@ -35,6 +35,7 @@ struct write_queue {
int (*read_cb)(struct bsc_fd *fd);
int (*write_cb)(struct bsc_fd *fd, struct msgb *msg);
+ int (*except_cb)(struct bsc_fd *fd);
};
void write_queue_init(struct write_queue *queue, int max_length);
diff --git a/libosmocore/src/Makefile.am b/libosmocore/src/Makefile.am
index 16978074a..ce063d058 100644
--- a/libosmocore/src/Makefile.am
+++ b/libosmocore/src/Makefile.am
@@ -10,7 +10,7 @@ lib_LTLIBRARIES = libosmocore.la
libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c rxlev_stat.c \
tlv_parser.c bitvec.c comp128.c gsm_utils.c statistics.c \
write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c \
- logging.c
+ logging.c gsm0808.c rate_ctr.c
if ENABLE_TALLOC
libosmocore_la_SOURCES += talloc.c
diff --git a/libosmocore/src/gsm0808.c b/libosmocore/src/gsm0808.c
new file mode 100644
index 000000000..7a7eb3a08
--- /dev/null
+++ b/libosmocore/src/gsm0808.c
@@ -0,0 +1,295 @@
+/* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009,2010 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <osmocore/gsm0808.h>
+#include <osmocore/protocol/gsm_08_08.h>
+#include <osmocore/gsm48.h>
+
+#include <arpa/inet.h>
+
+#define BSSMAP_MSG_SIZE 512
+#define BSSMAP_MSG_HEADROOM 128
+
+struct msgb *gsm0808_create_layer3(struct msgb *msg_l3, uint16_t nc, uint16_t cc, int lac, int _ci)
+{
+ uint8_t *data;
+ uint16_t *ci;
+ struct msgb* msg;
+ struct gsm48_loc_area_id *lai;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap cmpl l3");
+ if (!msg)
+ return NULL;
+
+ /* create the bssmap header */
+ msg->l3h = msgb_put(msg, 2);
+ msg->l3h[0] = 0x0;
+
+ /* create layer 3 header */
+ data = msgb_put(msg, 1);
+ data[0] = BSS_MAP_MSG_COMPLETE_LAYER_3;
+
+ /* create the cell header */
+ data = msgb_put(msg, 3);
+ data[0] = GSM0808_IE_CELL_IDENTIFIER;
+ data[1] = 1 + sizeof(*lai) + 2;
+ data[2] = CELL_IDENT_WHOLE_GLOBAL;
+
+ lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai));
+ gsm48_generate_lai(lai, cc, nc, lac);
+
+ ci = (uint16_t *) msgb_put(msg, 2);
+ *ci = htons(_ci);
+
+ /* copy the layer3 data */
+ data = msgb_put(msg, msgb_l3len(msg_l3) + 2);
+ data[0] = GSM0808_IE_LAYER_3_INFORMATION;
+ data[1] = msgb_l3len(msg_l3);
+ memcpy(&data[2], msg_l3->l3h, data[1]);
+
+ /* update the size */
+ msg->l3h[1] = msgb_l3len(msg) - 2;
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_reset(void)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: reset");
+ if (!msg)
+ return NULL;
+
+ msg->l3h = msgb_put(msg, 6);
+ msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+ msg->l3h[1] = 0x04;
+ msg->l3h[2] = 0x30;
+ msg->l3h[3] = 0x04;
+ msg->l3h[4] = 0x01;
+ msg->l3h[5] = 0x20;
+ return msg;
+}
+
+struct msgb *gsm0808_create_clear_complete(void)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: clear complete");
+ if (!msg)
+ return NULL;
+
+ msg->l3h = msgb_put(msg, 3);
+ msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+ msg->l3h[1] = 1;
+ msg->l3h[2] = BSS_MAP_MSG_CLEAR_COMPLETE;
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "cipher-complete");
+ if (!msg)
+ return NULL;
+
+ /* send response with BSS override for A5/1... cheating */
+ msg->l3h = msgb_put(msg, 3);
+ msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+ msg->l3h[1] = 0xff;
+ msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_COMPLETE;
+
+ /* include layer3 in case we have at least two octets */
+ if (layer3 && msgb_l3len(layer3) > 2) {
+ msg->l4h = msgb_put(msg, msgb_l3len(layer3) + 2);
+ msg->l4h[0] = GSM0808_IE_LAYER_3_MESSAGE_CONTENTS;
+ msg->l4h[1] = msgb_l3len(layer3);
+ memcpy(&msg->l4h[2], layer3->l3h, msgb_l3len(layer3));
+ }
+
+ /* and the optional BSS message */
+ msg->l4h = msgb_put(msg, 2);
+ msg->l4h[0] = GSM0808_IE_CHOSEN_ENCR_ALG;
+ msg->l4h[1] = alg_id;
+
+ /* update the size */
+ msg->l3h[1] = msgb_l3len(msg) - 2;
+ return msg;
+}
+
+struct msgb *gsm0808_create_cipher_reject(uint8_t cause)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: clear complete");
+ if (!msg)
+ return NULL;
+
+ msg->l3h = msgb_put(msg, 3);
+ msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+ msg->l3h[1] = 2;
+ msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_REJECT;
+ msg->l3h[3] = cause;
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_classmark_update(const uint8_t *classmark_data, uint8_t length)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "classmark-update");
+ if (!msg)
+ return NULL;
+
+ msg->l3h = msgb_put(msg, 3);
+ msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+ msg->l3h[1] = 0xff;
+ msg->l3h[2] = BSS_MAP_MSG_CLASSMARK_UPDATE;
+
+ msg->l4h = msgb_put(msg, length);
+ memcpy(msg->l4h, classmark_data, length);
+
+ /* update the size */
+ msg->l3h[1] = msgb_l3len(msg) - 2;
+ return msg;
+}
+
+struct msgb *gsm0808_create_sapi_reject(uint8_t link_id)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: sapi 'n' reject");
+ if (!msg)
+ return NULL;
+
+ msg->l3h = msgb_put(msg, 5);
+ msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+ msg->l3h[1] = 3;
+ msg->l3h[2] = BSS_MAP_MSG_SAPI_N_REJECT;
+ msg->l3h[3] = link_id;
+ msg->l3h[4] = GSM0808_CAUSE_BSS_NOT_EQUIPPED;
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_assignment_completed(struct gsm_lchan *lchan, uint8_t rr_cause,
+ uint8_t chosen_channel, uint8_t encr_alg_id,
+ uint8_t speech_mode)
+{
+ uint8_t *data;
+
+ struct msgb *msg = msgb_alloc(35, "bssmap: ass compl");
+ if (!msg)
+ return NULL;
+
+ msg->l3h = msgb_put(msg, 3);
+ msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+ msg->l3h[1] = 0xff;
+ msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_COMPLETE;
+
+ /* write 3.2.2.22 */
+ data = msgb_put(msg, 2);
+ data[0] = GSM0808_IE_RR_CAUSE;
+ data[1] = rr_cause;
+
+ /* write cirtcuit identity code 3.2.2.2 */
+ /* write cell identifier 3.2.2.17 */
+ /* write chosen channel 3.2.2.33 when BTS picked it */
+ data = msgb_put(msg, 2);
+ data[0] = GSM0808_IE_CHOSEN_CHANNEL;
+ data[1] = chosen_channel;
+
+ /* write chosen encryption algorithm 3.2.2.44 */
+ data = msgb_put(msg, 2);
+ data[0] = GSM0808_IE_CHOSEN_ENCR_ALG;
+ data[1] = encr_alg_id;
+
+ /* write circuit pool 3.2.2.45 */
+ /* write speech version chosen: 3.2.2.51 when BTS picked it */
+ if (speech_mode != 0) {
+ data = msgb_put(msg, 2);
+ data[0] = GSM0808_IE_SPEECH_VERSION;
+ data[1] = speech_mode;
+ }
+
+ /* write LSA identifier 3.2.2.15 */
+
+
+ /* update the size */
+ msg->l3h[1] = msgb_l3len(msg) - 2;
+ return msg;
+}
+
+struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause)
+{
+ uint8_t *data;
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: ass fail");
+ if (!msg)
+ return NULL;
+
+ msg->l3h = msgb_put(msg, 6);
+ msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+ msg->l3h[1] = 0xff;
+ msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_FAILURE;
+ msg->l3h[3] = GSM0808_IE_CAUSE;
+ msg->l3h[4] = 1;
+ msg->l3h[5] = cause;
+
+ /* RR cause 3.2.2.22 */
+ if (rr_cause) {
+ data = msgb_put(msg, 2);
+ data[0] = GSM0808_IE_RR_CAUSE;
+ data[1] = *rr_cause;
+ }
+
+ /* Circuit pool 3.22.45 */
+ /* Circuit pool list 3.2.2.46 */
+
+ /* update the size */
+ msg->l3h[1] = msgb_l3len(msg) - 2;
+ return msg;
+}
+
+static const struct tlv_definition bss_att_tlvdef = {
+ .def = {
+ [GSM0808_IE_IMSI] = { TLV_TYPE_TLV },
+ [GSM0808_IE_TMSI] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CELL_IDENTIFIER_LIST] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CHANNEL_NEEDED] = { TLV_TYPE_TV },
+ [GSM0808_IE_EMLPP_PRIORITY] = { TLV_TYPE_TV },
+ [GSM0808_IE_CHANNEL_TYPE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_PRIORITY] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CIRCUIT_IDENTITY_CODE] = { TLV_TYPE_TV },
+ [GSM0808_IE_DOWNLINK_DTX_FLAG] = { TLV_TYPE_TV },
+ [GSM0808_IE_INTERFERENCE_BAND_TO_USE] = { TLV_TYPE_TV },
+ [GSM0808_IE_CLASSMARK_INFORMATION_T2] = { TLV_TYPE_TLV },
+ [GSM0808_IE_GROUP_CALL_REFERENCE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_TALKER_FLAG] = { TLV_TYPE_T },
+ [GSM0808_IE_CONFIG_EVO_INDI] = { TLV_TYPE_TV },
+ [GSM0808_IE_LSA_ACCESS_CTRL_SUPPR] = { TLV_TYPE_TV },
+ [GSM0808_IE_SERVICE_HANDOVER] = { TLV_TYPE_TV},
+ [GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CIPHER_RESPONSE_MODE] = { TLV_TYPE_TV },
+ },
+};
+
+const struct tlv_definition *gsm0808_att_tlvdef()
+{
+ return &bss_att_tlvdef;
+}
diff --git a/libosmocore/src/gsm48.c b/libosmocore/src/gsm48.c
index 5761c67b4..d957aef60 100644
--- a/libosmocore/src/gsm48.c
+++ b/libosmocore/src/gsm48.c
@@ -97,10 +97,10 @@ static const struct value_string rr_cause_names[] = {
};
/* FIXME: convert to value_string */
-static const char *cc_state_names[32] = {
+static const char *cc_state_names[33] = {
"NULL",
"INITIATED",
- "illegal state 2",
+ "MM_CONNECTION_PEND",
"MO_CALL_PROC",
"CALL_DELIVERED",
"illegal state 5",
@@ -261,3 +261,93 @@ int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi)
return 2 + buf[1];
}
+
+/* Convert Mobile Identity (10.5.1.4) to string */
+int gsm48_mi_to_string(char *string, const int str_len, const uint8_t *mi,
+ const int mi_len)
+{
+ int i;
+ uint8_t mi_type;
+ char *str_cur = string;
+ uint32_t tmsi;
+
+ mi_type = mi[0] & GSM_MI_TYPE_MASK;
+
+ switch (mi_type) {
+ case GSM_MI_TYPE_NONE:
+ break;
+ case GSM_MI_TYPE_TMSI:
+ /* Table 10.5.4.3, reverse generate_mid_from_tmsi */
+ if (mi_len == GSM48_TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) {
+ memcpy(&tmsi, &mi[1], 4);
+ tmsi = ntohl(tmsi);
+ return snprintf(string, str_len, "%u", tmsi);
+ }
+ break;
+ case GSM_MI_TYPE_IMSI:
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
+ *str_cur++ = bcd2char(mi[0] >> 4);
+
+ for (i = 1; i < mi_len; i++) {
+ if (str_cur + 2 >= string + str_len)
+ return str_cur - string;
+ *str_cur++ = bcd2char(mi[i] & 0xf);
+ /* skip last nibble in last input byte when GSM_EVEN */
+ if( (i != mi_len-1) || (mi[0] & GSM_MI_ODD))
+ *str_cur++ = bcd2char(mi[i] >> 4);
+ }
+ break;
+ default:
+ break;
+ }
+ *str_cur++ = '\0';
+
+ return str_cur - string;
+}
+
+void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf)
+{
+ raid->mcc = (buf[0] & 0xf) * 100;
+ raid->mcc += (buf[0] >> 4) * 10;
+ raid->mcc += (buf[1] & 0xf) * 1;
+
+ /* I wonder who came up with the stupidity of encoding the MNC
+ * differently depending on how many digits its decimal number has! */
+ if ((buf[1] >> 4) == 0xf) {
+ raid->mnc = (buf[2] & 0xf) * 10;
+ raid->mnc += (buf[2] >> 4) * 1;
+ } else {
+ raid->mnc = (buf[2] & 0xf) * 100;
+ raid->mnc += (buf[2] >> 4) * 10;
+ raid->mnc += (buf[1] >> 4) * 1;
+ }
+
+ raid->lac = ntohs(*(uint16_t *)(buf + 3));
+ raid->rac = buf[5];
+}
+
+int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid)
+{
+ uint16_t mcc = raid->mcc;
+ uint16_t mnc = raid->mnc;
+
+ buf[0] = ((mcc / 100) % 10) | (((mcc / 10) % 10) << 4);
+ buf[1] = (mcc % 10);
+
+ /* I wonder who came up with the stupidity of encoding the MNC
+ * differently depending on how many digits its decimal number has! */
+ if (mnc < 100) {
+ buf[1] |= 0xf0;
+ buf[2] = ((mnc / 10) % 10) | ((mnc % 10) << 4);
+ } else {
+ buf[1] |= (mnc % 10) << 4;
+ buf[2] = ((mnc / 100) % 10) | (((mcc / 10) % 10) << 4);
+ }
+
+ *(uint16_t *)(buf+3) = htons(raid->lac);
+
+ buf[5] = raid->rac;
+
+ return 6;
+}
diff --git a/libosmocore/src/gsm48_ie.c b/libosmocore/src/gsm48_ie.c
index 4ca5fb800..3c2a1f7b6 100644
--- a/libosmocore/src/gsm48_ie.c
+++ b/libosmocore/src/gsm48_ie.c
@@ -2,7 +2,7 @@
* 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
- * (C) 2008-2010 by Andreas Eversberg
+ * (C) 2009-2010 by Andreas Eversberg
*
* All Rights Reserved
*
diff --git a/libosmocore/src/gsm_utils.c b/libosmocore/src/gsm_utils.c
index 593dd5c94..b392fd37c 100644
--- a/libosmocore/src/gsm_utils.c
+++ b/libosmocore/src/gsm_utils.c
@@ -359,3 +359,18 @@ uint32_t gsm_gsmtime2fn(struct gsm_time *time)
/* TS 05.02 Chapter 4.3.3 TDMA frame number */
return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1));
}
+
+/* TS 03.03 Chapter 2.6 */
+int gprs_tlli_type(uint32_t tlli)
+{
+ if ((tlli & 0xc0000000) == 0xc0000000)
+ return TLLI_LOCAL;
+ else if ((tlli & 0xc0000000) == 0x80000000)
+ return TLLI_FOREIGN;
+ else if ((tlli & 0xf8000000) == 0x78000000)
+ return TLLI_RANDOM;
+ else if ((tlli & 0xf8000000) == 0x70000000)
+ return TLLI_AUXILIARY;
+
+ return TLLI_RESERVED;
+}
diff --git a/libosmocore/src/logging.c b/libosmocore/src/logging.c
index 508ccfd3e..e72a6e204 100644
--- a/libosmocore/src/logging.c
+++ b/libosmocore/src/logging.c
@@ -20,11 +20,16 @@
*
*/
+#include "../config.h"
+
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+
+#ifdef HAVE_STRINGS_H
#include <strings.h>
+#endif
#include <time.h>
#include <errno.h>
@@ -53,6 +58,11 @@ int log_parse_level(const char *lvl)
return get_string_value(loglevel_strs, lvl);
}
+const char *log_level_str(unsigned int lvl)
+{
+ return get_value_string(loglevel_strs, lvl);
+}
+
int log_parse_category(const char *category)
{
int i;
@@ -302,31 +312,46 @@ void log_set_category_filter(struct log_target *target, int category,
target->categories[category].loglevel = level;
}
+/* since C89/C99 says stderr is a macro, we can safely do this! */
+#ifdef stderr
static void _stderr_output(struct log_target *target, const char *log)
{
fprintf(target->tgt_stdout.out, "%s", log);
fflush(target->tgt_stdout.out);
}
+#endif
struct log_target *log_target_create(void)
{
struct log_target *target;
+ unsigned int i;
target = talloc_zero(tall_log_ctx, struct log_target);
if (!target)
return NULL;
INIT_LLIST_HEAD(&target->entry);
- memcpy(target->categories, log_info->cat,
- sizeof(struct log_category)*log_info->num_cat);
+
+ /* initialize the per-category enabled/loglevel from defaults */
+ for (i = 0; i < log_info->num_cat; i++) {
+ struct log_category *cat = &target->categories[i];
+ cat->enabled = log_info->cat[i].enabled;
+ cat->loglevel = log_info->cat[i].loglevel;
+ }
+
+ /* global settings */
target->use_color = 1;
target->print_timestamp = 0;
+
+ /* global log level */
target->loglevel = 0;
return target;
}
struct log_target *log_target_create_stderr(void)
{
+/* since C89/C99 says stderr is a macro, we can safely do this! */
+#ifdef stderr
struct log_target *target;
target = log_target_create();
@@ -336,6 +361,55 @@ struct log_target *log_target_create_stderr(void)
target->tgt_stdout.out = stderr;
target->output = _stderr_output;
return target;
+#else
+ return NULL;
+#endif /* stderr */
+}
+
+const char *log_vty_level_string(struct log_info *info)
+{
+ const struct value_string *vs;
+ unsigned int len = 3; /* ()\0 */
+ char *str;
+
+ for (vs = loglevel_strs; vs->value || vs->str; vs++)
+ len += strlen(vs->str) + 1;
+
+ str = talloc_zero_size(NULL, len);
+ if (!str)
+ return NULL;
+
+ str[0] = '(';
+ for (vs = loglevel_strs; vs->value || vs->str; vs++) {
+ strcat(str, vs->str);
+ strcat(str, "|");
+ }
+ str[strlen(str)-1] = ')';
+
+ return str;
+}
+
+const char *log_vty_category_string(struct log_info *info)
+{
+ unsigned int len = 3; /* "()\0" */
+ unsigned int i;
+ char *str;
+
+ for (i = 0; i < info->num_cat; i++)
+ len += strlen(info->cat[i].name) + 1;
+
+ str = talloc_zero_size(NULL, len);
+ if (!str)
+ return NULL;
+
+ str[0] = '(';
+ for (i = 0; i < info->num_cat; i++) {
+ strcat(str, info->cat[i].name+1);
+ strcat(str, "|");
+ }
+ str[strlen(str)-1] = ')';
+
+ return str;
}
void log_init(const struct log_info *cat)
diff --git a/libosmocore/src/msgb.c b/libosmocore/src/msgb.c
index 60af373eb..a60e2ffa5 100644
--- a/libosmocore/src/msgb.c
+++ b/libosmocore/src/msgb.c
@@ -80,10 +80,11 @@ void msgb_reset(struct msgb *msg)
msg->head = msg->_data;
msg->tail = msg->_data;
- msg->bts_link = NULL;
msg->trx = NULL;
msg->lchan = NULL;
msg->l2h = NULL;
msg->l3h = NULL;
- msg->smsh = NULL;
+ msg->l4h = NULL;
+
+ memset(&msg->cb, 0, sizeof(msg->cb));
}
diff --git a/libosmocore/src/rate_ctr.c b/libosmocore/src/rate_ctr.c
new file mode 100644
index 000000000..e48c77928
--- /dev/null
+++ b/libosmocore/src/rate_ctr.c
@@ -0,0 +1,122 @@
+/* utility routines for keeping conters about events and the event rates */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <osmocore/linuxlist.h>
+#include <osmocore/talloc.h>
+#include <osmocore/timer.h>
+#include <osmocore/rate_ctr.h>
+
+static LLIST_HEAD(rate_ctr_groups);
+
+static void *tall_rate_ctr_ctx;
+
+struct rate_ctr_group *rate_ctr_group_alloc(void *ctx,
+ const struct rate_ctr_group_desc *desc,
+ unsigned int idx)
+{
+ unsigned int size;
+ struct rate_ctr_group *group;
+
+ size = sizeof(struct rate_ctr_group) +
+ desc->num_ctr * sizeof(struct rate_ctr);
+
+ if (!ctx)
+ ctx = tall_rate_ctr_ctx;
+
+ group = talloc_zero_size(ctx, size);
+ if (!group)
+ return NULL;
+
+ group->desc = desc;
+ group->idx = idx;
+
+ llist_add(&group->list, &rate_ctr_groups);
+
+ return group;
+}
+
+void rate_ctr_group_free(struct rate_ctr_group *grp)
+{
+ llist_del(&grp->list);
+ talloc_free(grp);
+}
+
+void rate_ctr_add(struct rate_ctr *ctr, int inc)
+{
+ ctr->current += inc;
+}
+
+static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv)
+{
+ /* calculate rate over last interval */
+ ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last;
+ /* save current counter for next interval */
+ ctr->intv[intv].last = ctr->current;
+}
+
+static struct timer_list rate_ctr_timer;
+static uint64_t timer_ticks;
+
+/* The one-second interval has expired */
+static void rate_ctr_group_intv(struct rate_ctr_group *grp)
+{
+ unsigned int i;
+
+ for (i = 0; i < grp->desc->num_ctr; i++) {
+ struct rate_ctr *ctr = &grp->ctr[i];
+
+ interval_expired(ctr, RATE_CTR_INTV_SEC);
+ if ((timer_ticks % 60) == 0)
+ interval_expired(ctr, RATE_CTR_INTV_MIN);
+ if ((timer_ticks % (60*60)) == 0)
+ interval_expired(ctr, RATE_CTR_INTV_HOUR);
+ if ((timer_ticks % (24*60*60)) == 0)
+ interval_expired(ctr, RATE_CTR_INTV_DAY);
+ }
+}
+
+static void rate_ctr_timer_cb(void *data)
+{
+ struct rate_ctr_group *ctrg;
+
+ /* Increment number of ticks before we calculate intervals,
+ * as a counter value of 0 would already wrap all counters */
+ timer_ticks++;
+
+ llist_for_each_entry(ctrg, &rate_ctr_groups, list)
+ rate_ctr_group_intv(ctrg);
+
+ bsc_schedule_timer(&rate_ctr_timer, 1, 0);
+}
+
+int rate_ctr_init(void *tall_ctx)
+{
+ tall_rate_ctr_ctx = tall_ctx;
+ rate_ctr_timer.cb = rate_ctr_timer_cb;
+ bsc_schedule_timer(&rate_ctr_timer, 1, 0);
+
+ return 0;
+}
diff --git a/libosmocore/src/select.c b/libosmocore/src/select.c
index 9517778ce..2f6afa7f5 100644
--- a/libosmocore/src/select.c
+++ b/libosmocore/src/select.c
@@ -121,7 +121,8 @@ restart:
/* ugly, ugly hack. If more than one filedescriptors were
* unregistered, they might have been consecutive and
* llist_for_each_entry_safe() is no longer safe */
- if (unregistered_count > 1)
+ /* this seems to happen with the last element of the list as well */
+ if (unregistered_count >= 1)
goto restart;
}
return work;
diff --git a/libosmocore/src/write_queue.c b/libosmocore/src/write_queue.c
index a0ac2d6fd..618a8c0b3 100644
--- a/libosmocore/src/write_queue.c
+++ b/libosmocore/src/write_queue.c
@@ -32,6 +32,9 @@ int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what)
if (what & BSC_FD_READ)
queue->read_cb(fd);
+ if (what & BSC_FD_EXCEPT)
+ queue->except_cb(fd);
+
if (what & BSC_FD_WRITE) {
struct msgb *msg;
diff --git a/openbsc/README b/openbsc/README
index 51807bb44..fa01ff4cd 100644
--- a/openbsc/README
+++ b/openbsc/README
@@ -14,6 +14,8 @@ Its currently supported interfaces towards the BTS are:
* A-bis over IP as used by the ip.access nanoBTS product family
+You can find the project documentation at http://openbsc.gnumonks.org/
+
This project is still in its early days, and there are lots of areas where it
doesn't behave as per GSM spec.
diff --git a/openbsc/configure.in b/openbsc/configure.in
index 615e59d91..66d4ee1f3 100644
--- a/openbsc/configure.in
+++ b/openbsc/configure.in
@@ -18,7 +18,7 @@ dnl checks for libraries
AC_SEARCH_LIBS(crypt, crypt,
[LIBCRYPT="-lcrypt"; AC_DEFINE([VTY_CRYPT_PW], [], [Use crypt functionality of vty.])])
-PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.1.3)
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.1.6)
dnl checks for header files
AC_HEADER_STDC
@@ -48,6 +48,8 @@ AC_OUTPUT(
include/sccp/Makefile
include/Makefile
src/Makefile
+ src/ipaccess/Makefile
+ src/gprs/Makefile
tests/Makefile
tests/debug/Makefile
tests/gsm0408/Makefile
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index 259e6d6f5..afb62dec2 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -6,7 +6,9 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \
bsc_rll.h mncc.h transaction.h ussd.h gsm_04_80.h \
silent_call.h mgcp.h meas_rep.h rest_octets.h \
system_information.h handover.h mgcp_internal.h \
- vty.h
+ vty.h \
+ crc24.h gprs_bssgp.h gprs_llc.h gprs_ns.h \
+ gb_proxy.h gprs_sgsn.h gsm_04_08_gprs.h sgsn.h
openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h
openbscdir = $(includedir)/openbsc
diff --git a/openbsc/include/openbsc/abis_nm.h b/openbsc/include/openbsc/abis_nm.h
index 45307e3c8..c20e4e172 100644
--- a/openbsc/include/openbsc/abis_nm.h
+++ b/openbsc/include/openbsc/abis_nm.h
@@ -92,7 +92,7 @@ int abis_nm_sw_act_req_ack(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i1,
int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *msg);
int abis_nm_event_reports(struct gsm_bts *bts, int on);
int abis_nm_reset_resource(struct gsm_bts *bts);
-int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
+int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname,
u_int8_t win_size, int forced,
gsm_cbfn *cbfn, void *cb_data);
int abis_nm_software_load_status(struct gsm_bts *bts);
@@ -148,7 +148,7 @@ int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type,
u_int8_t *attr, int attr_len);
int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, u_int8_t *attr,
int attr_len);
-int abis_nm_ipaccess_restart(struct gsm_bts *bts);
+int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx);
int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class,
u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr,
u_int8_t *attr, u_int8_t attr_len);
@@ -164,7 +164,8 @@ enum nm_evt {
EVT_STATECHG_ADM,
};
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
- struct gsm_nm_state *old_state, struct gsm_nm_state *new_state);
+ struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
+ struct abis_om_obj_inst *obj_inst);
const char *nm_opstate_name(u_int8_t os);
const char *nm_avail_name(u_int8_t avail);
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index e6973eef0..8e6774d15 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -70,10 +70,11 @@ u_int64_t str_to_imsi(const char *imsi_str);
u_int8_t lchan2chan_nr(const struct gsm_lchan *lchan);
int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id);
+int rsl_lchan_set_state(struct gsm_lchan *lchan, int);
+
/* to be provided by external code */
int abis_rsl_sendmsg(struct msgb *msg);
int rsl_deact_sacch(struct gsm_lchan *lchan);
-int rsl_chan_release(struct gsm_lchan *lchan);
/* BCCH related code */
int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf);
diff --git a/openbsc/include/openbsc/chan_alloc.h b/openbsc/include/openbsc/chan_alloc.h
index f564e9e4d..d4f5858b7 100644
--- a/openbsc/include/openbsc/chan_alloc.h
+++ b/openbsc/include/openbsc/chan_alloc.h
@@ -45,6 +45,7 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type);
/* Free a logical channel (SDCCH, TCH, ...) */
void lchan_free(struct gsm_lchan *lchan);
+void lchan_reset(struct gsm_lchan *lchan);
/* Consider releasing the channel */
int lchan_auto_release(struct gsm_lchan *lchan);
diff --git a/openbsc/include/openbsc/crc24.h b/openbsc/include/openbsc/crc24.h
new file mode 100644
index 000000000..358fcb58f
--- /dev/null
+++ b/openbsc/include/openbsc/crc24.h
@@ -0,0 +1,8 @@
+#ifndef _CRC24_H
+#define _CRC24_H
+
+#define INIT_CRC24 0xffffff
+
+u_int32_t crc24_calc(u_int32_t fcs, u_int8_t *cp, unsigned int len);
+
+#endif
diff --git a/openbsc/include/openbsc/db.h b/openbsc/include/openbsc/db.h
index d0a1278ef..1782efb50 100644
--- a/openbsc/include/openbsc/db.h
+++ b/openbsc/include/openbsc/db.h
@@ -66,6 +66,9 @@ int db_apdu_blob_store(struct gsm_subscriber *subscr,
u_int8_t *apdu);
/* Statistics counter storage */
+struct counter;
int db_store_counter(struct counter *ctr);
+struct rate_ctr_group;
+int db_store_rate_ctr_group(struct rate_ctr_group *ctrg);
#endif /* _DB_H */
diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h
index f1c5a699a..65fd0bb53 100644
--- a/openbsc/include/openbsc/debug.h
+++ b/openbsc/include/openbsc/debug.h
@@ -29,6 +29,9 @@ enum {
DHO,
DDB,
DREF,
+ DGPRS,
+ DNS,
+ DBSSGP,
Debug_LastEntry,
};
diff --git a/openbsc/include/openbsc/gb_proxy.h b/openbsc/include/openbsc/gb_proxy.h
new file mode 100644
index 000000000..db236b5d0
--- /dev/null
+++ b/openbsc/include/openbsc/gb_proxy.h
@@ -0,0 +1,42 @@
+#ifndef _GB_PROXY_H
+#define _GB_PROXY_H
+
+#include <sys/types.h>
+
+#include <osmocore/msgb.h>
+
+#include <openbsc/gprs_ns.h>
+#include <vty/command.h>
+
+struct gbproxy_config {
+ /* parsed from config file */
+ u_int32_t nsip_listen_ip;
+ u_int16_t nsip_listen_port;
+
+ u_int32_t nsip_sgsn_ip;
+ u_int16_t nsip_sgsn_port;
+
+ u_int16_t nsip_sgsn_nsei;
+ u_int16_t nsip_sgsn_nsvci;
+
+ /* misc */
+ struct gprs_ns_inst *nsi;
+};
+
+extern struct gbproxy_config gbcfg;
+extern struct cmd_element show_gbproxy_cmd;
+
+/* gb_proxy_vty .c */
+
+int gbproxy_vty_init(void);
+int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg);
+
+
+/* gb_proxy.c */
+
+/* Main input function for Gb proxy */
+int gbprox_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci);
+
+int gbprox_signal(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data);
+#endif
diff --git a/openbsc/include/openbsc/gprs_bssgp.h b/openbsc/include/openbsc/gprs_bssgp.h
new file mode 100644
index 000000000..d3ccb12ee
--- /dev/null
+++ b/openbsc/include/openbsc/gprs_bssgp.h
@@ -0,0 +1,163 @@
+#ifndef _GPRS_BSSGP_H
+#define _GPRS_BSSGP_H
+
+#include <stdint.h>
+
+/* Section 11.3.26 / Table 11.27 */
+enum bssgp_pdu_type {
+ /* PDUs between RL and BSSGP SAPs */
+ BSSGP_PDUT_DL_UNITDATA = 0x00,
+ BSSGP_PDUT_UL_UNITDATA = 0x01,
+ BSSGP_PDUT_RA_CAPABILITY = 0x02,
+ BSSGP_PDUT_PTM_UNITDATA = 0x03,
+ /* PDUs between GMM SAPs */
+ BSSGP_PDUT_PAGING_PS = 0x06,
+ BSSGP_PDUT_PAGING_CS = 0x07,
+ BSSGP_PDUT_RA_CAPA_UDPATE = 0x08,
+ BSSGP_PDUT_RA_CAPA_UPDATE_ACK = 0x09,
+ BSSGP_PDUT_RADIO_STATUS = 0x0a,
+ BSSGP_PDUT_SUSPEND = 0x0b,
+ BSSGP_PDUT_SUSPEND_ACK = 0x0c,
+ BSSGP_PDUT_SUSPEND_NACK = 0x0d,
+ BSSGP_PDUT_RESUME = 0x0e,
+ BSSGP_PDUT_RESUME_ACK = 0x0f,
+ BSSGP_PDUT_RESUME_NACK = 0x10,
+ /* PDus between NM SAPs */
+ BSSGP_PDUT_BVC_BLOCK = 0x20,
+ BSSGP_PDUT_BVC_BLOCK_ACK = 0x21,
+ BSSGP_PDUT_BVC_RESET = 0x22,
+ BSSGP_PDUT_BVC_RESET_ACK = 0x23,
+ BSSGP_PDUT_BVC_UNBLOCK = 0x24,
+ BSSGP_PDUT_BVC_UNBLOCK_ACK = 0x25,
+ BSSGP_PDUT_FLOW_CONTROL_BVC = 0x26,
+ BSSGP_PDUT_FLOW_CONTROL_BVC_ACK = 0x27,
+ BSSGP_PDUT_FLOW_CONTROL_MS = 0x28,
+ BSSGP_PDUT_FLOW_CONTROL_MS_ACK = 0x29,
+ BSSGP_PDUT_FLUSH_LL = 0x2a,
+ BSSGP_PDUT_FLUSH_LL_ACK = 0x2b,
+ BSSGP_PDUT_LLC_DISCARD = 0x2c,
+ BSSGP_PDUT_SGSN_INVOKE_TRACE = 0x40,
+ BSSGP_PDUT_STATUS = 0x41,
+ /* PDUs between PFM SAP's */
+ BSSGP_PDUT_DOWNLOAD_BSS_PFC = 0x50,
+ BSSGP_PDUT_CREATE_BSS_PFC = 0x51,
+ BSSGP_PDUT_CREATE_BSS_PFC_ACK = 0x52,
+ BSSGP_PDUT_CREATE_BSS_PFC_NACK = 0x53,
+ BSSGP_PDUT_MODIFY_BSS_PFC = 0x54,
+ BSSGP_PDUT_MODIFY_BSS_PFC_ACK = 0x55,
+ BSSGP_PDUT_DELETE_BSS_PFC = 0x56,
+ BSSGP_PDUT_DELETE_BSS_PFC_ACK = 0x57,
+};
+
+/* Section 10.2.1 and 10.2.2 */
+struct bssgp_ud_hdr {
+ uint8_t pdu_type;
+ uint32_t tlli;
+ uint8_t qos_profile[3];
+ uint8_t data[0]; /* TLV's */
+} __attribute__((packed));
+
+struct bssgp_normal_hdr {
+ uint8_t pdu_type;
+ uint8_t data[0]; /* TLV's */
+};
+
+enum bssgp_iei_type {
+ BSSGP_IE_ALIGNMENT = 0x00,
+ BSSGP_IE_BMAX_DEFAULT_MS = 0x01,
+ BSSGP_IE_BSS_AREA_ID = 0x02,
+ BSSGP_IE_BUCKET_LEAK_RATE = 0x03,
+ BSSGP_IE_BVCI = 0x04,
+ BSSGP_IE_BVC_BUCKET_SIZE = 0x05,
+ BSSGP_IE_BVC_MEASUREMENT = 0x06,
+ BSSGP_IE_CAUSE = 0x07,
+ BSSGP_IE_CELL_ID = 0x08,
+ BSSGP_IE_CHAN_NEEDED = 0x09,
+ BSSGP_IE_DRX_PARAMS = 0x0a,
+ BSSGP_IE_EMLPP_PRIO = 0x0b,
+ BSSGP_IE_FLUSH_ACTION = 0x0c,
+ BSSGP_IE_IMSI = 0x0d,
+ BSSGP_IE_LLC_PDU = 0x0e,
+ BSSGP_IE_LLC_FRAMES_DISCARDED = 0x0f,
+ BSSGP_IE_LOCATION_AREA = 0x10,
+ BSSGP_IE_MOBILE_ID = 0x11,
+ BSSGP_IE_MS_BUCKET_SIZE = 0x12,
+ BSSGP_IE_MS_RADIO_ACCESS_CAP = 0x13,
+ BSSGP_IE_OMC_ID = 0x14,
+ BSSGP_IE_PDU_IN_ERROR = 0x15,
+ BSSGP_IE_PDU_LIFETIME = 0x16,
+ BSSGP_IE_PRIORITY = 0x17,
+ BSSGP_IE_QOS_PROFILE = 0x18,
+ BSSGP_IE_RADIO_CAUSE = 0x19,
+ BSSGP_IE_RA_CAP_UPD_CAUSE = 0x1a,
+ BSSGP_IE_ROUTEING_AREA = 0x1b,
+ BSSGP_IE_R_DEFAULT_MS = 0x1c,
+ BSSGP_IE_SUSPEND_REF_NR = 0x1d,
+ BSSGP_IE_TAG = 0x1e,
+ BSSGP_IE_TLLI = 0x1f,
+ BSSGP_IE_TMSI = 0x20,
+ BSSGP_IE_TRACE_REFERENC = 0x21,
+ BSSGP_IE_TRACE_TYPE = 0x22,
+ BSSGP_IE_TRANSACTION_ID = 0x23,
+ BSSGP_IE_TRIGGER_ID = 0x24,
+ BSSGP_IE_NUM_OCT_AFF = 0x25,
+ BSSGP_IE_LSA_ID_LIST = 0x26,
+ BSSGP_IE_LSA_INFORMATION = 0x27,
+ BSSGP_IE_PACKET_FLOW_ID = 0x28,
+ BSSGP_IE_PACKET_FLOW_TIMER = 0x29,
+ BSSGP_IE_AGG_BSS_QOS_PROFILE = 0x3a,
+ BSSGP_IE_FEATURE_BITMAP = 0x3b,
+ BSSGP_IE_BUCKET_FULL_RATIO = 0x3c,
+ BSSGP_IE_SERVICE_UTRAN_CCO = 0x3d,
+};
+
+/* Section 11.3.8 / Table 11.10: Cause coding */
+enum gprs_bssgp_cause {
+ BSSGP_CAUSE_PROC_OVERLOAD = 0x00,
+ BSSGP_CAUSE_EQUIP_FAIL = 0x01,
+ BSSGP_CAUSE_TRASIT_NET_FAIL = 0x02,
+ BSSGP_CAUSE_CAPA_GREATER_0KPBS = 0x03,
+ BSSGP_CAUSE_UNKNOWN_MS = 0x04,
+ BSSGP_CAUSE_UNKNOWN_BVCI = 0x05,
+ BSSGP_CAUSE_CELL_TRAF_CONG = 0x06,
+ BSSGP_CAUSE_SGSN_CONG = 0x07,
+ BSSGP_CAUSE_OML_INTERV = 0x08,
+ BSSGP_CAUSE_BVCI_BLOCKED = 0x09,
+ BSSGP_CAUSE_PFC_CREATE_FAIL = 0x0a,
+ BSSGP_CAUSE_SEM_INCORR_PDU = 0x20,
+ BSSGP_CAUSE_INV_MAND_INF = 0x21,
+ BSSGP_CAUSE_MISSING_MAND_IE = 0x22,
+ BSSGP_CAUSE_MISSING_COND_IE = 0x23,
+ BSSGP_CAUSE_UNEXP_COND_IE = 0x24,
+ BSSGP_CAUSE_COND_IE_ERR = 0x25,
+ BSSGP_CAUSE_PDU_INCOMP_STATE = 0x26,
+ BSSGP_CAUSE_PROTO_ERR_UNSPEC = 0x27,
+ BSSGP_CAUSE_PDU_INCOMP_FEAT = 0x28,
+};
+
+/* Our implementation */
+
+/* gprs_bssgp_util.c */
+extern struct gprs_ns_inst *bssgp_nsi;
+struct msgb *bssgp_msgb_alloc(void);
+const char *bssgp_cause_str(enum gprs_bssgp_cause cause);
+/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */
+int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei,
+ uint16_t bvci, uint16_t ns_bvci);
+/* Chapter 10.4.14: Status */
+int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg);
+
+/* gprs_bssgp.c */
+
+#include <osmocore/tlv.h>
+
+extern int gprs_bssgp_rcvmsg(struct msgb *msg);
+uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf);
+
+/* Wrapper around TLV parser to parse BSSGP IEs */
+static inline int bssgp_tlv_parse(struct tlv_parsed *tp, uint8_t *buf, int len)
+{
+ return tlv_parse(tp, &tvlv_att_def, buf, len, 0, 0);
+}
+
+#endif /* _GPRS_BSSGP_H */
diff --git a/openbsc/include/openbsc/gprs_llc.h b/openbsc/include/openbsc/gprs_llc.h
new file mode 100644
index 000000000..5a6682d80
--- /dev/null
+++ b/openbsc/include/openbsc/gprs_llc.h
@@ -0,0 +1,32 @@
+#ifndef _GPRS_LLC_H
+#define _GPRS_LLC_H
+
+#include <stdint.h>
+
+/* Section 4.7 LLC Layer Structure */
+enum gprs_llc_sapi {
+ GPRS_SAPI_GMM = 1,
+ GPRS_SAPI_TOM2 = 2,
+ GPRS_SAPI_SNDCP3 = 3,
+ GPRS_SAPI_SNDCP5 = 5,
+ GPRS_SAPI_SMS = 7,
+ GPRS_SAPI_TOM8 = 8,
+ GPRS_SAPI_SNDCP9 = 9,
+ GPRS_SAPI_SNDCP11 = 11,
+};
+
+/* Section 6.4 Commands and Responses */
+enum gprs_llc_u_cmd {
+ GPRS_LLC_U_DM_RESP = 0x01,
+ GPRS_LLC_U_DISC_CMD = 0x04,
+ GPRS_LLC_U_UA_RESP = 0x06,
+ GPRS_LLC_U_SABM_CMD = 0x07,
+ GPRS_LLC_U_FRMR_RESP = 0x08,
+ GPRS_LLC_U_XID = 0x0b,
+ GPRS_LLC_U_NULL_CMD = 0x00,
+};
+
+int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv);
+int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command);
+
+#endif
diff --git a/openbsc/include/openbsc/gprs_ns.h b/openbsc/include/openbsc/gprs_ns.h
new file mode 100644
index 000000000..4ccf4c7b9
--- /dev/null
+++ b/openbsc/include/openbsc/gprs_ns.h
@@ -0,0 +1,215 @@
+#ifndef _GPRS_NS_H
+#define _GPRS_NS_H
+
+#include <stdint.h>
+
+/* GPRS Networks Service (NS) messages on the Gb interface
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
+ * 3GPP TS 48.016 version 6.5.0 Release 6 / ETSI TS 148 016 V6.5.0 (2005-11) */
+
+struct gprs_ns_hdr {
+ uint8_t pdu_type;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* TS 08.16, Section 10.3.7, Table 14 */
+enum ns_pdu_type {
+ NS_PDUT_UNITDATA = 0x00,
+ NS_PDUT_RESET = 0x02,
+ NS_PDUT_RESET_ACK = 0x03,
+ NS_PDUT_BLOCK = 0x04,
+ NS_PDUT_BLOCK_ACK = 0x05,
+ NS_PDUT_UNBLOCK = 0x06,
+ NS_PDUT_UNBLOCK_ACK = 0x07,
+ NS_PDUT_STATUS = 0x08,
+ NS_PDUT_ALIVE = 0x0a,
+ NS_PDUT_ALIVE_ACK = 0x0b,
+ /* TS 48.016 Section 10.3.7, Table 10.3.7.1 */
+ SNS_PDUT_ACK = 0x0c,
+ SNS_PDUT_ADD = 0x0d,
+ SNS_PDUT_CHANGE_WEIGHT = 0x0e,
+ SNS_PDUT_CONFIG = 0x0f,
+ SNS_PDUT_CONFIG_ACK = 0x10,
+ SNS_PDUT_DELETE = 0x11,
+ SNS_PDUT_SIZE = 0x12,
+ SNS_PDUT_SIZE_ACK = 0x13,
+};
+
+/* TS 08.16, Section 10.3, Table 12 */
+enum ns_ctrl_ie {
+ NS_IE_CAUSE = 0x00,
+ NS_IE_VCI = 0x01,
+ NS_IE_PDU = 0x02,
+ NS_IE_BVCI = 0x03,
+ NS_IE_NSEI = 0x04,
+ /* TS 48.016 Section 10.3, Table 10.3.1 */
+ NS_IE_IPv4_LIST = 0x05,
+ NS_IE_IPv6_LIST = 0x06,
+ NS_IE_MAX_NR_NSVC = 0x07,
+ NS_IE_IPv4_EP_NR = 0x08,
+ NS_IE_IPv6_EP_NR = 0x09,
+ NS_IE_RESET_FLAG = 0x0a,
+ NS_IE_IP_ADDR = 0x0b,
+};
+
+/* TS 08.16, Section 10.3.2, Table 13 */
+enum ns_cause {
+ NS_CAUSE_TRANSIT_FAIL = 0x00,
+ NS_CAUSE_OM_INTERVENTION = 0x01,
+ NS_CAUSE_EQUIP_FAIL = 0x02,
+ NS_CAUSE_NSVC_BLOCKED = 0x03,
+ NS_CAUSE_NSVC_UNKNOWN = 0x04,
+ NS_CAUSE_BVCI_UNKNOWN = 0x05,
+ NS_CAUSE_SEM_INCORR_PDU = 0x08,
+ NS_CAUSE_PDU_INCOMP_PSTATE = 0x0a,
+ NS_CAUSE_PROTO_ERR_UNSPEC = 0x0b,
+ NS_CAUSE_INVAL_ESSENT_IE = 0x0c,
+ NS_CAUSE_MISSING_ESSENT_IE = 0x0d,
+ /* TS 48.016 Section 10.3.2, Table 10.3.2.1 */
+ NS_CAUSE_INVAL_NR_IPv4_EP = 0x0e,
+ NS_CAUSE_INVAL_NR_IPv6_EP = 0x0f,
+ NS_CAUSE_INVAL_NR_NS_VC = 0x10,
+ NS_CAUSE_INVAL_WEIGH = 0x11,
+ NS_CAUSE_UNKN_IP_EP = 0x12,
+ NS_CAUSE_UNKN_IP_ADDR = 0x13,
+ NS_CAUSE_UNKN_IP_TEST_FAILED = 0x14,
+};
+
+/* Our Implementation */
+#include <netinet/in.h>
+#include <osmocore/linuxlist.h>
+#include <osmocore/msgb.h>
+#include <osmocore/timer.h>
+#include <osmocore/select.h>
+
+#define NS_TIMERS_COUNT 7
+#define NS_TIMERS "(tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries)"
+#define NS_TIMERS_HELP \
+ "(un)blocking Timer (Tns-block) timeout\n" \
+ "(un)blocking Timer (Tns-block) number of retries\n" \
+ "Reset Timer (Tns-reset) timeout\n" \
+ "Reset Timer (Tns-reset) number of retries\n" \
+ "Test Timer (Tns-test) timeout\n" \
+
+enum ns_timeout {
+ NS_TOUT_TNS_BLOCK,
+ NS_TOUT_TNS_BLOCK_RETRIES,
+ NS_TOUT_TNS_RESET,
+ NS_TOUT_TNS_RESET_RETRIES,
+ NS_TOUT_TNS_TEST,
+ NS_TOUT_TNS_ALIVE,
+ NS_TOUT_TNS_ALIVE_RETRIES,
+};
+
+#define NSE_S_BLOCKED 0x0001
+#define NSE_S_ALIVE 0x0002
+
+enum gprs_ns_ll {
+ GPRS_NS_LL_UDP,
+ GPRS_NS_LL_E1,
+};
+
+enum gprs_ns_evt {
+ GPRS_NS_EVT_UNIT_DATA,
+};
+
+struct gprs_nsvc;
+typedef int gprs_ns_cb_t(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
+ struct msgb *msg, uint16_t bvci);
+
+/* An instance of the NS protocol stack */
+struct gprs_ns_inst {
+ /* callback to the user for incoming UNIT DATA IND */
+ gprs_ns_cb_t *cb;
+
+ /* linked lists of all NSVC in this instance */
+ struct llist_head gprs_nsvcs;
+
+ /* a NSVC object that's needed to deal with packets for unknown NSVC */
+ struct gprs_nsvc *unknown_nsvc;
+
+ uint16_t timeout[NS_TIMERS_COUNT];
+
+ /* which link-layer are we based on? */
+ enum gprs_ns_ll ll;
+
+ union {
+ /* NS-over-IP specific bits */
+ struct {
+ struct bsc_fd fd;
+ } nsip;
+ };
+};
+
+enum nsvc_timer_mode {
+ /* standard timers */
+ NSVC_TIMER_TNS_TEST,
+ NSVC_TIMER_TNS_ALIVE,
+ NSVC_TIMER_TNS_RESET,
+ _NSVC_TIMER_NR,
+};
+
+struct gprs_nsvc {
+ struct llist_head list;
+ struct gprs_ns_inst *nsi;
+
+ uint16_t nsei; /* end-to-end significance */
+ uint16_t nsvci; /* uniquely identifies NS-VC at SGSN */
+
+ uint32_t state;
+ uint32_t remote_state;
+
+ struct timer_list timer;
+ enum nsvc_timer_mode timer_mode;
+ int alive_retries;
+
+ unsigned int remote_end_is_sgsn:1;
+ unsigned int persistent:1;
+
+ struct rate_ctr_group *ctrg;
+
+ union {
+ struct {
+ struct sockaddr_in bts_addr;
+ } ip;
+ };
+};
+
+/* Create a new NS protocol instance */
+struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb);
+
+/* Destroy a NS protocol instance */
+void gprs_ns_destroy(struct gprs_ns_inst *nsi);
+
+/* Listen for incoming GPRS packets */
+int nsip_listen(struct gprs_ns_inst *nsi, uint16_t udp_port);
+
+struct sockaddr_in;
+
+/* main entry point, here incoming NS frames enter */
+int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
+ struct sockaddr_in *saddr);
+
+/* main function for higher layers (BSSGP) to send NS messages */
+int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg);
+
+int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause);
+int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause);
+int gprs_ns_tx_unblock(struct gprs_nsvc *nsvc);
+
+/* Listen for incoming GPRS packets */
+int nsip_listen(struct gprs_ns_inst *nsi, uint16_t udp_port);
+
+/* Establish a connection (from the BSS) to the SGSN */
+struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi,
+ struct sockaddr_in *dest, uint16_t nsei,
+ uint16_t nsvci);
+
+struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci);
+void nsvc_delete(struct gprs_nsvc *nsvc);
+struct gprs_nsvc *nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei);
+
+/* Add NS-specific VTY stuff */
+int gprs_ns_vty_init(struct gprs_ns_inst *nsi);
+
+#endif
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
new file mode 100644
index 000000000..bdc0b1c8f
--- /dev/null
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -0,0 +1,114 @@
+#ifndef _GPRS_SGSN_H
+#define _GPRS_SGSN_H
+
+#include <stdint.h>
+
+/* TS 04.08 4.1.3.3 GMM mobility management states on the network side */
+enum gprs_mm_state {
+ GMM_DEREGISTERED, /* 4.1.3.3.1.1 */
+ GMM_COMMON_PROC_INIT, /* 4.1.3.3.1.2 */
+ GMM_REGISTERED_NORMAL, /* 4.1.3.3.2.1 */
+ GMM_REGISTERED_SUSPENDED, /* 4.1.3.3.2.2 */
+ GMM_DEREGISTERED_INIT, /* 4.1.3.3.1.4 */
+};
+
+enum gprs_ciph_algo {
+ GPRS_ALGO_GEA0,
+ GPRS_ALGO_GEA1,
+ GPRS_ALGO_GEA2,
+};
+
+#define MS_RADIO_ACCESS_CAPA
+
+/* According to TS 03.60, Table 5: SGSN MM and PDP Contexts */
+/* Extended by 3GPP TS 23.060, Table 6: SGSN MM and PDP Contexts */
+struct sgsn_mm_ctx {
+ struct llist_head list;
+
+ char imsi[GSM_IMSI_LENGTH];
+ enum gprs_mm_state mm_state;
+ uint32_t p_tmsi;
+ uint32_t p_tmsi_sig;
+ char imei[GSM_IMEI_LENGTH];
+ /* Opt: Software Version Numbber / TS 23.195 */
+ char msisdn[GSM_EXTENSION_LENGTH];
+ struct gprs_ra_id ra;
+ uint16_t cell_id;
+ uint32_t cell_id_age;
+ uint16_t sac; /* Iu: Service Area Code */
+ uint32_t sac_age;/* Iu: Service Area Code age */
+ /* VLR number */
+ uint32_t new_sgsn_addr;
+ /* Authentication Triplets */
+ /* Kc */
+ /* Iu: CK, IK, KSI */
+ /* CKSN */
+ enum gprs_ciph_algo ciph_algo;
+ struct {
+ uint8_t buf[14]; /* 10.5.5.12a */
+ uint8_t len;
+ } ms_radio_access_capa;
+ struct {
+ uint8_t buf[4]; /* 10.5.5.12 */
+ uint8_t len;
+ } ms_network_capa;
+ uint16_t drx_parms;
+ int mnrg; /* MS reported to HLR? */
+ int ngaf; /* MS reported to MSC/VLR? */
+ int ppf; /* paging for GPRS + non-GPRS? */
+ /* SMS Parameters */
+ int recovery;
+ uint8_t radio_prio_sms;
+
+ struct llist_head pdp_list;
+
+ /* Additional bits not present in the GSM TS */
+ uint32_t tlli;
+ struct timer_list timer;
+ unsigned int T;
+};
+
+enum pdp_ctx_state {
+ PDP_STAE_NONE,
+};
+
+enum pdp_type {
+ PDP_TYPE_NONE,
+};
+
+struct sgsn_pdp_ctx {
+ struct llist_head list;
+
+ unsigned int id;
+ enum pdp_ctx_state state;
+ enum pdp_type type;
+ uint32_t addresss;
+ char *apn_subscribed;
+ char *apn_used;
+ uint16_t nsapi;
+ uint8_t ti; /* transaction identifier */
+ uint32_t ggsn_in_use;
+ int vplmn_allowed;
+ uint32_t qos_profile_subscr;
+ uint32_t qos_profile_req;
+ uint32_t qos_profile_neg;
+ uint8_t radio_prio;
+ uint32_t tx_npdu_nr;
+ uint32_t rx_npdu_nr;
+ uint32_t tx_gtp_snd;
+ uint32_t rx_gtp_snu;
+ uint32_t charging_id;
+ int reordering_reqd;
+};
+
+/* look-up a SGSN MM context based on TLLI + RAI */
+struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
+ const struct gprs_ra_id *raid);
+struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t tmsi);
+struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi);
+
+/* Allocate a new SGSN MM context */
+struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli,
+ const struct gprs_ra_id *raid);
+
+#endif /* _GPRS_SGSN_H */
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index daf3bd780..74dcbe52a 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -12,6 +12,15 @@ struct gsm_subscriber;
struct gsm_network;
struct gsm_trans;
+#define GSM48_ALLOC_SIZE 1024
+#define GSM48_ALLOC_HEADROOM 128
+
+static inline struct msgb *gsm48_msgb_alloc(void)
+{
+ return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM,
+ "GSM 04.08");
+}
+
/* config options controlling the behaviour of the lower leves */
void gsm0408_allow_everyone(int allow);
@@ -22,7 +31,6 @@ enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, in
int gsm48_tx_mm_info(struct gsm_lchan *lchan);
int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand, int key_seq);
int gsm48_tx_mm_auth_rej(struct gsm_lchan *lchan);
-struct msgb *gsm48_msgb_alloc(void);
int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans);
int gsm48_send_rr_release(struct gsm_lchan *lchan);
diff --git a/openbsc/include/openbsc/gsm_04_08_gprs.h b/openbsc/include/openbsc/gsm_04_08_gprs.h
new file mode 100644
index 000000000..344b2774b
--- /dev/null
+++ b/openbsc/include/openbsc/gsm_04_08_gprs.h
@@ -0,0 +1,350 @@
+#ifndef _GSM48_GPRS_H
+#define _GSM48_GPRS_H
+
+#include <stdint.h>
+
+/* Table 10.4 / 10.4a, GPRS Mobility Management (GMM) */
+#define GSM48_MT_GMM_ATTACH_REQ 0x01
+#define GSM48_MT_GMM_ATTACH_ACK 0x02
+#define GSM48_MT_GMM_ATTACH_COMPL 0x03
+#define GSM48_MT_GMM_ATTACH_REJ 0x04
+#define GSM48_MT_GMM_DETACH_REQ 0x05
+#define GSM48_MT_GMM_DETACH_ACK 0x06
+
+#define GSM48_MT_GMM_RA_UPD_REQ 0x08
+#define GSM48_MT_GMM_RA_UPD_ACK 0x09
+#define GSM48_MT_GMM_RA_UPD_COMPL 0x0a
+#define GSM48_MT_GMM_RA_UPD_REJ 0x0b
+
+#define GSM48_MT_GMM_PTMSI_REALL_CMD 0x10
+#define GSM48_MT_GMM_PTMSI_REALL_COMPL 0x11
+#define GSM48_MT_GMM_AUTH_CIPH_REQ 0x12
+#define GSM48_MT_GMM_AUTH_CIPH_RESP 0x13
+#define GSM48_MT_GMM_AUTH_CIPH_REJ 0x14
+#define GSM48_MT_GMM_ID_REQ 0x15
+#define GSM48_MT_GMM_ID_RESP 0x16
+#define GSM48_MT_GMM_STATUS 0x20
+#define GSM48_MT_GMM_INFO 0x21
+
+/* Table 10.4a, GPRS Session Management (GSM) */
+#define GSM48_MT_GSM_ACT_PDP_REQ 0x41
+#define GSM48_MT_GSM_ACT_PDP_ACK 0x42
+#define GSM48_MT_GSM_ACT_PDP_REJ 0x43
+#define GSM48_MT_GSM_REQ_PDP_ACT 0x44
+#define GSM48_MT_GSM_REQ_PDP_ACT_REJ 0x45
+#define GSM48_MT_GSM_DEACT_PDP_REQ 0x46
+#define GSM48_MT_GSM_DEACT_PDP_ACK 0x47
+#define GSM48_MT_GSM_ACT_AA_PDP_REQ 0x50
+#define GSM48_MT_GSM_ACT_AA_PDP_ACK 0x51
+#define GSM48_MT_GSM_ACT_AA_PDP_REJ 0x52
+#define GSM48_MT_GSM_DEACT_AA_PDP_REQ 0x53
+#define GSM48_MT_GSM_DEACT_AA_PDP_ACK 0x54
+#define GSM48_MT_GSM_STATUS 0x55
+
+/* Chapter 10.5.5.2 / Table 10.5.135 */
+#define GPRS_ATT_T_ATTACH 1
+#define GPRS_ATT_T_ATT_WHILE_IMSI 2
+#define GPRS_ATT_T_COMBINED 3
+
+/* Chapter 10.5.5.18 / Table 105.150 */
+#define GPRS_UPD_T_RA 0
+#define GPRS_UPD_T_RA_LA 1
+#define GPRS_UPD_T_RA_LA_IMSI_ATT 2
+#define GPRS_UPD_T_PERIODIC 3
+
+enum gsm48_gprs_ie_mm {
+ GSM48_IE_GMM_TIMER_READY = 0x17, /* 10.5.7.3 */
+ GSM48_IE_GMM_PTMSI_SIG = 0x19, /* 10.5.5.8 */
+ GSM48_IE_GMM_AUTH_RAND = 0x21, /* 10.5.3.1 */
+ GSM48_IE_GMM_AUTH_SRES = 0x22, /* 10.5.3.2 */
+ GSM48_IE_GMM_IMEISV = 0x23, /* 10.5.1.4 */
+ GSM48_IE_GMM_DRX_PARAM = 0x27, /* 10.5.5.6 */
+ GSM48_IE_GMM_MS_NET_CAPA = 0x31, /* 10.5.5.12 */
+};
+
+enum gsm48_gprs_ie_sm {
+ GSM48_IE_GSM_APN = 0x28, /* 10.5.6.1 */
+ GSM48_IE_GSM_PROTO_CONF_OPT = 0x27, /* 10.5.6.3 */
+ GSM48_IE_GSM_PDP_ADDR = 0x2b, /* 10.5.6.4 */
+ GSM48_IE_GSM_AA_TMR = 0x29, /* 10.5.7.3 */
+ GSM48_IE_GSM_NAME_FULL = 0x43, /* 10.5.3.5a */
+ GSM48_IE_GSM_NAME_SHORT = 0x45, /* 10.5.3.5a */
+ GSM48_IE_GSM_TIMEZONE = 0x46, /* 10.5.3.8 */
+ GSM48_IE_GSM_UTC_AND_TZ = 0x47, /* 10.5.3.9 */
+ GSM48_IE_GSM_LSA_ID = 0x48, /* 10.5.3.11 */
+};
+
+/* Chapter 9.4.15 / Table 9.4.15 */
+struct gsm48_ra_upd_ack {
+ uint8_t force_stby:4, /* 10.5.5.7 */
+ upd_result:4; /* 10.5.5.17 */
+ uint8_t ra_upd_timer; /* 10.5.7.3 */
+ struct gsm48_ra_id ra_id; /* 10.5.5.15 */
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 10.5.7.3 */
+enum gsm48_gprs_tmr_unit {
+ GPRS_TMR_2SECONDS = 0 << 5,
+ GPRS_TMR_MINUTE = 1 << 5,
+ GPRS_TMR_6MINUTE = 2 << 5,
+ GPRS_TMR_DEACTIVATED = 3 << 5,
+};
+
+/* Chapter 9.4.2 / Table 9.4.2 */
+struct gsm48_attach_ack {
+ uint8_t att_result:4, /* 10.5.5.7 */
+ force_stby:4; /* 10.5.5.1 */
+ uint8_t ra_upd_timer; /* 10.5.7.3 */
+ uint8_t radio_prio; /* 10.5.7.2 */
+ struct gsm48_ra_id ra_id; /* 10.5.5.15 */
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 9.5.1 / Table 9.5.1 */
+struct gsm48_act_pdp_ctx_req {
+ uint8_t req_nsapi;
+ uint8_t req_llc_sapi;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 10.5.5.14 / Table 10.5.147 */
+enum gsm48_gmm_cause {
+ GMM_CAUSE_IMSI_UNKNOWN = 0x02,
+ GMM_CAUSE_ILLEGAL_MS = 0x03,
+ GMM_CAUSE_ILLEGAL_ME = 0x06,
+ GMM_CAUSE_GPRS_NOTALLOWED = 0x07,
+ GMM_CAUSE_GPRS_OTHER_NOTALLOWED = 0x08,
+ GMM_CAUSE_MS_ID_NOT_DERIVED = 0x09,
+ GMM_CAUSE_IMPL_DETACHED = 0x0a,
+ GMM_CAUSE_PLMN_NOTALLOWED = 0x0b,
+ GMM_CAUSE_LA_NOTALLOWED = 0x0c,
+ GMM_CAUSE_ROAMING_NOTALLOWED = 0x0d,
+ GMM_CAUSE_NO_GPRS_PLMN = 0x0e,
+ GMM_CAUSE_MSC_TEMP_NOTREACH = 0x10,
+ GMM_CAUSE_NET_FAIL = 0x11,
+ GMM_CAUSE_CONGESTION = 0x16,
+ GMM_CAUSE_SEM_INCORR_MSG = 0x5f,
+ GMM_CAUSE_INV_MAND_INFO = 0x60,
+ GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL = 0x61,
+ GMM_CAUSE_MSGT_INCOMP_P_STATE = 0x62,
+ GMM_CAUSE_IE_NOTEXIST_NOTIMPL = 0x63,
+ GMM_CAUSE_COND_IE_ERR = 0x64,
+ GMM_CAUSE_MSG_INCOMP_P_STATE = 0x65,
+ GMM_CAUSE_PROTO_ERR_UNSPEC = 0x6f,
+};
+
+/* Chapter 10.4.6.6 / Table 10.5.157 */
+enum gsm48_gsm_cause {
+ GSM_CAUSE_INSUFF_RSRC = 0x1a,
+ GSM_CAUSE_MISSING_APN = 0x1b,
+ GSM_CAUSE_UNKNOWN_PDP = 0x1c,
+ GSM_CAUSE_AUTH_FAILED = 0x1d,
+ GSM_CAUSE_ACT_REJ_GGSN = 0x1e,
+ GSM_CAUSE_ACT_REJ_UNSPEC = 0x1f,
+ GSM_CAUSE_SERV_OPT_NOTSUPP = 0x20,
+ GSM_CAUSE_REQ_SERV_OPT_NOTSUB = 0x21,
+ GSM_CAUSE_SERV_OPT_TEMP_OOO = 0x22,
+ GSM_CAUSE_NSAPI_IN_USE = 0x23,
+ GSM_CAUSE_DEACT_REGULAR = 0x24,
+ GSM_CAUSE_QOS_NOT_ACCEPTED = 0x25,
+ GSM_CAUSE_NET_FAIL = 0x26,
+ GSM_CAUSE_REACT_RQD = 0x27,
+ GSM_CAUSE_FEATURE_NOTSUPP = 0x28,
+ GSM_CAUSE_INVALID_TRANS_ID = 0x51,
+ GSM_CAUSE_SEM_INCORR_MSG = 0x5f,
+ GSM_CAUSE_INV_MAND_INFO = 0x60,
+ GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL = 0x61,
+ GSM_CAUSE_MSGT_INCOMP_P_STATE = 0x62,
+ GSM_CAUSE_IE_NOTEXIST_NOTIMPL = 0x63,
+ GSM_CAUSE_COND_IE_ERR = 0x64,
+ GSM_CAUSE_MSG_INCOMP_P_STATE = 0x65,
+ GSM_CAUSE_PROTO_ERR_UNSPEC = 0x6f,
+};
+
+/* Section 6.1.2.2: Session management states on the network side */
+enum gsm48_pdp_state {
+ PDP_S_INACTIVE,
+ PDP_S_ACTIVE_PENDING,
+ PDP_S_ACTIVE,
+ PDP_S_INACTIVE_PENDING,
+ PDP_S_MODIFY_PENDING,
+};
+
+/* Table 10.5.155/3GPP TS 24.008 */
+enum gsm48_pdp_type_org {
+ PDP_TYPE_ORG_ETSI = 0x00,
+ PDP_TYPE_ORG_IETF = 0x01,
+};
+enum gsm48_pdp_type_nr {
+ PDP_TYPE_N_ETSI_RESERVED = 0x00,
+ PDP_TYPE_N_ETSI_PPP = 0x01,
+ PDP_TYPE_N_IETF_IPv4 = 0x21,
+ PDP_TYPE_N_IETF_IPv6 = 0x57,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_reliab_class {
+ GSM48_QOS_RC_LLC_ACK_RLC_ACK_DATA_PROT = 2,
+ GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT = 3,
+ GSM48_QOS_RC_LLC_UN_RLC_UN_PROT_DATA = 4,
+ GSM48_QOS_RC_LLC_UN_RLC_UN_DATA_UN = 5,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_preced_class {
+ GSM48_QOS_PC_HIGH = 1,
+ GSM48_QOS_PC_NORMAL = 2,
+ GSM48_QOS_PC_LOW = 3,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_peak_tput {
+ GSM48_QOS_PEAK_TPUT_1000bps = 1,
+ GSM48_QOS_PEAK_TPUT_2000bps = 2,
+ GSM48_QOS_PEAK_TPUT_4000bps = 3,
+ GSM48_QOS_PEAK_TPUT_8000bps = 4,
+ GSM48_QOS_PEAK_TPUT_16000bps = 5,
+ GSM48_QOS_PEAK_TPUT_32000bps = 6,
+ GSM48_QOS_PEAK_TPUT_64000bps = 7,
+ GSM48_QOS_PEAK_TPUT_128000bps = 8,
+ GSM48_QOS_PEAK_TPUT_256000bps = 9,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_mean_tput {
+ GSM48_QOS_MEAN_TPUT_100bph = 1,
+ GSM48_QOS_MEAN_TPUT_200bph = 2,
+ GSM48_QOS_MEAN_TPUT_500bph = 3,
+ GSM48_QOS_MEAN_TPUT_1000bph = 4,
+ GSM48_QOS_MEAN_TPUT_2000bph = 5,
+ GSM48_QOS_MEAN_TPUT_5000bph = 6,
+ GSM48_QOS_MEAN_TPUT_10000bph = 7,
+ GSM48_QOS_MEAN_TPUT_20000bph = 8,
+ GSM48_QOS_MEAN_TPUT_50000bph = 9,
+ GSM48_QOS_MEAN_TPUT_100kbph = 10,
+ GSM48_QOS_MEAN_TPUT_200kbph = 11,
+ GSM48_QOS_MEAN_TPUT_500kbph = 0xc,
+ GSM48_QOS_MEAN_TPUT_1Mbph = 0xd,
+ GSM48_QOS_MEAN_TPUT_2Mbph = 0xe,
+ GSM48_QOS_MEAN_TPUT_5Mbph = 0xf,
+ GSM48_QOS_MEAN_TPUT_10Mbph = 0x10,
+ GSM48_QOS_MEAN_TPUT_20Mbph = 0x11,
+ GSM48_QOS_MEAN_TPUT_50Mbph = 0x12,
+ GSM48_QOS_MEAN_TPUT_BEST_EFFORT = 0x1f,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_err_sdu {
+ GSM48_QOS_ERRSDU_NODETECT = 1,
+ GSM48_QOS_ERRSDU_YES = 2,
+ GSM48_QOS_ERRSDU_NO = 3,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_deliv_order {
+ GSM48_QOS_DO_ORDERED = 1,
+ GSM48_QOS_DO_UNORDERED = 2,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_traf_class {
+ GSM48_QOS_TC_CONVERSATIONAL = 1,
+ GSM48_QOS_TC_STREAMING = 2,
+ GSM48_QOS_TC_INTERACTIVE = 3,
+ GSM48_QOS_TC_BACKGROUND = 4,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_max_sdu_size {
+ /* values below in 10 octet granularity */
+ GSM48_QOS_MAXSDU_1502 = 0x97,
+ GSM48_QOS_MAXSDU_1510 = 0x98,
+ GSM48_QOS_MAXSDU_1520 = 0x99,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_max_bitrate {
+ GSM48_QOS_MBRATE_1k = 0x01,
+ GSM48_QOS_MBRATE_63k = 0x3f,
+ GSM48_QOS_MBRATE_64k = 0x40,
+ GSM48_QOS_MBRATE_568k = 0x7f,
+ GSM48_QOS_MBRATE_576k = 0x80,
+ GSM48_QOS_MBRATE_8640k = 0xfe,
+ GSM48_QOS_MBRATE_0k = 0xff,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_resid_ber {
+ GSM48_QOS_RBER_5e_2 = 0x01,
+ GSM48_QOS_RBER_1e_2 = 0x02,
+ GSM48_QOS_RBER_5e_3 = 0x03,
+ GSM48_QOS_RBER_4e_3 = 0x04,
+ GSM48_QOS_RBER_1e_3 = 0x05,
+ GSM48_QOS_RBER_1e_4 = 0x06,
+ GSM48_QOS_RBER_1e_5 = 0x07,
+ GSM48_QOS_RBER_1e_6 = 0x08,
+ GSM48_QOS_RBER_6e_8 = 0x09,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_sdu_err {
+ GSM48_QOS_SERR_1e_2 = 0x01,
+ GSM48_QOS_SERR_7e_2 = 0x02,
+ GSM48_QOS_SERR_1e_3 = 0x03,
+ GSM48_QOS_SERR_1e_4 = 0x04,
+ GSM48_QOS_SERR_1e_5 = 0x05,
+ GSM48_QOS_SERR_1e_6 = 0x06,
+ GSM48_QOS_SERR_1e_1 = 0x07,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+struct gsm48_qos {
+ /* octet 3 */
+ uint8_t reliab_class:3;
+ uint8_t delay_class:3;
+ uint8_t spare:2;
+ /* octet 4 */
+ uint8_t preced_class:3;
+ uint8_t spare2:1;
+ uint8_t peak_tput:4;
+ /* octet 5 */
+ uint8_t mean_tput:5;
+ uint8_t spare3:3;
+ /* octet 6 */
+ uint8_t deliv_err_sdu:3;
+ uint8_t deliv_order:2;
+ uint8_t traf_class:3;
+ /* octet 7 */
+ uint8_t max_sdu_size;
+ /* octet 8 */
+ uint8_t max_bitrate_up;
+ /* octet 9 */
+ uint8_t max_bitrate_down;
+ /* octet 10 */
+ uint8_t sdu_err_ratio:4;
+ uint8_t resid_ber:4;
+ /* octet 11 */
+ uint8_t handling_prio:2;
+ uint8_t xfer_delay:6;
+ /* octet 12 */
+ uint8_t guar_bitrate_up;
+ /* octet 13 */
+ uint8_t guar_bitrate_down;
+ /* octet 14 */
+ uint8_t src_stats_desc:4;
+ uint8_t sig_ind:1;
+ uint8_t spare5:3;
+ /* octet 15 */
+ uint8_t max_bitrate_down_ext;
+ /* octet 16 */
+ uint8_t guar_bitrate_down_ext;
+};
+
+
+int gprs_tlli_type(uint32_t tlli);
+
+struct gsm_bts *gsm48_bts_by_ra_id(struct gsm_network *net,
+ const uint8_t *buf, unsigned int len);
+
+#endif /* _GSM48_GPRS_H */
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 8dfa5886b..91527c933 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -73,6 +73,37 @@ enum gsm_paging_event {
GSM_PAGING_OOM,
};
+enum bts_gprs_mode {
+ BTS_GPRS_NONE = 0,
+ BTS_GPRS_GPRS = 1,
+ BTS_GPRS_EGPRS = 2,
+};
+
+/* the data structure stored in msgb->cb for openbsc apps */
+struct openbsc_msgb_cb {
+ unsigned char *bssgph;
+ unsigned char *llch;
+
+ /* Cell Identifier */
+ unsigned char *bssgp_cell_id;
+
+ /* Identifiers of a BTS, equal to 'struct bssgp_bts_ctx' */
+ u_int16_t nsei;
+ u_int16_t bvci;
+
+ /* Identifier of a MS (inside BTS), equal to 'struct sgsn_mm_ctx' */
+ u_int32_t tlli;
+} __attribute__((packed));
+#define OBSC_MSGB_CB(__msgb) ((struct openbsc_msgb_cb *)&((__msgb)->cb[0]))
+#define msgb_tlli(__x) OBSC_MSGB_CB(__x)->tlli
+#define msgb_nsei(__x) OBSC_MSGB_CB(__x)->nsei
+#define msgb_bvci(__x) OBSC_MSGB_CB(__x)->bvci
+#define msgb_gmmh(__x) (__x)->l3h
+#define msgb_bssgph(__x) OBSC_MSGB_CB(__x)->bssgph
+#define msgb_bssgp_len(__x) ((__x)->tail - (uint8_t *)msgb_bssgph(__x))
+#define msgb_bcid(__x) OBSC_MSGB_CB(__x)->bssgp_cell_id
+#define msgb_llch(__x) OBSC_MSGB_CB(__x)->llch
+
struct msgb;
typedef int gsm_cbfn(unsigned int hooknum,
unsigned int event,
@@ -99,11 +130,6 @@ typedef int gsm_cbfn(unsigned int hooknum,
} while(0);
-/* communications link with a BTS */
-struct gsm_bts_link {
- struct gsm_bts *bts;
-};
-
/* Real authentication information containing Ki */
enum gsm_auth_algo {
AUTH_ALGO_NONE,
@@ -250,6 +276,7 @@ struct gsm_lchan {
u_int16_t bound_port;
u_int16_t connect_port;
u_int16_t conn_id;
+ u_int8_t rtp_payload;
u_int8_t rtp_payload2;
u_int8_t speech_mode;
struct rtp_socket *rtp_socket;
@@ -368,7 +395,6 @@ struct gsm_paging_request {
struct gsm_bts_paging_state {
/* pending requests */
struct llist_head pending_requests;
- struct gsm_paging_request *last_request;
struct gsm_bts *bts;
struct timer_list work_timer;
@@ -383,11 +409,14 @@ struct gsm_envabtse {
struct gsm_bts_gprs_nsvc {
struct gsm_bts *bts;
+ /* data read via VTY config file, to configure the BTS
+ * via OML from BSC */
int id;
u_int16_t nsvci;
- u_int16_t local_port;
- u_int16_t remote_port;
- u_int32_t remote_ip;
+ u_int16_t local_port; /* on the BTS */
+ u_int16_t remote_port; /* on the SGSN */
+ u_int32_t remote_ip; /* on the SGSN */
+
struct gsm_nm_state nm_state;
};
@@ -476,18 +505,24 @@ struct gsm_bts {
/* Not entirely sure how ip.access specific this is */
struct {
- int enabled;
+ enum bts_gprs_mode mode;
struct {
struct gsm_nm_state nm_state;
u_int16_t nsei;
+ uint8_t timer[7];
} nse;
struct {
struct gsm_nm_state nm_state;
u_int16_t bvci;
+ uint8_t timer[11];
} cell;
struct gsm_bts_gprs_nsvc nsvc[2];
u_int8_t rac;
} gprs;
+
+ /* RACH NM values */
+ int rach_b_thresh;
+ int rach_ldavg_slots;
/* transceivers */
int num_trx;
@@ -535,6 +570,14 @@ struct gsmnet_stats {
struct counter *alerted; /* we alerted the other end */
struct counter *connected;/* how many calls were accepted */
} call;
+ struct {
+ struct counter *rf_fail;
+ struct counter *rll_err;
+ } chan;
+ struct {
+ struct counter *oml_fail;
+ struct counter *rsl_fail;
+ } bts;
};
enum gsm_auth_policy {
@@ -697,15 +740,10 @@ const char *gsm_auth_policy_name(enum gsm_auth_policy policy);
enum rrlp_mode rrlp_mode_parse(const char *arg);
const char *rrlp_mode_name(enum rrlp_mode mode);
-void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked);
+enum bts_gprs_mode bts_gprs_mode_parse(const char *arg);
+const char *bts_gprs_mode_name(enum bts_gprs_mode mode);
-/* A parsed GPRS routing area */
-struct gprs_ra_id {
- u_int16_t mnc;
- u_int16_t mcc;
- u_int16_t lac;
- u_int8_t rac;
-};
+void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked);
int gsm48_ra_id_by_bts(u_int8_t *buf, struct gsm_bts *bts);
void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts);
diff --git a/openbsc/include/openbsc/ipaccess.h b/openbsc/include/openbsc/ipaccess.h
index 86248aae5..f8ddfd467 100644
--- a/openbsc/include/openbsc/ipaccess.h
+++ b/openbsc/include/openbsc/ipaccess.h
@@ -53,6 +53,8 @@ int ipaccess_send_id_req(int fd);
int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len);
+int ipaccess_drop_oml(struct gsm_bts *bts);
+int ipaccess_drop_rsl(struct gsm_bts_trx *trx);
/*
* Firmware specific header
diff --git a/openbsc/include/openbsc/rest_octets.h b/openbsc/include/openbsc/rest_octets.h
index 4e72c0f87..6d9011963 100644
--- a/openbsc/include/openbsc/rest_octets.h
+++ b/openbsc/include/openbsc/rest_octets.h
@@ -71,6 +71,7 @@ enum gprs_nmo {
GPRS_NMO_III = 2, /* no paging coordination */
};
+/* TS 04.60 12.24 */
struct gprs_cell_options {
enum gprs_nmo nmo;
/* T3168: wait for packet uplink assignment message */
@@ -79,6 +80,16 @@ struct gprs_cell_options {
u_int32_t t3192; /* in milliseconds */
u_int32_t drx_timer_max;/* in seconds */
u_int32_t bs_cv_max;
+
+ u_int8_t ext_info_present;
+ struct {
+ u_int8_t egprs_supported;
+ u_int8_t use_egprs_p_ch_req;
+ u_int8_t bep_period;
+ u_int8_t pfc_supported;
+ u_int8_t dtm_supported;
+ u_int8_t bss_paging_coordination;
+ } ext_info;
};
/* TS 04.60 Table 12.9.2 */
diff --git a/openbsc/include/openbsc/rtp_proxy.h b/openbsc/include/openbsc/rtp_proxy.h
index f82711a8e..65b1a5fac 100644
--- a/openbsc/include/openbsc/rtp_proxy.h
+++ b/openbsc/include/openbsc/rtp_proxy.h
@@ -28,6 +28,12 @@
#include <osmocore/linuxlist.h>
#include <osmocore/select.h>
+#define RTP_PT_GSM_FULL 3
+#define RTP_PT_GSM_HALF 96
+#define RTP_PT_GSM_EFR 97
+#define RTP_PT_AMR_FULL 98
+#define RTP_PT_AMR_HALF 99
+
enum rtp_rx_action {
RTP_NONE,
RTP_PROXY,
diff --git a/openbsc/include/openbsc/sgsn.h b/openbsc/include/openbsc/sgsn.h
new file mode 100644
index 000000000..2dc53c13d
--- /dev/null
+++ b/openbsc/include/openbsc/sgsn.h
@@ -0,0 +1,30 @@
+#ifndef _SGSN_H
+#define _SGSN_H
+
+#include <sys/types.h>
+
+#include <osmocore/msgb.h>
+
+#include <openbsc/gprs_ns.h>
+
+struct sgsn_config {
+ /* parsed from config file */
+ u_int32_t nsip_listen_ip;
+ u_int16_t nsip_listen_port;
+
+ /* misc */
+ struct gprs_ns_inst *nsi;
+};
+
+
+/* sgsn_vty.c */
+
+int sgsn_vty_init(void);
+int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg);
+
+/* sgsn.c */
+
+/* Main input function for Gb proxy */
+int sgsn_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci);
+
+#endif
diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h
index 1b974e288..48f7946b3 100644
--- a/openbsc/include/openbsc/signal.h
+++ b/openbsc/include/openbsc/signal.h
@@ -26,7 +26,6 @@
#include <errno.h>
#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
#include <osmocore/signal.h>
@@ -43,6 +42,7 @@ enum signal_subsystems {
SS_SCALL,
SS_GLOBAL,
SS_CHALLOC,
+ SS_NS,
};
/* SS_PAGING signals */
@@ -118,6 +118,8 @@ enum signal_global {
S_GLOBAL_SHUTDOWN,
};
+struct gsm_subscriber;
+
struct paging_signal_data {
struct gsm_subscriber *subscr;
struct gsm_bts *bts;
@@ -133,7 +135,7 @@ struct scall_signal_data {
};
struct ipacc_ack_signal_data {
- struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
u_int8_t msg_type;
};
@@ -143,4 +145,16 @@ struct challoc_signal_data {
enum gsm_chan_t type;
};
+enum signal_ns {
+ S_NS_RESET,
+ S_NS_BLOCK,
+ S_NS_UNBLOCK,
+ S_NS_ALIVE_EXP, /* Tns-alive expired more than N times */
+};
+
+struct ns_signal_data {
+ struct gprs_nsvc *nsvc;
+ uint8_t cause;
+};
+
#endif
diff --git a/openbsc/include/openbsc/vty.h b/openbsc/include/openbsc/vty.h
index 40e219162..f1b1148ad 100644
--- a/openbsc/include/openbsc/vty.h
+++ b/openbsc/include/openbsc/vty.h
@@ -1,6 +1,10 @@
#ifndef OPENBSC_VTY_H
#define OPENBSC_VTY_H
+struct gsm_network;
+struct vty;
+
void openbsc_vty_add_cmds(void);
+void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *);
#endif
diff --git a/openbsc/include/sccp/sccp_types.h b/openbsc/include/sccp/sccp_types.h
index 42fda96ae..22bd70f21 100644
--- a/openbsc/include/sccp/sccp_types.h
+++ b/openbsc/include/sccp/sccp_types.h
@@ -411,4 +411,10 @@ struct sccp_data_it {
u_int8_t credit;
} __attribute__((packed));
+struct sccp_proto_err {
+ u_int8_t type;
+ struct sccp_source_reference destination_local_reference;
+ u_int8_t error_cause;
+};
+
#endif
diff --git a/openbsc/include/vty/buffer.h b/openbsc/include/vty/buffer.h
index 31519400f..c9467a91d 100644
--- a/openbsc/include/vty/buffer.h
+++ b/openbsc/include/vty/buffer.h
@@ -28,7 +28,7 @@
/* Create a new buffer. Memory will be allocated in chunks of the given
size. If the argument is 0, the library will supply a reasonable
default size suitable for buffering socket I/O. */
-struct buffer *buffer_new(size_t);
+struct buffer *buffer_new(void *ctx, size_t);
/* Free all data in the buffer. */
void buffer_reset(struct buffer *);
diff --git a/openbsc/include/vty/command.h b/openbsc/include/vty/command.h
index 03b071f70..1b6e0a7b7 100644
--- a/openbsc/include/vty/command.h
+++ b/openbsc/include/vty/command.h
@@ -107,6 +107,9 @@ enum node_type {
TS_NODE,
SUBSCR_NODE,
MGCP_NODE,
+ GBPROXY_NODE,
+ SGSN_NODE,
+ NS_NODE,
};
/* Node which has some commands and prompt string and configuration
@@ -173,6 +176,17 @@ struct desc {
/* helper defines for end-user DEFUN* macros */
#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
+ static struct cmd_element cmdname = \
+ { \
+ .string = cmdstr, \
+ .func = funcname, \
+ .doc = helpstr, \
+ .attr = attrs, \
+ .daemon = dnum, \
+ };
+
+/* global (non static) cmd_element */
+#define gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
struct cmd_element cmdname = \
{ \
.string = cmdstr, \
@@ -195,6 +209,12 @@ struct desc {
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
DEFUN_CMD_FUNC_TEXT(funcname)
+/* global (non static) cmd_element */
+#define gDEFUN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
DEFUN_CMD_FUNC_DECL(funcname) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
@@ -236,6 +256,10 @@ struct desc {
#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
+/* global (non static) cmd_element */
+#define gALIAS(funcname, cmdname, cmdstr, helpstr) \
+ gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
+
#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0)
@@ -326,6 +350,7 @@ struct desc {
void install_node(struct cmd_node *, int (*)(struct vty *));
void install_default(enum node_type);
void install_element(enum node_type, struct cmd_element *);
+void install_element_ve(struct cmd_element *cmd);
void sort_node();
/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index 2c1d37a04..177c410ad 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -2,29 +2,32 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
AM_LDFLAGS = $(LIBOSMOCORE_LIBS)
-sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config \
- isdnsync bsc_mgcp ipaccess-proxy
-noinst_LIBRARIES = libbsc.a libmsc.a libvty.a
+# build current directory before building gprs
+SUBDIRS = . ipaccess gprs
+
+sbin_PROGRAMS = bsc_hack bs11_config isdnsync bsc_mgcp
+noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libsccp.a
noinst_HEADERS = vty/cardshell.h
bscdir = $(libdir)
bsc_LIBRARIES = libsccp.a
libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
- chan_alloc.c debug.c \
+ chan_alloc.c debug.c socket.c \
gsm_subscriber_base.c subchan_demux.c bsc_rll.c transaction.c \
trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c \
input/misdn.c input/ipaccess.c \
talloc_ctx.c system_information.c rest_octets.c \
rtp_proxy.c bts_siemens_bs11.c bts_ipaccess_nanobts.c \
- bts_unknown.c bsc_version.c bsc_api.c vty_interface_cmds.c
+ bts_unknown.c bsc_version.c bsc_api.c
-libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \
+libmsc_a_SOURCES = gsm_subscriber.c db.c \
mncc.c gsm_04_08.c gsm_04_11.c transaction.c \
token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c \
handover_logic.c handover_decision.c meas_rep.c
-libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
+libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c \
+ telnet_interface.c vty_interface_cmds.c vty/utils.c
libsccp_a_SOURCES = sccp/sccp.c
@@ -34,15 +37,8 @@ bsc_hack_LDADD = libmsc.a libbsc.a libmsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c debug.c \
rs232.c bts_siemens_bs11.c
-ipaccess_find_SOURCES = ipaccess/ipaccess-find.c
-
-ipaccess_config_SOURCES = ipaccess/ipaccess-config.c ipaccess/ipaccess-firmware.c
-ipaccess_config_LDADD = libbsc.a libmsc.a libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
-
isdnsync_SOURCES = isdnsync.c
bsc_mgcp_SOURCES = mgcp/mgcp_main.c mgcp/mgcp_protocol.c mgcp/mgcp_network.c mgcp/mgcp_vty.c \
- debug.c telnet_interface.c vty_interface_cmds.c
+ debug.c
bsc_mgcp_LDADD = libvty.a
-
-ipaccess_proxy_SOURCES = ipaccess/ipaccess-proxy.c debug.c
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index 5e6e8196c..09285bd43 100644
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -678,7 +678,7 @@ static int update_admstate(struct gsm_bts *bts, u_int8_t obj_class,
new_state = *nm_state;
new_state.administrative = adm_state;
- rc = nm_state_event(EVT_STATECHG_ADM, obj_class, obj, nm_state, &new_state);
+ rc = nm_state_event(EVT_STATECHG_ADM, obj_class, obj, nm_state, &new_state, obj_inst);
nm_state->administrative = adm_state;
@@ -732,7 +732,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
/* Update the operational state of a given object in our in-memory data
* structures and send an event to the higher layer */
void *obj = objclass2obj(bts, foh->obj_class, &foh->obj_inst);
- rc = nm_state_event(EVT_STATECHG_OPER, foh->obj_class, obj, nm_state, &new_state);
+ rc = nm_state_event(EVT_STATECHG_OPER, foh->obj_class, obj, nm_state, &new_state, &foh->obj_inst);
nm_state->operational = new_state.operational;
nm_state->availability = new_state.availability;
if (nm_state->administrative == 0)
@@ -822,15 +822,56 @@ static int ipacc_sw_activate(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i
return abis_nm_sendmsg(bts, msg);
}
+static int abis_nm_parse_sw_descr(const u_int8_t *sw_descr, int sw_descr_len)
+{
+ static const struct tlv_definition sw_descr_def = {
+ .def = {
+ [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V, },
+ [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V, },
+ },
+ };
+
+ u_int8_t tag;
+ u_int16_t tag_len;
+ const u_int8_t *val;
+ int ofs = 0, len;
+
+ /* Classic TLV parsing doesn't work well with SW_DESCR because of it's
+ * nested nature and the fact you have to assume it contains only two sub
+ * tags NM_ATT_FILE_VERSION & NM_ATT_FILE_ID to parse it */
+
+ if (sw_descr[0] != NM_ATT_SW_DESCR) {
+ DEBUGP(DNM, "SW_DESCR attribute identifier not found!\n");
+ return -1;
+ }
+ ofs += 1;
+
+ len = tlv_parse_one(&tag, &tag_len, &val,
+ &sw_descr_def, &sw_descr[ofs], sw_descr_len-ofs);
+ if (len < 0 || (tag != NM_ATT_FILE_ID)) {
+ DEBUGP(DNM, "FILE_ID attribute identifier not found!\n");
+ return -2;
+ }
+ ofs += len;
+
+ len = tlv_parse_one(&tag, &tag_len, &val,
+ &sw_descr_def, &sw_descr[ofs], sw_descr_len-ofs);
+ if (len < 0 || (tag != NM_ATT_FILE_VERSION)) {
+ DEBUGP(DNM, "FILE_VERSION attribute identifier not found!\n");
+ return -3;
+ }
+ ofs += len;
+
+ return ofs;
+}
+
static int abis_nm_rx_sw_act_req(struct msgb *mb)
{
struct abis_om_hdr *oh = msgb_l2(mb);
struct abis_om_fom_hdr *foh = msgb_l3(mb);
struct tlv_parsed tp;
const u_int8_t *sw_config;
- int sw_config_len;
- int file_id_len;
- int ret;
+ int ret, sw_config_len, sw_descr_len;
debugp_foh(foh);
@@ -854,20 +895,16 @@ static int abis_nm_rx_sw_act_req(struct msgb *mb)
DEBUGP(DNM, "Found SW config: %s\n", hexdump(sw_config, sw_config_len));
}
- if (sw_config[0] != NM_ATT_SW_DESCR)
- DEBUGP(DNM, "SW_DESCR attribute identifier not found!\n");
- if (sw_config[1] != NM_ATT_FILE_ID)
- DEBUGP(DNM, "FILE_ID attribute identifier not found!\n");
- file_id_len = sw_config[2] * 256 + sw_config[3];
+ /* Use the first SW_DESCR present in SW config */
+ sw_descr_len = abis_nm_parse_sw_descr(sw_config, sw_config_len);
+ if (sw_descr_len < 0)
+ return -EINVAL;
- /* Assumes first SW file in list is the one to be activated */
- /* sw_config + 4 to skip over 2 attribute ID bytes and 16-bit length field */
return ipacc_sw_activate(mb->trx->bts, foh->obj_class,
foh->obj_inst.bts_nr,
foh->obj_inst.trx_nr,
foh->obj_inst.ts_nr,
- sw_config + 4,
- file_id_len);
+ sw_config, sw_descr_len);
}
/* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */
@@ -1102,6 +1139,7 @@ enum sw_state {
struct abis_nm_sw {
struct gsm_bts *bts;
+ int trx_nr;
gsm_cbfn *cbfn;
void *cb_data;
int forced;
@@ -1555,7 +1593,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
}
/* Load the specified software into the BTS */
-int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
+int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname,
u_int8_t win_size, int forced,
gsm_cbfn *cbfn, void *cb_data)
{
@@ -1569,6 +1607,7 @@ int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
return -EBUSY;
sw->bts = bts;
+ sw->trx_nr = trx_nr;
switch (bts->type) {
case GSM_BTS_TYPE_BS11:
@@ -1579,8 +1618,8 @@ int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
break;
case GSM_BTS_TYPE_NANOBTS:
sw->obj_class = NM_OC_BASEB_TRANSC;
- sw->obj_instance[0] = 0x00;
- sw->obj_instance[1] = 0x00;
+ sw->obj_instance[0] = sw->bts->nr;
+ sw->obj_instance[1] = sw->trx_nr;
sw->obj_instance[2] = 0xff;
break;
case GSM_BTS_TYPE_UNKNOWN:
@@ -2514,7 +2553,7 @@ static int bs11_swload_cbfn(unsigned int hook, unsigned int event,
fle = fl_dequeue(&bs11_sw->file_list);
if (fle) {
/* start download the next file of our file list */
- rc = abis_nm_software_load(bs11_sw->bts, fle->fname,
+ rc = abis_nm_software_load(bs11_sw->bts, 0xff, fle->fname,
bs11_sw->win_size,
bs11_sw->forced,
&bs11_swload_cbfn, bs11_sw);
@@ -2570,7 +2609,7 @@ int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname,
return -EINVAL;
/* start download the next file of our file list */
- rc = abis_nm_software_load(bts, fle->fname, win_size, forced,
+ rc = abis_nm_software_load(bts, 0xff, fle->fname, win_size, forced,
bs11_swload_cbfn, bs11_sw);
talloc_free(fle);
return rc;
@@ -2738,12 +2777,12 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
case NM_MT_IPACC_RSL_CONNECT_NACK:
case NM_MT_IPACC_SET_NVATTR_NACK:
case NM_MT_IPACC_GET_NVATTR_NACK:
- signal.bts = msg->trx->bts;
+ signal.trx = gsm_bts_trx_by_nr(msg->trx->bts, foh->obj_inst.trx_nr);
signal.msg_type = foh->msg_type;
dispatch_signal(SS_NM, S_NM_IPACC_NACK, &signal);
break;
case NM_MT_IPACC_SET_NVATTR_ACK:
- signal.bts = msg->trx->bts;
+ signal.trx = gsm_bts_trx_by_nr(msg->trx->bts, foh->obj_inst.trx_nr);
signal.msg_type = foh->msg_type;
dispatch_signal(SS_NM, S_NM_IPACC_ACK, &signal);
break;
@@ -2829,9 +2868,16 @@ int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx,
}
/* restart / reboot an ip.access nanoBTS */
-int abis_nm_ipaccess_restart(struct gsm_bts *bts)
+int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx)
{
- return __simple_cmd(bts, NM_MT_IPACC_RESTART);
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC,
+ trx->bts->nr, trx->nr, 0xff);
+
+ return abis_nm_sendmsg(trx->bts, msg);
}
int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class,
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 0e572ccce..53b29823e 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -727,7 +727,6 @@ int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id)
link_id, 0);
msgb_tv_put(msg, RSL_IE_RELEASE_MODE, 0); /* normal release */
- lchan->state = LCHAN_S_REL_REQ;
/* FIXME: start some timer in case we don't receive a REL ACK ? */
msg->trx = lchan->ts->trx;
@@ -735,6 +734,12 @@ int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id)
return abis_rsl_sendmsg(msg);
}
+int rsl_lchan_set_state(struct gsm_lchan *lchan, int state)
+{
+ lchan->state = state;
+ return 0;
+}
+
/* Chapter 8.4.2: Channel Activate Acknowledge */
static int rsl_rx_chan_act_ack(struct msgb *msg)
{
@@ -749,7 +754,7 @@ static int rsl_rx_chan_act_ack(struct msgb *msg)
LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n",
gsm_lchan_name(msg->lchan),
gsm_lchans_name(msg->lchan->state));
- msg->lchan->state = LCHAN_S_ACTIVE;
+ rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE);
dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_ACK, msg->lchan);
@@ -775,9 +780,9 @@ static int rsl_rx_chan_act_nack(struct msgb *msg)
print_rsl_cause(LOGL_ERROR, cause,
TLVP_LEN(&tp, RSL_IE_CAUSE));
if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC)
- msg->lchan->state = LCHAN_S_NONE;
+ rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE);
} else
- msg->lchan->state = LCHAN_S_NONE;
+ rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE);
LOGPC(DRSL, LOGL_ERROR, "\n");
@@ -805,6 +810,7 @@ static int rsl_rx_conn_fail(struct msgb *msg)
LOGPC(DRSL, LOGL_NOTICE, "\n");
/* FIXME: only free it after channel release ACK */
+ counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rf_fail);
return rsl_rf_chan_release(msg->lchan);
}
@@ -981,7 +987,7 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n",
gsm_lchan_name(msg->lchan),
gsm_lchans_name(msg->lchan->state));
- msg->lchan->state = LCHAN_S_NONE;
+ rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE);
lchan_free(msg->lchan);
break;
case RSL_MT_MODE_MODIFY_ACK:
@@ -1124,7 +1130,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel "
"in state %s\n", gsm_lchan_name(lchan),
gsm_lchans_name(lchan->state));
- lchan->state = LCHAN_S_ACT_REQ;
+ rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ);
ts_number = lchan->ts->nr;
arfcn = lchan->ts->trx->arfcn;
@@ -1181,6 +1187,10 @@ static int rsl_rx_ccch_load(struct msgb *msg)
switch (rslh->data[0]) {
case RSL_IE_PAGING_LOAD:
pg_buf_space = rslh->data[1] << 8 | rslh->data[2];
+ if (is_ipaccess_bts(msg->trx->bts) && pg_buf_space == 0xffff) {
+ /* paging load below configured threshold, use 50 as default */
+ pg_buf_space = 50;
+ }
paging_update_buffer_space(msg->trx->bts, pg_buf_space);
break;
case RSL_IE_RACH_LOAD:
@@ -1240,8 +1250,10 @@ static int rsl_rx_rll_err_ind(struct msgb *msg)
rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND);
- if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED)
+ if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED) {
+ counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rll_err);
return rsl_rf_chan_release(msg->lchan);
+ }
return 0;
}
@@ -1363,6 +1375,44 @@ static u_int8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan)
return 0;
}
+static u_int8_t ipa_rtp_pt_for_lchan(struct gsm_lchan *lchan)
+{
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ switch (lchan->type) {
+ case GSM_LCHAN_TCH_F:
+ return RTP_PT_GSM_FULL;
+ case GSM_LCHAN_TCH_H:
+ return RTP_PT_GSM_HALF;
+ default:
+ break;
+ }
+ case GSM48_CMODE_SPEECH_EFR:
+ switch (lchan->type) {
+ case GSM_LCHAN_TCH_F:
+ return RTP_PT_GSM_EFR;
+ /* there's no half-rate EFR */
+ default:
+ break;
+ }
+ case GSM48_CMODE_SPEECH_AMR:
+ switch (lchan->type) {
+ case GSM_LCHAN_TCH_F:
+ return RTP_PT_AMR_FULL;
+ case GSM_LCHAN_TCH_H:
+ return RTP_PT_AMR_HALF;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access rtp payload type for "
+ "tch_mode == 0x%02x\n & lchan_type == %d",
+ lchan->tch_mode, lchan->type);
+ return 0;
+}
+
/* ip.access specific RSL extensions */
static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv)
{
@@ -1429,10 +1479,13 @@ int rsl_ipacc_crcx(struct gsm_lchan *lchan)
/* 0x1- == receive-only, 0x-1 == EFR codec */
lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan);
+ lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan);
msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode);
+ msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload);
- DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x\n",
- gsm_lchan_name(lchan), lchan->abis_ip.speech_mode);
+ DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x RTP_PAYLOAD=%d\n",
+ gsm_lchan_name(lchan), lchan->abis_ip.speech_mode,
+ lchan->abis_ip.rtp_payload);
msg->trx = lchan->ts->trx;
@@ -1459,11 +1512,13 @@ int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
/* 0x0- == both directions, 0x-1 == EFR codec */
lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan);
+ lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan);
ia.s_addr = htonl(ip);
- DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD2=%d CONN_ID=%d "
- "speech_mode=0x%02x\n", gsm_lchan_name(lchan), inet_ntoa(ia), port,
- rtp_payload2, lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode);
+ DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD=%d RTP_PAYLOAD2=%d "
+ "CONN_ID=%d speech_mode=0x%02x\n", gsm_lchan_name(lchan),
+ inet_ntoa(ia), port, lchan->abis_ip.rtp_payload, rtp_payload2,
+ lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode);
msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP);
@@ -1471,6 +1526,7 @@ int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
*att_ip = ia.s_addr;
msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, port);
msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode);
+ msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload);
if (rtp_payload2)
msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2);
@@ -1652,9 +1708,21 @@ static int abis_rsl_rx_ipacc(struct msgb *msg)
/* Entry-point where L2 RSL from BTS enters */
int abis_rsl_rcvmsg(struct msgb *msg)
{
- struct abis_rsl_common_hdr *rslh = msgb_l2(msg) ;
+ struct abis_rsl_common_hdr *rslh;
int rc = 0;
+ if (!msg) {
+ DEBUGP(DRSL, "Empty RSL msg?..\n");
+ return -1;
+ }
+
+ if (msgb_l2len(msg) < sizeof(*rslh)) {
+ DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg));
+ return -1;
+ }
+
+ rslh = msgb_l2(msg);
+
switch (rslh->msg_discr & 0xfe) {
case ABIS_RSL_MDISC_RLL:
rc = abis_rsl_rx_rll(msg);
diff --git a/openbsc/src/bs11_config.c b/openbsc/src/bs11_config.c
index a7493b422..8f6de8a77 100644
--- a/openbsc/src/bs11_config.c
+++ b/openbsc/src/bs11_config.c
@@ -481,7 +481,7 @@ static int handle_state_resp(enum abis_bs11_phase state)
* argument, so our swload_cbfn can distinguish
* a safety load from a regular software */
if (file_is_readable(fname_safety))
- rc = abis_nm_software_load(g_bts, fname_safety,
+ rc = abis_nm_software_load(g_bts, 0xff, fname_safety,
win_size, param_forced,
swload_cbfn, g_bts);
else
@@ -697,7 +697,8 @@ int handle_serial_msg(struct msgb *rx_msg)
}
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
- struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
+ struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
+ struct abis_om_obj_inst *obj_ins)
{
return 0;
}
diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c
index f3436621f..77446a2c1 100644
--- a/openbsc/src/bsc_init.c
+++ b/openbsc/src/bsc_init.c
@@ -31,6 +31,7 @@
#include <openbsc/system_information.h>
#include <openbsc/paging.h>
#include <openbsc/signal.h>
+#include <openbsc/chan_alloc.h>
#include <osmocore/talloc.h>
/* global pointer to the gsm network data structure */
@@ -377,11 +378,11 @@ static unsigned char nanobts_attr_cell[] = {
4, /* N3103 */
8, /* N3105 */
15, /* RLC CV countdown */
- NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x0f, 0x00,
+ NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x0f, 0x00, /* CS1..CS4 */
NM_ATT_IPACC_RLC_CFG_2, 0, 5,
- 0x00, 250,
- 0x00, 250,
- 2, /* MCS2 */
+ 0x00, 250, /* T downlink TBF extension (0..500) */
+ 0x00, 250, /* T uplink TBF extension (0..500) */
+ 2, /* CS2 */
#if 0
/* EDGE model only, breaks older models.
* Should inquire the BTS capabilities */
@@ -400,7 +401,8 @@ static unsigned char nanobts_attr_nsvc0[] = {
/* Callback function to be called whenever we get a GSM 12.21 state change event */
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
- struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
+ struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
+ struct abis_om_obj_inst *obj_inst)
{
struct gsm_bts *bts;
struct gsm_bts_trx *trx;
@@ -461,7 +463,7 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
break;
case NM_OC_GPRS_NSE:
bts = container_of(obj, struct gsm_bts, gprs.nse);
- if (!bts->gprs.enabled)
+ if (bts->gprs.mode == BTS_GPRS_NONE)
break;
if (new_state->availability == 5) {
abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
@@ -475,7 +477,7 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
break;
case NM_OC_GPRS_CELL:
bts = container_of(obj, struct gsm_bts, gprs.cell);
- if (!bts->gprs.enabled)
+ if (bts->gprs.mode == BTS_GPRS_NONE)
break;
if (new_state->availability == 5) {
abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
@@ -490,9 +492,9 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
case NM_OC_GPRS_NSVC:
nsvc = obj;
bts = nsvc->bts;
- if (!bts->gprs.enabled)
+ if (bts->gprs.mode == BTS_GPRS_NONE)
break;
- /* We skip NSVC1 since we only use NSVC0 */
+ /* We skip NSVC1 since we only use NSVC0 */
if (nsvc->id == 1)
break;
if (new_state->availability == NM_AVSTATE_OFF_LINE) {
@@ -798,7 +800,7 @@ static int set_system_infos(struct gsm_bts_trx *trx)
DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
rsl_bcch_info(trx, i, si_tmp, sizeof(si_tmp));
}
- if (bts->gprs.enabled) {
+ if (bts->gprs.mode != BTS_GPRS_NONE) {
i = 13;
rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_13);
if (rc < 0)
@@ -852,6 +854,22 @@ static void patch_nm_tables(struct gsm_bts *bts)
bs11_attr_radio[2] |= arfcn_high;
bs11_attr_radio[3] = arfcn_low;
+ /* patch the RACH attributes */
+ if (bts->rach_b_thresh != -1) {
+ nanobts_attr_bts[33] = bts->rach_b_thresh & 0xff;
+ bs11_attr_bts[33] = bts->rach_b_thresh & 0xff;
+ }
+
+ if (bts->rach_ldavg_slots != -1) {
+ u_int8_t avg_high = bts->rach_ldavg_slots & 0xff;
+ u_int8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f;
+
+ nanobts_attr_bts[35] = avg_high;
+ nanobts_attr_bts[36] = avg_low;
+ bs11_attr_bts[35] = avg_high;
+ bs11_attr_bts[36] = avg_low;
+ }
+
/* patch BSIC */
bs11_attr_bts[1] = bts->bsic;
nanobts_attr_bts[sizeof(nanobts_attr_bts)-11] = bts->bsic;
@@ -866,6 +884,10 @@ static void patch_nm_tables(struct gsm_bts *bts)
/* patch NSEI */
nanobts_attr_nse[3] = bts->gprs.nse.nsei >> 8;
nanobts_attr_nse[4] = bts->gprs.nse.nsei & 0xff;
+ memcpy(nanobts_attr_nse+8, bts->gprs.nse.timer,
+ ARRAY_SIZE(bts->gprs.nse.timer));
+ memcpy(nanobts_attr_nse+18, bts->gprs.cell.timer,
+ ARRAY_SIZE(bts->gprs.cell.timer));
/* patch NSVCI */
nanobts_attr_nsvc0[3] = bts->gprs.nsvc[0].nsvci >> 8;
@@ -885,6 +907,11 @@ static void patch_nm_tables(struct gsm_bts *bts)
/* patch RAC */
nanobts_attr_cell[3] = bts->gprs.rac;
+ if (bts->gprs.mode == BTS_GPRS_EGPRS) {
+ /* patch EGPRS coding schemes MCS 1..9 */
+ nanobts_attr_cell[29] = 0x8f;
+ nanobts_attr_cell[30] = 0xff;
+ }
}
static void bootstrap_rsl(struct gsm_bts_trx *trx)
@@ -899,6 +926,8 @@ static void bootstrap_rsl(struct gsm_bts_trx *trx)
void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
{
+ int ts_no, lchan_no;
+
switch (event) {
case EVT_E1_TEI_UP:
switch (type) {
@@ -913,8 +942,35 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
}
break;
case EVT_E1_TEI_DN:
- LOGP(DMI, LOGL_NOTICE, "Lost some E1 TEI link\n");
- /* FIXME: deal with TEI or L1 link loss */
+ LOGP(DMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", type, trx);
+
+ if (type == E1INP_SIGN_OML)
+ counter_inc(trx->bts->network->stats.bts.oml_fail);
+ else if (type == E1INP_SIGN_RSL)
+ counter_inc(trx->bts->network->stats.bts.rsl_fail);
+
+ /*
+ * free all allocated channels. change the nm_state so the
+ * trx and trx_ts becomes unusable and chan_alloc.c can not
+ * allocate from it.
+ */
+ for (ts_no = 0; ts_no < ARRAY_SIZE(trx->ts); ++ts_no) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[ts_no];
+
+ for (lchan_no = 0; lchan_no < ARRAY_SIZE(ts->lchan); ++lchan_no) {
+ if (ts->lchan[lchan_no].state != GSM_LCHAN_NONE)
+ lchan_free(&ts->lchan[lchan_no]);
+ lchan_reset(&ts->lchan[lchan_no]);
+ }
+
+ ts->nm_state.operational = 0;
+ ts->nm_state.availability = 0;
+ }
+
+ trx->nm_state.operational = 0;
+ trx->nm_state.availability = 0;
+ trx->bb_transc.nm_state.operational = 0;
+ trx->bb_transc.nm_state.availability = 0;
break;
default:
break;
@@ -923,6 +979,8 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
static int bootstrap_bts(struct gsm_bts *bts)
{
+ int i, n;
+
switch (bts->band) {
case GSM_BAND_1800:
if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) {
@@ -959,10 +1017,34 @@ static int bootstrap_bts(struct gsm_bts *bts)
/* Control Channel Description */
bts->si_common.chan_desc.att = 1;
- bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5;
/* T3212 is set from vty/config */
+ /* Set ccch config by looking at ts config */
+ for (n=0, i=0; i<8; i++)
+ n += bts->c0->ts[i].pchan == GSM_PCHAN_CCCH ? 1 : 0;
+
+ switch (n) {
+ case 0:
+ bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
+ break;
+ case 1:
+ bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_NC;
+ break;
+ case 2:
+ bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_2_NC;
+ break;
+ case 3:
+ bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_3_NC;
+ break;
+ case 4:
+ bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_4_NC;
+ break;
+ default:
+ LOGP(DNM, LOGL_ERROR, "Unsupported CCCH timeslot configuration\n");
+ return -EINVAL;
+ }
+
/* some defaults for our system information */
bts->si_common.cell_options.radio_link_timeout = 2; /* 12 */
bts->si_common.cell_options.dtx = 2; /* MS shall not use upplink DTX */
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index cd48e4359..107abdc92 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -330,6 +330,20 @@ void lchan_free(struct gsm_lchan *lchan)
* channel using it */
}
+/*
+ * There was an error with the TRX and we need to forget
+ * any state so that a lchan can be allocated again after
+ * the trx is fully usable.
+ */
+void lchan_reset(struct gsm_lchan *lchan)
+{
+ bsc_del_timer(&lchan->T3101);
+
+ lchan->type = GSM_LCHAN_NONE;
+ lchan->state = LCHAN_S_NONE;
+}
+
+
/* Consider releasing the channel now */
int lchan_auto_release(struct gsm_lchan *lchan)
{
@@ -348,6 +362,7 @@ int lchan_auto_release(struct gsm_lchan *lchan)
lchan->conn.use_count);
DEBUGP(DRLL, "%s Recycling Channel\n", gsm_lchan_name(lchan));
+ rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ);
rsl_release_request(lchan, 0);
return 1;
}
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index 8bf47ab38..57a7863c9 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -20,13 +20,8 @@
*
*/
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_04_11.h>
-#include <openbsc/db.h>
-#include <osmocore/talloc.h>
-#include <openbsc/debug.h>
-#include <osmocore/statistics.h>
-
+#include <stdint.h>
+#include <inttypes.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
@@ -34,6 +29,14 @@
#include <errno.h>
#include <dbi/dbi.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_04_11.h>
+#include <openbsc/db.h>
+#include <osmocore/talloc.h>
+#include <openbsc/debug.h>
+#include <osmocore/statistics.h>
+#include <osmocore/rate_ctr.h>
+
static char *db_basename = NULL;
static char *db_dirname = NULL;
static dbi_conn conn;
@@ -124,6 +127,13 @@ static char *create_stmts[] = {
"value INTEGER NOT NULL, "
"name TEXT NOT NULL "
")",
+ "CREATE TABLE IF NOT EXISTS RateCounters ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "timestamp TIMESTAMP NOT NULL, "
+ "value INTEGER NOT NULL, "
+ "name TEXT NOT NULL, "
+ "index INTEGER NOT NULL "
+ ")",
"CREATE TABLE IF NOT EXISTS AuthKeys ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"subscriber_id INTEGER UNIQUE NOT NULL, "
@@ -1180,3 +1190,42 @@ int db_store_counter(struct counter *ctr)
dbi_result_free(result);
return 0;
}
+
+static int db_store_rate_ctr(struct rate_ctr_group *ctrg, unsigned int num,
+ char *q_prefix)
+{
+ dbi_result result;
+ char *q_name;
+
+ dbi_conn_quote_string_copy(conn, ctrg->desc->ctr_desc[num].name,
+ &q_name);
+
+ result = dbi_conn_queryf(conn,
+ "Insert INTO RateCounters "
+ "(timestamp,name,index,value) VALUES "
+ "(datetime('now'),%s.%s,%u,%"PRIu64")",
+ q_prefix, q_name, ctrg->idx, ctrg->ctr[num].current);
+
+ free(q_name);
+
+ if (!result)
+ return -EIO;
+
+ dbi_result_free(result);
+ return 0;
+}
+
+int db_store_rate_ctr_group(struct rate_ctr_group *ctrg)
+{
+ unsigned int i;
+ char *q_prefix;
+
+ dbi_conn_quote_string_copy(conn, ctrg->desc->group_name_prefix, &q_prefix);
+
+ for (i = 0; i < ctrg->desc->num_ctr; i++)
+ db_store_rate_ctr(ctrg, i, q_prefix);
+
+ free(q_prefix);
+
+ return 0;
+}
diff --git a/openbsc/src/debug.c b/openbsc/src/debug.c
index a55d79013..9218f6471 100644
--- a/openbsc/src/debug.c
+++ b/openbsc/src/debug.c
@@ -39,81 +39,81 @@
static const struct log_info_cat default_categories[] = {
[DRLL] = {
.name = "DRLL",
- .description = "Radio Link Layer",
+ .description = "A-bis Radio Link Layer (RLL)",
.color = "\033[1;31m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DCC] = {
.name = "DCC",
- .description = "Call Control",
+ .description = "Layer3 Call Control (CC)",
.color = "\033[1;32m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DMM] = {
.name = "DMM",
- .description = "Mobility Management",
+ .description = "Layer3 Mobility Management (MM)",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DRR] = {
.name = "DRR",
- .description = "Radio Resource",
+ .description = "Layer3 Radio Resource (RR)",
.color = "\033[1;34m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DRSL] = {
.name = "DRSL",
- .description = "Radio Siganlling Link",
+ .description = "A-bis Radio Siganlling Link (RSL)",
.color = "\033[1;35m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DNM] = {
.name = "DNM",
- .description = "Network Management (OML)",
+ .description = "A-bis Network Management / O&M (NM/OML)",
.color = "\033[1;36m",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DMNCC] = {
.name = "DMNCC",
- .description = "BSC<->MSC interface",
+ .description = "MNCC API for Call Control application",
.color = "\033[1;39m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DSMS] = {
.name = "DSMS",
- .description = "Short Message Service",
+ .description = "Layer3 Short Message Service (SMS)",
.color = "\033[1;37m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DPAG] = {
.name = "DPAG",
- .description = "Paging",
+ .description = "Paging Subsystem",
.color = "\033[1;38m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DMEAS] = {
.name = "DMEAS",
- .description = "Measurement Processing",
+ .description = "Radio Measurement Processing",
.enabled = 0, .loglevel = LOGL_NOTICE,
},
[DMI] = {
.name = "DMI",
- .description = "mISDN Input Driver",
+ .description = "A-bis Input Driver for Signalling",
.enabled = 0, .loglevel = LOGL_NOTICE,
},
[DMIB] = {
.name = "DMIB",
- .description = "mISDN B-Channels",
+ .description = "A-bis Input Driver for B-Channels (voice)",
.enabled = 0, .loglevel = LOGL_NOTICE,
},
[DMUX] = {
.name = "DMUX",
- .description = "TRAU Frame Multiplex",
+ .description = "A-bis B-Subchannel TRAU Frame Multiplex",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DINP] = {
.name = "DINP",
- .description = "Input Driver",
+ .description = "A-bis Intput Subsystem",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DSCCP] = {
@@ -138,7 +138,7 @@ static const struct log_info_cat default_categories[] = {
},
[DDB] = {
.name = "DDB",
- .description = "Database",
+ .description = "Database Layer",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DREF] = {
@@ -146,6 +146,21 @@ static const struct log_info_cat default_categories[] = {
.description = "Reference Counting",
.enabled = 0, .loglevel = LOGL_NOTICE,
},
+ [DGPRS] = {
+ .name = "DGPRS",
+ .description = "GPRS Packet Service",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DNS] = {
+ .name = "DNS",
+ .description = "GPRS Network Service (NS)",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DBSSGP] = {
+ .name = "DBSSGP",
+ .description = "GPRS BSS Gateway Protocol (BSSGP)",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
};
enum log_ctxt {
diff --git a/openbsc/src/e1_input.c b/openbsc/src/e1_input.c
index fba59a784..b1dfe9b1d 100644
--- a/openbsc/src/e1_input.c
+++ b/openbsc/src/e1_input.c
@@ -420,7 +420,17 @@ e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type,
void e1inp_sign_link_destroy(struct e1inp_sign_link *link)
{
+ struct msgb *msg;
+
llist_del(&link->list);
+ while (!llist_empty(&link->tx_list)) {
+ msg = msgb_dequeue(&link->tx_list);
+ msgb_free(msg);
+ }
+
+ if (link->ts->type == E1INP_TS_TYPE_SIGN)
+ bsc_del_timer(&link->ts->sign.tx_timer);
+
talloc_free(link);
}
diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am
new file mode 100644
index 000000000..88c358a1b
--- /dev/null
+++ b/openbsc/src/gprs/Makefile.am
@@ -0,0 +1,18 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
+AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
+AM_LDFLAGS = $(LIBOSMOCORE_LIBS)
+
+sbin_PROGRAMS = osmo-gbproxy osmo-sgsn
+noinst_LIBRARIES = libsgsn.a
+
+libsgsn_a_SOURCES = gprs_ns.c gprs_bssgp.c gprs_llc.c gsm_04_08_gprs.c \
+ crc24.c gprs_sgsn.c gprs_bssgp_util.c
+
+osmo_gbproxy_SOURCES = gb_proxy.c gb_proxy_main.c gb_proxy_vty.c \
+ gprs_ns.c gprs_ns_vty.c gprs_bssgp_util.c \
+ $(top_srcdir)/src/socket.c $(top_srcdir)/src/debug.c
+osmo_gbproxy_LDADD = $(top_builddir)/src/libvty.a
+
+osmo_sgsn_SOURCES = sgsn_main.c sgsn_vty.c gprs_ns_vty.c \
+ $(top_srcdir)/src/socket.c $(top_srcdir)/src/debug.c
+osmo_sgsn_LDADD = $(top_builddir)/src/libvty.a libsgsn.a
diff --git a/openbsc/src/gprs/crc24.c b/openbsc/src/gprs/crc24.c
new file mode 100644
index 000000000..108212083
--- /dev/null
+++ b/openbsc/src/gprs/crc24.c
@@ -0,0 +1,69 @@
+/* GPRS LLC CRC-24 Implementation */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <sys/types.h>
+#include <openbsc/crc24.h>
+
+/* CRC24 table - FCS */
+static const u_int32_t tbl_crc24[256] = {
+ 0x00000000, 0x00d6a776, 0x00f64557, 0x0020e221, 0x00b78115, 0x00612663, 0x0041c442, 0x00976334,
+ 0x00340991, 0x00e2aee7, 0x00c24cc6, 0x0014ebb0, 0x00838884, 0x00552ff2, 0x0075cdd3, 0x00a36aa5,
+ 0x00681322, 0x00beb454, 0x009e5675, 0x0048f103, 0x00df9237, 0x00093541, 0x0029d760, 0x00ff7016,
+ 0x005c1ab3, 0x008abdc5, 0x00aa5fe4, 0x007cf892, 0x00eb9ba6, 0x003d3cd0, 0x001ddef1, 0x00cb7987,
+ 0x00d02644, 0x00068132, 0x00266313, 0x00f0c465, 0x0067a751, 0x00b10027, 0x0091e206, 0x00474570,
+ 0x00e42fd5, 0x003288a3, 0x00126a82, 0x00c4cdf4, 0x0053aec0, 0x008509b6, 0x00a5eb97, 0x00734ce1,
+ 0x00b83566, 0x006e9210, 0x004e7031, 0x0098d747, 0x000fb473, 0x00d91305, 0x00f9f124, 0x002f5652,
+ 0x008c3cf7, 0x005a9b81, 0x007a79a0, 0x00acded6, 0x003bbde2, 0x00ed1a94, 0x00cdf8b5, 0x001b5fc3,
+ 0x00fb4733, 0x002de045, 0x000d0264, 0x00dba512, 0x004cc626, 0x009a6150, 0x00ba8371, 0x006c2407,
+ 0x00cf4ea2, 0x0019e9d4, 0x00390bf5, 0x00efac83, 0x0078cfb7, 0x00ae68c1, 0x008e8ae0, 0x00582d96,
+ 0x00935411, 0x0045f367, 0x00651146, 0x00b3b630, 0x0024d504, 0x00f27272, 0x00d29053, 0x00043725,
+ 0x00a75d80, 0x0071faf6, 0x005118d7, 0x0087bfa1, 0x0010dc95, 0x00c67be3, 0x00e699c2, 0x00303eb4,
+ 0x002b6177, 0x00fdc601, 0x00dd2420, 0x000b8356, 0x009ce062, 0x004a4714, 0x006aa535, 0x00bc0243,
+ 0x001f68e6, 0x00c9cf90, 0x00e92db1, 0x003f8ac7, 0x00a8e9f3, 0x007e4e85, 0x005eaca4, 0x00880bd2,
+ 0x00437255, 0x0095d523, 0x00b53702, 0x00639074, 0x00f4f340, 0x00225436, 0x0002b617, 0x00d41161,
+ 0x00777bc4, 0x00a1dcb2, 0x00813e93, 0x005799e5, 0x00c0fad1, 0x00165da7, 0x0036bf86, 0x00e018f0,
+ 0x00ad85dd, 0x007b22ab, 0x005bc08a, 0x008d67fc, 0x001a04c8, 0x00cca3be, 0x00ec419f, 0x003ae6e9,
+ 0x00998c4c, 0x004f2b3a, 0x006fc91b, 0x00b96e6d, 0x002e0d59, 0x00f8aa2f, 0x00d8480e, 0x000eef78,
+ 0x00c596ff, 0x00133189, 0x0033d3a8, 0x00e574de, 0x007217ea, 0x00a4b09c, 0x008452bd, 0x0052f5cb,
+ 0x00f19f6e, 0x00273818, 0x0007da39, 0x00d17d4f, 0x00461e7b, 0x0090b90d, 0x00b05b2c, 0x0066fc5a,
+ 0x007da399, 0x00ab04ef, 0x008be6ce, 0x005d41b8, 0x00ca228c, 0x001c85fa, 0x003c67db, 0x00eac0ad,
+ 0x0049aa08, 0x009f0d7e, 0x00bfef5f, 0x00694829, 0x00fe2b1d, 0x00288c6b, 0x00086e4a, 0x00dec93c,
+ 0x0015b0bb, 0x00c317cd, 0x00e3f5ec, 0x0035529a, 0x00a231ae, 0x007496d8, 0x005474f9, 0x0082d38f,
+ 0x0021b92a, 0x00f71e5c, 0x00d7fc7d, 0x00015b0b, 0x0096383f, 0x00409f49, 0x00607d68, 0x00b6da1e,
+ 0x0056c2ee, 0x00806598, 0x00a087b9, 0x007620cf, 0x00e143fb, 0x0037e48d, 0x001706ac, 0x00c1a1da,
+ 0x0062cb7f, 0x00b46c09, 0x00948e28, 0x0042295e, 0x00d54a6a, 0x0003ed1c, 0x00230f3d, 0x00f5a84b,
+ 0x003ed1cc, 0x00e876ba, 0x00c8949b, 0x001e33ed, 0x008950d9, 0x005ff7af, 0x007f158e, 0x00a9b2f8,
+ 0x000ad85d, 0x00dc7f2b, 0x00fc9d0a, 0x002a3a7c, 0x00bd5948, 0x006bfe3e, 0x004b1c1f, 0x009dbb69,
+ 0x0086e4aa, 0x005043dc, 0x0070a1fd, 0x00a6068b, 0x003165bf, 0x00e7c2c9, 0x00c720e8, 0x0011879e,
+ 0x00b2ed3b, 0x00644a4d, 0x0044a86c, 0x00920f1a, 0x00056c2e, 0x00d3cb58, 0x00f32979, 0x00258e0f,
+ 0x00eef788, 0x003850fe, 0x0018b2df, 0x00ce15a9, 0x0059769d, 0x008fd1eb, 0x00af33ca, 0x007994bc,
+ 0x00dafe19, 0x000c596f, 0x002cbb4e, 0x00fa1c38, 0x006d7f0c, 0x00bbd87a, 0x009b3a5b, 0x004d9d2d
+};
+
+#define INIT_CRC24 0xffffff
+
+u_int32_t crc24_calc(u_int32_t fcs, u_int8_t *cp, unsigned int len)
+{
+ while (len--)
+ fcs = (fcs >> 8) ^ tbl_crc24[(fcs ^ *cp++) & 0xff];
+ return fcs;
+}
diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c
new file mode 100644
index 000000000..5fbf9bfcc
--- /dev/null
+++ b/openbsc/src/gprs/gb_proxy.c
@@ -0,0 +1,580 @@
+/* NS-over-IP proxy */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by On Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#include <osmocore/talloc.h>
+#include <osmocore/select.h>
+
+#include <openbsc/signal.h>
+#include <openbsc/debug.h>
+#include <openbsc/gprs_ns.h>
+#include <openbsc/gprs_bssgp.h>
+#include <openbsc/gb_proxy.h>
+
+struct gbprox_peer {
+ struct llist_head list;
+
+ /* NS-VC over which we send/receive data to this BVC */
+ struct gprs_nsvc *nsvc;
+
+ /* BVCI used for Point-to-Point to this peer */
+ uint16_t bvci;
+
+ /* Routeing Area that this peer is part of (raw 04.08 encoding) */
+ uint8_t ra[6];
+};
+
+/* Linked list of all Gb peers (except SGSN) */
+static LLIST_HEAD(gbprox_bts_peers);
+
+/* Find the gbprox_peer by its BVCI */
+static struct gbprox_peer *peer_by_bvci(uint16_t bvci)
+{
+ struct gbprox_peer *peer;
+ llist_for_each_entry(peer, &gbprox_bts_peers, list) {
+ if (peer->bvci == bvci)
+ return peer;
+ }
+ return NULL;
+}
+
+static struct gbprox_peer *peer_by_nsvc(struct gprs_nsvc *nsvc)
+{
+ struct gbprox_peer *peer;
+ llist_for_each_entry(peer, &gbprox_bts_peers, list) {
+ if (peer->nsvc == nsvc)
+ return peer;
+ }
+ return NULL;
+}
+
+/* look-up a peer by its Routeing Area Code (RAC) */
+static struct gbprox_peer *peer_by_rac(const uint8_t *ra)
+{
+ struct gbprox_peer *peer;
+ llist_for_each_entry(peer, &gbprox_bts_peers, list) {
+ if (!memcmp(peer->ra, ra, 6))
+ return peer;
+ }
+ return NULL;
+}
+
+/* look-up a peer by its Location Area Code (LAC) */
+static struct gbprox_peer *peer_by_lac(const uint8_t *la)
+{
+ struct gbprox_peer *peer;
+ llist_for_each_entry(peer, &gbprox_bts_peers, list) {
+ if (!memcmp(peer->ra, la, 5))
+ return peer;
+ }
+ return NULL;
+}
+
+static struct gbprox_peer *peer_alloc(uint16_t bvci)
+{
+ struct gbprox_peer *peer;
+
+ peer = talloc_zero(tall_bsc_ctx, struct gbprox_peer);
+ if (!peer)
+ return NULL;
+
+ peer->bvci = bvci;
+ llist_add(&peer->list, &gbprox_bts_peers);
+
+ return peer;
+}
+
+static void peer_free(struct gbprox_peer *peer)
+{
+ llist_del(&peer->list);
+ talloc_free(peer);
+}
+
+/* FIXME: this needs to go to libosmocore/msgb.c */
+static struct msgb *msgb_copy(const struct msgb *msg, const char *name)
+{
+ struct msgb *new_msg;
+
+ new_msg = msgb_alloc(msg->data_len, name);
+ if (!new_msg)
+ return NULL;
+
+ /* copy header */
+ memcpy(new_msg, msg, sizeof(*new_msg));
+ /* copy data */
+ memcpy(new_msg->data, msg->data, new_msg->data_len);
+
+ return new_msg;
+}
+
+/* strip off the NS header */
+static void strip_ns_hdr(struct msgb *msg)
+{
+ int strip_len = msgb_bssgph(msg) - msg->data;
+ msgb_pull(msg, strip_len);
+}
+
+/* feed a message down the NS-VC associated with the specified peer */
+static int gbprox_relay2sgsn(struct msgb *old_msg, uint16_t ns_bvci)
+{
+ /* create a copy of the message so the old one can
+ * be free()d safely when we return from gbprox_rcvmsg() */
+ struct msgb *msg = msgb_copy(old_msg, "msgb_relay2sgsn");
+
+ DEBUGP(DGPRS, "NSEI=%u proxying BTS->SGSN (NS_BVCI=%u, NSEI=%u)\n",
+ msgb_nsei(msg), ns_bvci, gbcfg.nsip_sgsn_nsei);
+
+ msgb_bvci(msg) = ns_bvci;
+ msgb_nsei(msg) = gbcfg.nsip_sgsn_nsei;
+
+ strip_ns_hdr(msg);
+
+ return gprs_ns_sendmsg(bssgp_nsi, msg);
+}
+
+/* feed a message down the NS-VC associated with the specified peer */
+static int gbprox_relay2peer(struct msgb *old_msg, struct gbprox_peer *peer,
+ uint16_t ns_bvci)
+{
+ /* create a copy of the message so the old one can
+ * be free()d safely when we return from gbprox_rcvmsg() */
+ struct msgb *msg = msgb_copy(old_msg, "msgb_relay2peer");
+
+ DEBUGP(DGPRS, "NSEI=%u proxying SGSN->BSS (NS_BVCI=%u, NSEI=%u)\n",
+ msgb_nsei(msg), ns_bvci, peer->nsvc->nsei);
+
+ msgb_bvci(msg) = ns_bvci;
+ msgb_nsei(msg) = peer->nsvc->nsei;
+
+ /* Strip the old NS header, it will be replaced with a new one */
+ strip_ns_hdr(msg);
+
+ return gprs_ns_sendmsg(bssgp_nsi, msg);
+}
+
+/* Send a message to a peer identified by ptp_bvci but using ns_bvci
+ * in the NS hdr */
+static int gbprox_relay2bvci(struct msgb *msg, uint16_t ptp_bvci,
+ uint16_t ns_bvci)
+{
+ struct gbprox_peer *peer;
+
+ peer = peer_by_bvci(ptp_bvci);
+ if (!peer) {
+ LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n",
+ ptp_bvci);
+ return -ENOENT;
+ }
+
+ return gbprox_relay2peer(msg, peer, ns_bvci);
+}
+
+/* Receive an incoming signalling message from a BSS-side NS-VC */
+static int gbprox_rx_sig_from_bss(struct msgb *msg, struct gprs_nsvc *nsvc,
+ uint16_t ns_bvci)
+{
+ struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
+ struct tlv_parsed tp;
+ uint8_t pdu_type = bgph->pdu_type;
+ int data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
+ struct gbprox_peer *from_peer;
+ struct gprs_ra_id raid;
+
+ if (ns_bvci != 0) {
+ LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u BVCI=%u is not signalling\n",
+ nsvc->nsei, ns_bvci);
+ return -EINVAL;
+ }
+
+ /* we actually should never see those two for BVCI == 0, but double-check
+ * just to make sure */
+ if (pdu_type == BSSGP_PDUT_UL_UNITDATA ||
+ pdu_type == BSSGP_PDUT_DL_UNITDATA) {
+ LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u UNITDATA not allowed in "
+ "signalling\n", nsvc->nsei);
+ return -EINVAL;
+ }
+
+ bssgp_tlv_parse(&tp, bgph->data, data_len);
+
+ switch (pdu_type) {
+ case BSSGP_PDUT_SUSPEND:
+ case BSSGP_PDUT_RESUME:
+ /* We implement RAC snooping during SUSPEND/RESUME, since
+ * it establishes a relationsip between BVCI/peer and the
+ * routeing area code. The snooped information is then
+ * used for routing the {SUSPEND,RESUME}_[N]ACK back to
+ * the correct BSSGP */
+ if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA))
+ goto err_mand_ie;
+ from_peer = peer_by_nsvc(nsvc);
+ if (!from_peer)
+ goto err_no_peer;
+ memcpy(from_peer->ra, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA),
+ sizeof(from_peer->ra));
+ gsm48_parse_ra(&raid, from_peer->ra);
+ LOGP(DGPRS, LOGL_INFO, "NSEI=%u BSSGP SUSPEND/RESUME "
+ "RAC snooping: RAC %u-%u-%u-%u behind BVCI=%u, "
+ "NSVCI=%u\n",nsvc->nsei, raid.mcc, raid.mnc, raid.lac,
+ raid.rac , from_peer->bvci, nsvc->nsvci);
+ /* FIXME: This only supports one BSS per RA */
+ break;
+ case BSSGP_PDUT_BVC_RESET:
+ /* If we receive a BVC reset on the signalling endpoint, we
+ * don't want the SGSN to reset, as the signalling endpoint
+ * is common for all point-to-point BVCs (and thus all BTS) */
+ if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) {
+ uint16_t bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI));
+ LOGP(DGPRS, LOGL_INFO, "NSEI=%u Rx BVC RESET (BVCI=%u)\n",
+ nsvc->nsei, bvci);
+ if (bvci == 0) {
+ /* FIXME: only do this if SGSN is alive! */
+ LOGP(DGPRS, LOGL_INFO, "NSEI=%u Tx fake "
+ "BVC RESET ACK of BVCI=0\n", nsvc->nsei);
+ return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK,
+ nsvc->nsei, 0, ns_bvci);
+ }
+ from_peer = peer_by_bvci(bvci);
+ if (!from_peer) {
+ /* if a PTP-BVC is reset, and we don't know that
+ * PTP-BVCI yet, we should allocate a new peer */
+ LOGP(DGPRS, LOGL_INFO, "Allocationg new peer for "
+ "BVCI=%u via NSVCI=%u/NSEI=%u\n", bvci,
+ nsvc->nsvci, nsvc->nsei);
+ from_peer = peer_alloc(bvci);
+ from_peer->nsvc = nsvc;
+ }
+ if (TLVP_PRESENT(&tp, BSSGP_IE_CELL_ID)) {
+ struct gprs_ra_id raid;
+ /* We have a Cell Identifier present in this
+ * PDU, this means we can extend our local
+ * state information about this particular cell
+ * */
+ memcpy(from_peer->ra,
+ TLVP_VAL(&tp, BSSGP_IE_CELL_ID),
+ sizeof(from_peer->ra));
+ gsm48_parse_ra(&raid, from_peer->ra);
+ LOGP(DGPRS, LOGL_INFO, "NSEI=%u/BVCI=%u "
+ "Cell ID %u-%u-%u-%u\n", nsvc->nsei,
+ bvci, raid.mcc, raid.mnc, raid.lac,
+ raid.rac);
+ }
+ }
+ break;
+ }
+
+ /* Normally, we can simply pass on all signalling messages from BSS to
+ * SGSN */
+ return gbprox_relay2sgsn(msg, ns_bvci);
+err_no_peer:
+ LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) cannot find peer based on RAC\n",
+ nsvc->nsei);
+ return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
+err_mand_ie:
+ LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) missing mandatory RA IE\n",
+ nsvc->nsei);
+ return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
+}
+
+/* Receive paging request from SGSN, we need to relay to proper BSS */
+static int gbprox_rx_paging(struct msgb *msg, struct tlv_parsed *tp,
+ struct gprs_nsvc *nsvc, uint16_t ns_bvci)
+{
+ struct gbprox_peer *peer = NULL;
+
+ LOGP(DGPRS, LOGL_INFO, "NSEI=%u(SGSN) BSSGP PAGING ",
+ nsvc->nsei);
+ if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
+ uint16_t bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
+ LOGPC(DGPRS, LOGL_INFO, "routing by BVCI to peer BVCI=%u\n",
+ bvci);
+ } else if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
+ peer = peer_by_rac(TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
+ LOGPC(DGPRS, LOGL_INFO, "routing by RAC to peer BVCI=%u\n",
+ peer->bvci);
+ } else if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) {
+ peer = peer_by_lac(TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA));
+ LOGPC(DGPRS, LOGL_INFO, "routing by LAC to peer BVCI=%u\n",
+ peer->bvci);
+ } else
+ LOGPC(DGPRS, LOGL_INFO, "\n");
+
+ if (!peer) {
+ LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) BSSGP PAGING: "
+ "unable to route, missing IE\n", nsvc->nsei);
+ return -EINVAL;
+ }
+ return gbprox_relay2peer(msg, peer, ns_bvci);
+}
+
+/* Receive an incoming BVC-RESET message from the SGSN */
+static int rx_reset_from_sgsn(struct msgb *msg, struct tlv_parsed *tp,
+ struct gprs_nsvc *nsvc, uint16_t ns_bvci)
+{
+ struct gbprox_peer *peer;
+ uint16_t ptp_bvci;
+
+ if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
+ return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE,
+ NULL, msg);
+ }
+ ptp_bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
+
+ if (ptp_bvci >= 2) {
+ /* A reset for a PTP BVC was received, forward it to its
+ * respective peer */
+ peer = peer_by_bvci(ptp_bvci);
+ if (!peer) {
+ LOGP(DGPRS, LOGL_ERROR, "NSEI=%u BVCI=%u: Cannot find BSS\n",
+ nsvc->nsei, ptp_bvci);
+ return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI,
+ NULL, msg);
+ }
+ return gbprox_relay2peer(msg, peer, ns_bvci);
+ }
+
+ /* A reset for the Signalling entity has been received
+ * from the SGSN. As the signalling BVCI is shared
+ * among all the BSS's that we multiplex, it needs to
+ * be relayed */
+ llist_for_each_entry(peer, &gbprox_bts_peers, list)
+ gbprox_relay2peer(msg, peer, ns_bvci);
+
+ return 0;
+}
+
+/* Receive an incoming signalling message from the SGSN-side NS-VC */
+static int gbprox_rx_sig_from_sgsn(struct msgb *msg, struct gprs_nsvc *nsvc,
+ uint16_t ns_bvci)
+{
+ struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
+ struct tlv_parsed tp;
+ uint8_t pdu_type = bgph->pdu_type;
+ int data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
+ struct gbprox_peer *peer;
+ uint16_t bvci;
+ int rc = 0;
+
+ if (ns_bvci != 0) {
+ LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BVCI=%u is not "
+ "signalling\n", nsvc->nsei, ns_bvci);
+ /* FIXME: Send proper error message */
+ return -EINVAL;
+ }
+
+ /* we actually should never see those two for BVCI == 0, but double-check
+ * just to make sure */
+ if (pdu_type == BSSGP_PDUT_UL_UNITDATA ||
+ pdu_type == BSSGP_PDUT_DL_UNITDATA) {
+ LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) UNITDATA not allowed in "
+ "signalling\n", nsvc->nsei);
+ return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
+ }
+
+ rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
+
+ switch (pdu_type) {
+ case BSSGP_PDUT_BVC_RESET:
+ rc = rx_reset_from_sgsn(msg, &tp, nsvc, ns_bvci);
+ break;
+ case BSSGP_PDUT_FLUSH_LL:
+ case BSSGP_PDUT_BVC_BLOCK_ACK:
+ case BSSGP_PDUT_BVC_UNBLOCK_ACK:
+ case BSSGP_PDUT_BVC_RESET_ACK:
+ /* simple case: BVCI IE is mandatory */
+ if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI))
+ goto err_mand_ie;
+ bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI));
+ rc = gbprox_relay2bvci(msg, bvci, ns_bvci);
+ break;
+ case BSSGP_PDUT_PAGING_PS:
+ case BSSGP_PDUT_PAGING_CS:
+ /* process the paging request (LAC/RAC lookup) */
+ rc = gbprox_rx_paging(msg, &tp, nsvc, ns_bvci);
+ break;
+ case BSSGP_PDUT_STATUS:
+ /* Some exception has occurred */
+ LOGP(DGPRS, LOGL_NOTICE,
+ "NSEI=%u(SGSN) BSSGP STATUS ", nsvc->nsei);
+ if (!TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) {
+ LOGPC(DGPRS, LOGL_NOTICE, "\n");
+ goto err_mand_ie;
+ }
+ LOGPC(DGPRS, LOGL_NOTICE,
+ "cause=0x%02x(%s) ", *TLVP_VAL(&tp, BSSGP_IE_CAUSE),
+ bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE)));
+ if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) {
+ uint16_t *bvci = (uint16_t *)
+ TLVP_VAL(&tp, BSSGP_IE_BVCI);
+ LOGPC(DGPRS, LOGL_NOTICE,
+ "BVCI=%u\n", ntohs(*bvci));
+ } else
+ LOGPC(DGPRS, LOGL_NOTICE, "\n");
+ break;
+ /* those only exist in the SGSN -> BSS direction */
+ case BSSGP_PDUT_SUSPEND_ACK:
+ case BSSGP_PDUT_SUSPEND_NACK:
+ case BSSGP_PDUT_RESUME_ACK:
+ case BSSGP_PDUT_RESUME_NACK:
+ /* RAC IE is mandatory */
+ if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA))
+ goto err_mand_ie;
+ peer = peer_by_rac(TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA));
+ if (!peer)
+ goto err_no_peer;
+ rc = gbprox_relay2peer(msg, peer, ns_bvci);
+ break;
+ case BSSGP_PDUT_SGSN_INVOKE_TRACE:
+ LOGP(DGPRS, LOGL_ERROR,
+ "NSEI=%u(SGSN) BSSGP INVOKE TRACE not supported\n",nsvc->nsei);
+ rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
+ break;
+ default:
+ LOGP(DGPRS, LOGL_NOTICE, "BSSGP PDU type 0x%02x unknown\n",
+ pdu_type);
+ rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
+ break;
+ }
+
+ return rc;
+err_mand_ie:
+ LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) missing mandatory IE\n",
+ nsvc->nsei);
+ return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
+err_no_peer:
+ LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) cannot find peer based on RAC\n",
+ nsvc->nsei);
+ return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
+}
+
+/* Main input function for Gb proxy */
+int gbprox_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci)
+{
+ int rc;
+
+ /* Only BVCI=0 messages need special treatment */
+ if (ns_bvci == 0 || ns_bvci == 1) {
+ if (nsvc->remote_end_is_sgsn)
+ rc = gbprox_rx_sig_from_sgsn(msg, nsvc, ns_bvci);
+ else
+ rc = gbprox_rx_sig_from_bss(msg, nsvc, ns_bvci);
+ } else {
+ /* All other BVCI are PTP and thus can be simply forwarded */
+ if (!nsvc->remote_end_is_sgsn) {
+ rc = gbprox_relay2sgsn(msg, ns_bvci);
+ } else {
+ struct gbprox_peer *peer = peer_by_bvci(ns_bvci);
+ if (!peer) {
+ LOGP(DGPRS, LOGL_INFO, "Allocationg new peer for "
+ "BVCI=%u via NSVC=%u/NSEI=%u\n", ns_bvci,
+ nsvc->nsvci, nsvc->nsei);
+ peer = peer_alloc(ns_bvci);
+ peer->nsvc = nsvc;
+ }
+ rc = gbprox_relay2peer(msg, peer, ns_bvci);
+ }
+ }
+
+ return rc;
+}
+
+/* Signal handler for signals from NS layer */
+int gbprox_signal(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct ns_signal_data *nssd = signal_data;
+ struct gprs_nsvc *nsvc = nssd->nsvc;
+ struct gbprox_peer *peer;
+
+ if (subsys != SS_NS)
+ return 0;
+
+ if (signal == S_NS_RESET && nsvc->nsei == gbcfg.nsip_sgsn_nsei) {
+ /* We have received a NS-RESET from the NSEI and NSVC
+ * of the SGSN. This might happen with SGSN that start
+ * their own NS-RESET procedure without waiting for our
+ * NS-RESET */
+ nsvc->remote_end_is_sgsn = 1;
+ }
+
+ if (signal == S_NS_ALIVE_EXP && nsvc->remote_end_is_sgsn) {
+ LOGP(DGPRS, LOGL_NOTICE, "Tns alive expired too often, "
+ "re-starting RESET procedure\n");
+ nsip_connect(nsvc->nsi, &nsvc->ip.bts_addr, nsvc->nsei,
+ nsvc->nsvci);
+ }
+
+ /* We currently only care about signals from the SGSN */
+ if (!nsvc->remote_end_is_sgsn)
+ return 0;
+
+ /* iterate over all BTS peers and send the respective PDU */
+ llist_for_each_entry(peer, &gbprox_bts_peers, list) {
+ switch (signal) {
+ case S_NS_RESET:
+ gprs_ns_tx_reset(peer->nsvc, nssd->cause);
+ break;
+ case S_NS_BLOCK:
+ gprs_ns_tx_block(peer->nsvc, nssd->cause);
+ break;
+ case S_NS_UNBLOCK:
+ gprs_ns_tx_unblock(peer->nsvc);
+ break;
+ }
+ }
+ return 0;
+}
+
+
+#include <vty/command.h>
+
+gDEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy",
+ SHOW_STR "Display information about the Gb proxy")
+{
+ struct gbprox_peer *peer;
+
+ llist_for_each_entry(peer, &gbprox_bts_peers, list) {
+ struct gprs_nsvc *nsvc = peer->nsvc;
+ struct gprs_ra_id raid;
+ gsm48_parse_ra(&raid, peer->ra);
+
+ vty_out(vty, "NSEI %5u, NS-VC %5u, PTP-BVCI %u, "
+ "RAC %u-%u-%u-%u%s",
+ nsvc->nsei, nsvc->nsvci, peer->bvci,
+ raid.mcc, raid.mnc, raid.lac, raid.rac, VTY_NEWLINE);
+ if (nsvc->nsi->ll == GPRS_NS_LL_UDP)
+ vty_out(vty, " remote address %s:%u%s",
+ inet_ntoa(nsvc->ip.bts_addr.sin_addr),
+ ntohs(nsvc->ip.bts_addr.sin_port), VTY_NEWLINE);
+ }
+ return CMD_SUCCESS;
+}
diff --git a/openbsc/src/gprs/gb_proxy_main.c b/openbsc/src/gprs/gb_proxy_main.c
new file mode 100644
index 000000000..fc8f1648d
--- /dev/null
+++ b/openbsc/src/gprs/gb_proxy_main.c
@@ -0,0 +1,188 @@
+/* NS-over-IP proxy */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by On Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocore/talloc.h>
+#include <osmocore/select.h>
+#include <osmocore/rate_ctr.h>
+
+#include <openbsc/signal.h>
+#include <openbsc/debug.h>
+#include <openbsc/gprs_ns.h>
+#include <openbsc/gprs_bssgp.h>
+#include <openbsc/telnet_interface.h>
+#include <openbsc/vty.h>
+#include <openbsc/gb_proxy.h>
+
+#include "../../bscconfig.h"
+
+/* this is here for the vty... it will never be called */
+void subscr_put() { abort(); }
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+void *tall_bsc_ctx;
+
+const char *openbsc_version = "Osmocom NSIP Proxy " PACKAGE_VERSION;
+const char *openbsc_copyright =
+ "Copyright (C) 2010 Harald Welte and On-Waves\n"
+ "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\n"
+ "Dieter Spaar, Andreas Eversberg, Holger Freyther\n\n"
+ "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n";
+
+static char *config_file = "osmo_gbproxy.cfg";
+struct gbproxy_config gbcfg;
+
+/* Pointer to the SGSN peer */
+extern struct gbprox_peer *gbprox_peer_sgsn;
+
+/* call-back function for the NS protocol */
+static int proxy_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
+ struct msgb *msg, u_int16_t bvci)
+{
+ int rc = 0;
+
+ switch (event) {
+ case GPRS_NS_EVT_UNIT_DATA:
+ rc = gbprox_rcvmsg(msg, nsvc, bvci);
+ break;
+ default:
+ LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
+ if (msg)
+ talloc_free(msg);
+ rc = -EIO;
+ break;
+ }
+ return rc;
+}
+
+static void signal_handler(int signal)
+{
+ fprintf(stdout, "signal %u received\n", signal);
+
+ switch (signal) {
+ case SIGINT:
+ dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL);
+ sleep(1);
+ exit(0);
+ break;
+ case SIGABRT:
+ /* in case of abort, we want to obtain a talloc report
+ * and then return to the caller, who will abort the process */
+ case SIGUSR1:
+ talloc_report(tall_vty_ctx, stderr);
+ talloc_report_full(tall_bsc_ctx, stderr);
+ break;
+ case SIGUSR2:
+ talloc_report_full(tall_vty_ctx, stderr);
+ break;
+ default:
+ break;
+ }
+}
+
+extern void *tall_msgb_ctx;
+
+int main(int argc, char **argv)
+{
+ struct gsm_network dummy_network;
+ struct log_target *stderr_target;
+ struct sockaddr_in sin;
+ int rc;
+
+ tall_bsc_ctx = talloc_named_const(NULL, 0, "nsip_proxy");
+ tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb");
+
+ signal(SIGINT, &signal_handler);
+ signal(SIGABRT, &signal_handler);
+ signal(SIGUSR1, &signal_handler);
+ signal(SIGUSR2, &signal_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ log_init(&log_info);
+ stderr_target = log_target_create_stderr();
+ log_add_target(stderr_target);
+ log_set_all_filter(stderr_target, 1);
+
+ rate_ctr_init(tall_bsc_ctx);
+ telnet_init(&dummy_network, 4246);
+
+ bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb);
+ if (!bssgp_nsi) {
+ LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
+ exit(1);
+ }
+ gbcfg.nsi = bssgp_nsi;
+ gprs_ns_vty_init(bssgp_nsi);
+ register_signal_handler(SS_NS, &gbprox_signal, NULL);
+
+ rc = gbproxy_parse_config(config_file, &gbcfg);
+ if (rc < 0) {
+ LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n");
+ exit(2);
+ }
+
+ nsip_listen(bssgp_nsi, gbcfg.nsip_listen_port);
+
+ /* 'establish' the outgoing connection to the SGSN */
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(gbcfg.nsip_sgsn_port);
+ sin.sin_addr.s_addr = htonl(gbcfg.nsip_sgsn_ip);
+ nsip_connect(bssgp_nsi, &sin, gbcfg.nsip_sgsn_nsei,
+ gbcfg.nsip_sgsn_nsvci);
+
+ while (1) {
+ rc = bsc_select_main(0);
+ if (rc < 0)
+ exit(3);
+ }
+
+ exit(0);
+}
+
+struct gsm_network;
+int bsc_vty_init(struct gsm_network *dummy)
+{
+ cmd_init(1);
+ vty_init();
+
+ openbsc_vty_add_cmds();
+ gbproxy_vty_init();
+ return 0;
+}
+
diff --git a/openbsc/src/gprs/gb_proxy_vty.c b/openbsc/src/gprs/gb_proxy_vty.c
new file mode 100644
index 000000000..e1cf97e87
--- /dev/null
+++ b/openbsc/src/gprs/gb_proxy_vty.c
@@ -0,0 +1,183 @@
+/*
+ * (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocore/talloc.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/gb_proxy.h>
+#include <openbsc/gprs_ns.h>
+
+#include <vty/command.h>
+#include <vty/vty.h>
+
+static struct gbproxy_config *g_cfg = NULL;
+
+/*
+ * vty code for mgcp below
+ */
+static struct cmd_node gbproxy_node = {
+ GBPROXY_NODE,
+ "%s(gbproxy)#",
+ 1,
+};
+
+static int config_write_gbproxy(struct vty *vty)
+{
+ struct in_addr ia;
+
+ vty_out(vty, "gbproxy%s", VTY_NEWLINE);
+
+ if (g_cfg->nsip_listen_ip) {
+ ia.s_addr = htonl(g_cfg->nsip_listen_ip);
+ vty_out(vty, " nsip bss local ip %s%s", inet_ntoa(ia),
+ VTY_NEWLINE);
+ }
+ vty_out(vty, " nsip bss local port %u%s", g_cfg->nsip_listen_port,
+ VTY_NEWLINE);
+ ia.s_addr = htonl(g_cfg->nsip_sgsn_ip);
+ vty_out(vty, " nsip sgsn remote ip %s%s", inet_ntoa(ia),
+ VTY_NEWLINE);
+ vty_out(vty, " nsip sgsn remote port %u%s", g_cfg->nsip_sgsn_port,
+ VTY_NEWLINE);
+ vty_out(vty, " nsip sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei,
+ VTY_NEWLINE);
+ vty_out(vty, " nsip sgsn nsvci %u%s", g_cfg->nsip_sgsn_nsvci,
+ VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gbproxy,
+ cfg_gbproxy_cmd,
+ "gbproxy",
+ "Configure the Gb proxy")
+{
+ vty->node = GBPROXY_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nsip_bss_local_ip,
+ cfg_nsip_bss_local_ip_cmd,
+ "nsip bss local ip A.B.C.D",
+ "Set the IP address on which we listen for BSS connects")
+{
+ struct in_addr ia;
+
+ inet_aton(argv[0], &ia);
+ g_cfg->nsip_listen_ip = ntohl(ia.s_addr);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nsip_bss_local_port,
+ cfg_nsip_bss_local_port_cmd,
+ "nsip bss local port <0-65534>",
+ "Set the UDP port on which we listen for BSS connects")
+{
+ unsigned int port = atoi(argv[0]);
+
+ g_cfg->nsip_listen_port = port;
+ return CMD_SUCCESS;
+}
+
+
+DEFUN(cfg_nsip_sgsn_ip,
+ cfg_nsip_sgsn_ip_cmd,
+ "nsip sgsn remote ip A.B.C.D",
+ "Set the IP of the SGSN to which the proxy shall connect")
+{
+ struct in_addr ia;
+
+ inet_aton(argv[0], &ia);
+ g_cfg->nsip_sgsn_ip = ntohl(ia.s_addr);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nsip_sgsn_port,
+ cfg_nsip_sgsn_port_cmd,
+ "nsip sgsn remote port <0-65534>",
+ "Set the UDP port of the SGSN to which the proxy shall connect")
+{
+ unsigned int port = atoi(argv[0]);
+
+ g_cfg->nsip_sgsn_port = port;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nsip_sgsn_nsei,
+ cfg_nsip_sgsn_nsei_cmd,
+ "nsip sgsn nsei <0-65534>",
+ "Set the NSEI to be used in the connection with the SGSN")
+{
+ unsigned int port = atoi(argv[0]);
+
+ g_cfg->nsip_sgsn_nsei = port;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nsip_sgsn_nsvci,
+ cfg_nsip_sgsn_nsvci_cmd,
+ "nsip sgsn nsvci <0-65534>",
+ "Set the NSVCI to be used in the connection with the SGSN")
+{
+ unsigned int port = atoi(argv[0]);
+
+ g_cfg->nsip_sgsn_nsvci = port;
+ return CMD_SUCCESS;
+}
+
+int gbproxy_vty_init(void)
+{
+ install_element_ve(&show_gbproxy_cmd);
+
+ install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
+ install_node(&gbproxy_node, config_write_gbproxy);
+ install_default(GBPROXY_NODE);
+ install_element(GBPROXY_NODE, &cfg_nsip_bss_local_ip_cmd);
+ install_element(GBPROXY_NODE, &cfg_nsip_bss_local_port_cmd);
+ install_element(GBPROXY_NODE, &cfg_nsip_sgsn_ip_cmd);
+ install_element(GBPROXY_NODE, &cfg_nsip_sgsn_port_cmd);
+ install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd);
+ install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsvci_cmd);
+
+ return 0;
+}
+
+int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
+{
+ int rc;
+
+ g_cfg = cfg;
+ rc = vty_read_config_file(config_file);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
+ return rc;
+ }
+
+ return 0;
+}
+
diff --git a/openbsc/src/gprs/gprs_bssgp.c b/openbsc/src/gprs/gprs_bssgp.c
new file mode 100644
index 000000000..9fdfd329d
--- /dev/null
+++ b/openbsc/src/gprs/gprs_bssgp.c
@@ -0,0 +1,448 @@
+/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+
+#include <netinet/in.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/talloc.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_04_08_gprs.h>
+#include <openbsc/gprs_bssgp.h>
+#include <openbsc/gprs_llc.h>
+#include <openbsc/gprs_ns.h>
+
+void *bssgp_tall_ctx = NULL;
+
+#define BVC_F_BLOCKED 0x0001
+
+/* The per-BTS context that we keep on the SGSN side of the BSSGP link */
+struct bssgp_bts_ctx {
+ struct llist_head list;
+
+ /* parsed RA ID and Cell ID of the remote BTS */
+ struct gprs_ra_id ra_id;
+ uint16_t cell_id;
+
+ /* NSEI and BVCI of underlying Gb link. Together they
+ * uniquely identify a link to a BTS (5.4.4) */
+ uint16_t bvci;
+ uint16_t nsei;
+
+ uint32_t bvc_state;
+
+ /* we might want to add this as a shortcut later, avoiding the NSVC
+ * lookup for every packet, similar to a routing cache */
+ //struct gprs_nsvc *nsvc;
+};
+static LLIST_HEAD(bts_ctxts);
+
+/* Find a BTS Context based on parsed RA ID and Cell ID */
+struct bssgp_bts_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid)
+{
+ struct bssgp_bts_ctx *bctx;
+
+ llist_for_each_entry(bctx, &bts_ctxts, list) {
+ if (!memcmp(&bctx->ra_id, raid, sizeof(bctx->ra_id)) &&
+ bctx->cell_id == cid)
+ return bctx;
+ }
+ return NULL;
+}
+
+/* Find a BTS context based on BVCI+NSEI tuple */
+struct bssgp_bts_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei)
+{
+ struct bssgp_bts_ctx *bctx;
+
+ llist_for_each_entry(bctx, &bts_ctxts, list) {
+ if (bctx->nsei == nsei && bctx->bvci == bvci)
+ return bctx;
+ }
+ return NULL;
+}
+
+struct bssgp_bts_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei)
+{
+ struct bssgp_bts_ctx *ctx;
+
+ ctx = talloc_zero(bssgp_tall_ctx, struct bssgp_bts_ctx);
+ if (!ctx)
+ return NULL;
+ ctx->bvci = bvci;
+ ctx->nsei = nsei;
+ llist_add(&ctx->list, &bts_ctxts);
+
+ return ctx;
+}
+
+/* Chapter 10.4.5: Flow Control BVC ACK */
+static int bssgp_tx_fc_bvc_ack(uint16_t nsei, uint8_t tag, uint16_t ns_bvci)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph =
+ (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+
+ msgb_nsei(msg) = nsei;
+ msgb_bvci(msg) = ns_bvci;
+
+ bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK;
+ msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag);
+
+ return gprs_ns_sendmsg(bssgp_nsi, msg);
+}
+
+uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf)
+{
+ /* 6 octets RAC */
+ gsm48_parse_ra(raid, buf);
+ /* 2 octets CID */
+ return ntohs(*(uint16_t *) (buf+6));
+}
+
+/* Chapter 8.4 BVC-Reset Procedure */
+static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
+ uint16_t ns_bvci)
+{
+ struct bssgp_bts_ctx *bctx;
+ uint16_t nsei = msgb_nsei(msg);
+ uint16_t bvci;
+ int rc;
+
+ bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
+ DEBUGPC(DBSSGP, "BVCI=%u, cause=%s\n", bvci,
+ bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE)));
+
+ /* look-up or create the BTS context for this BVC */
+ bctx = btsctx_by_bvci_nsei(bvci, nsei);
+ if (!bctx)
+ bctx = btsctx_alloc(bvci, nsei);
+
+ /* When we receive a BVC-RESET PDU (at least of a PTP BVCI), the BSS
+ * informs us about its RAC + Cell ID, so we can create a mapping */
+ if (bvci != 0 && bvci != 1) {
+ if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) {
+ LOGP(DBSSGP, LOGL_ERROR, "BSSGP RESET BVCI=%u "
+ "missing mandatory IE\n", bvci);
+ return -EINVAL;
+ }
+ /* actually extract RAC / CID */
+ bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id,
+ TLVP_VAL(tp, BSSGP_IE_CELL_ID));
+ LOGP(DBSSGP, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n",
+ bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac,
+ bctx->ra_id.rac, bctx->cell_id, bvci);
+ }
+
+ /* Acknowledge the RESET to the BTS */
+ rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK,
+ nsei, bvci, ns_bvci);
+ return 0;
+}
+
+/* Uplink unit-data */
+static int bssgp_rx_ul_ud(struct msgb *msg)
+{
+ struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
+ int data_len = msgb_bssgp_len(msg) - sizeof(*budh);
+ struct tlv_parsed tp;
+ int rc;
+
+ DEBUGP(DBSSGP, "BSSGP UL-UD\n");
+
+ /* extract TLLI and parse TLV IEs */
+ msgb_tlli(msg) = ntohl(budh->tlli);
+ rc = bssgp_tlv_parse(&tp, budh->data, data_len);
+
+ /* Cell ID and LLC_PDU are the only mandatory IE */
+ if (!TLVP_PRESENT(&tp, BSSGP_IE_CELL_ID) ||
+ !TLVP_PRESENT(&tp, BSSGP_IE_LLC_PDU))
+ return -EIO;
+
+ /* FIXME: lookup bssgp_bts_ctx based on BVCI + NSEI */
+
+ /* store pointer to LLC header and CELL ID in msgb->cb */
+ msgb_llch(msg) = TLVP_VAL(&tp, BSSGP_IE_LLC_PDU);
+ msgb_bcid(msg) = TLVP_VAL(&tp, BSSGP_IE_CELL_ID);
+
+ return gprs_llc_rcvmsg(msg, &tp);
+}
+
+static int bssgp_rx_suspend(struct msgb *msg)
+{
+ struct bssgp_normal_hdr *bgph =
+ (struct bssgp_normal_hdr *) msgb_bssgph(msg);
+ int data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
+ struct tlv_parsed tp;
+ int rc;
+
+ DEBUGP(DBSSGP, "BSSGP SUSPEND\n");
+
+ rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
+ if (rc < 0)
+ return rc;
+
+ if (!TLVP_PRESENT(&tp, BSSGP_IE_TLLI) ||
+ !TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA))
+ return -EIO;
+
+ /* FIXME: pass the SUSPEND request to GMM */
+ /* SEND SUSPEND_ACK or SUSPEND_NACK */
+}
+
+static int bssgp_rx_resume(struct msgb *msg)
+{
+ struct bssgp_normal_hdr *bgph =
+ (struct bssgp_normal_hdr *) msgb_bssgph(msg);
+ int data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
+ struct tlv_parsed tp;
+ int rc;
+
+ DEBUGP(DBSSGP, "BSSGP RESUME\n");
+
+ rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
+ if (rc < 0)
+ return rc;
+
+ if (!TLVP_PRESENT(&tp, BSSGP_IE_TLLI) ||
+ !TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA) ||
+ !TLVP_PRESENT(&tp, BSSGP_IE_SUSPEND_REF_NR))
+ return -EIO;
+
+ /* FIXME: pass the RESUME request to GMM */
+ /* SEND RESUME_ACK or RESUME_NACK */
+}
+
+static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp)
+{
+
+ DEBUGP(DBSSGP, "BSSGP FC BVC\n");
+
+ if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) ||
+ !TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) ||
+ !TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) ||
+ !TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) ||
+ !TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS))
+ return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
+
+ /* FIXME: actually implement flow control */
+
+ /* Send FLOW_CONTROL_BVC_ACK */
+ return bssgp_tx_fc_bvc_ack(msgb_nsei(msg), *TLVP_VAL(tp, BSSGP_IE_TAG),
+ msgb_bvci(msg));
+}
+
+/* We expect msgb_bssgph() to point to the BSSGP header */
+int gprs_bssgp_rcvmsg(struct msgb *msg)
+{
+ struct bssgp_normal_hdr *bgph =
+ (struct bssgp_normal_hdr *) msgb_bssgph(msg);
+ struct tlv_parsed tp;
+ uint8_t pdu_type = bgph->pdu_type;
+ int data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
+ uint16_t bvci; /* PTP BVCI */
+ uint16_t ns_bvci = msgb_bvci(msg);
+ int rc = 0;
+
+ /* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
+
+ /* UNITDATA BSSGP headers have TLLI in front */
+ if (pdu_type != BSSGP_PDUT_UL_UNITDATA &&
+ pdu_type != BSSGP_PDUT_DL_UNITDATA)
+ rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
+
+ switch (pdu_type) {
+ case BSSGP_PDUT_UL_UNITDATA:
+ /* some LLC data from the MS */
+ rc = bssgp_rx_ul_ud(msg);
+ break;
+ case BSSGP_PDUT_RA_CAPABILITY:
+ /* BSS requests RA capability or IMSI */
+ DEBUGP(DBSSGP, "BSSGP RA CAPABILITY UPDATE\n");
+ /* FIXME: send RA_CAPA_UPDATE_ACK */
+ break;
+ case BSSGP_PDUT_RADIO_STATUS:
+ DEBUGP(DBSSGP, "BSSGP RADIO STATUS\n");
+ /* BSS informs us of some exception */
+ /* FIXME: notify GMM */
+ break;
+ case BSSGP_PDUT_SUSPEND:
+ /* MS wants to suspend */
+ rc = bssgp_rx_suspend(msg);
+ break;
+ case BSSGP_PDUT_RESUME:
+ /* MS wants to resume */
+ rc = bssgp_rx_resume(msg);
+ break;
+ case BSSGP_PDUT_FLUSH_LL:
+ /* BSS informs MS has moved to one cell to other cell */
+ DEBUGP(DBSSGP, "BSSGP FLUSH LL\n");
+ /* FIXME: notify GMM */
+ /* Send FLUSH_LL_ACK */
+ break;
+ case BSSGP_PDUT_LLC_DISCARD:
+ /* BSS informs that some LLC PDU's have been discarded */
+ DEBUGP(DBSSGP, "BSSGP LLC DISCARDED\n");
+ /* FIXME: notify GMM */
+ break;
+ case BSSGP_PDUT_FLOW_CONTROL_BVC:
+ /* BSS informs us of available bandwidth in Gb interface */
+ rc = bssgp_rx_fc_bvc(msg, &tp);
+ break;
+ case BSSGP_PDUT_FLOW_CONTROL_MS:
+ /* BSS informs us of available bandwidth to one MS */
+ DEBUGP(DBSSGP, "BSSGP FC MS\n");
+ /* FIXME: actually implement flow control */
+ /* FIXME: Send FLOW_CONTROL_MS_ACK */
+ break;
+ case BSSGP_PDUT_BVC_BLOCK:
+ /* BSS tells us that BVC shall be blocked */
+ DEBUGP(DBSSGP, "BSSGP BVC BLOCK ");
+ if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI) ||
+ !TLVP_PRESENT(&tp, BSSGP_IE_CAUSE))
+ goto err_mand_ie;
+ bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI));
+ DEBUGPC(DBSSGP, "BVCI=%u, cause=%s\n", bvci,
+ bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE)));
+ /* We always acknowledge the BLOCKing */
+ rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK,
+ msgb_nsei(msg), bvci, ns_bvci);
+ break;
+ case BSSGP_PDUT_BVC_UNBLOCK:
+ /* BSS tells us that BVC shall be unblocked */
+ DEBUGP(DBSSGP, "BSSGP BVC UNBLOCK ");
+ if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI))
+ goto err_mand_ie;
+ bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI));
+ DEBUGPC(DBSSGP, "BVCI=%u\n", bvci);
+ /* We always acknowledge the unBLOCKing */
+ rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK,
+ msgb_nsei(msg), bvci, ns_bvci);
+ break;
+ case BSSGP_PDUT_BVC_RESET:
+ /* BSS tells us that BVC init is required */
+ DEBUGP(DBSSGP, "BSSGP BVC RESET ");
+ if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI) ||
+ !TLVP_PRESENT(&tp, BSSGP_IE_CAUSE))
+ goto err_mand_ie;
+ rc = bssgp_rx_bvc_reset(msg, &tp, ns_bvci);
+ break;
+ case BSSGP_PDUT_STATUS:
+ /* Some exception has occurred */
+ /* FIXME: notify GMM */
+ case BSSGP_PDUT_DOWNLOAD_BSS_PFC:
+ case BSSGP_PDUT_CREATE_BSS_PFC_ACK:
+ case BSSGP_PDUT_CREATE_BSS_PFC_NACK:
+ case BSSGP_PDUT_MODIFY_BSS_PFC:
+ case BSSGP_PDUT_DELETE_BSS_PFC_ACK:
+ DEBUGP(DBSSGP, "BSSGP PDU type 0x%02x not [yet] implemented\n",
+ pdu_type);
+ break;
+ /* those only exist in the SGSN -> BSS direction */
+ case BSSGP_PDUT_DL_UNITDATA:
+ case BSSGP_PDUT_PAGING_PS:
+ case BSSGP_PDUT_PAGING_CS:
+ case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
+ case BSSGP_PDUT_SUSPEND_ACK:
+ case BSSGP_PDUT_SUSPEND_NACK:
+ case BSSGP_PDUT_RESUME_ACK:
+ case BSSGP_PDUT_RESUME_NACK:
+ case BSSGP_PDUT_FLUSH_LL_ACK:
+ case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
+ case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
+ case BSSGP_PDUT_BVC_BLOCK_ACK:
+ case BSSGP_PDUT_BVC_UNBLOCK_ACK:
+ case BSSGP_PDUT_SGSN_INVOKE_TRACE:
+ DEBUGP(DBSSGP, "BSSGP PDU type 0x%02x only exists in DL\n",
+ pdu_type);
+ rc = -EINVAL;
+ break;
+ default:
+ DEBUGP(DBSSGP, "BSSGP PDU type 0x%02x unknown\n", pdu_type);
+ break;
+ }
+
+ return rc;
+err_mand_ie:
+ return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
+}
+
+/* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU
+ * to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */
+int gprs_bssgp_tx_dl_ud(struct msgb *msg)
+{
+ struct bssgp_bts_ctx *bctx;
+ struct bssgp_ud_hdr *budh;
+ uint8_t llc_pdu_tlv_hdr_len = 2;
+ uint8_t *llc_pdu_tlv, *qos_profile;
+ uint16_t pdu_lifetime = 1000; /* centi-seconds */
+ uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x21 };
+ uint16_t msg_len = msg->len;
+ uint16_t bvci = msgb_bvci(msg);
+ uint16_t nsei = msgb_nsei(msg);
+
+ /* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */
+ if (bvci < 2) {
+ LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n",
+ bvci);
+ return -EINVAL;
+ }
+
+ bctx = btsctx_by_bvci_nsei(bvci, nsei);
+ if (!bctx)
+ bctx = btsctx_alloc(bvci, nsei);
+
+ if (msg->len > TVLV_MAX_ONEBYTE)
+ llc_pdu_tlv_hdr_len += 1;
+
+ /* prepend the tag and length of the LLC-PDU TLV */
+ llc_pdu_tlv = msgb_push(msg, llc_pdu_tlv_hdr_len);
+ llc_pdu_tlv[0] = BSSGP_IE_LLC_PDU;
+ if (llc_pdu_tlv_hdr_len > 2) {
+ llc_pdu_tlv[1] = msg_len >> 8;
+ llc_pdu_tlv[2] = msg_len & 0xff;
+ } else {
+ llc_pdu_tlv[1] = msg_len & 0x3f;
+ llc_pdu_tlv[1] |= 0x80;
+ }
+
+ /* FIXME: optional elements */
+
+ /* prepend the pdu lifetime */
+ pdu_lifetime = htons(pdu_lifetime);
+ msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (uint8_t *)&pdu_lifetime);
+
+ /* prepend the QoS profile, TLLI and pdu type */
+ budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh));
+ memcpy(budh->qos_profile, qos_profile_default, sizeof(qos_profile_default));
+ budh->tlli = htonl(msgb_tlli(msg));
+ budh->pdu_type = BSSGP_PDUT_DL_UNITDATA;
+
+ /* Identifiers down: BVCI, NSEI (in msgb->cb) */
+
+ return gprs_ns_sendmsg(bssgp_nsi, msg);
+}
diff --git a/openbsc/src/gprs/gprs_bssgp_util.c b/openbsc/src/gprs/gprs_bssgp_util.c
new file mode 100644
index 000000000..d9b5175f6
--- /dev/null
+++ b/openbsc/src/gprs/gprs_bssgp_util.c
@@ -0,0 +1,119 @@
+/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+
+#include <netinet/in.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/talloc.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gprs_bssgp.h>
+#include <openbsc/gprs_ns.h>
+
+struct gprs_ns_inst *bssgp_nsi;
+
+/* BSSGP Protocol specific, not implementation specific */
+/* FIXME: This needs to go into libosmocore after finished */
+
+/* Chapter 11.3.9 / Table 11.10: Cause coding */
+static const struct value_string bssgp_cause_strings[] = {
+ { BSSGP_CAUSE_PROC_OVERLOAD, "Processor overload" },
+ { BSSGP_CAUSE_EQUIP_FAIL, "Equipment Failure" },
+ { BSSGP_CAUSE_TRASIT_NET_FAIL, "Transit netowkr service failure" },
+ { BSSGP_CAUSE_CAPA_GREATER_0KPBS,"Transmission capacity modified" },
+ { BSSGP_CAUSE_UNKNOWN_MS, "Unknown MS" },
+ { BSSGP_CAUSE_UNKNOWN_BVCI, "Unknown BVCI" },
+ { BSSGP_CAUSE_CELL_TRAF_CONG, "Cell traffic congestion" },
+ { BSSGP_CAUSE_SGSN_CONG, "SGSN congestion" },
+ { BSSGP_CAUSE_OML_INTERV, "O&M intervention" },
+ { BSSGP_CAUSE_BVCI_BLOCKED, "BVCI blocked" },
+ { BSSGP_CAUSE_PFC_CREATE_FAIL, "PFC create failure" },
+ { BSSGP_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" },
+ { BSSGP_CAUSE_INV_MAND_INF, "Invalid mandatory information" },
+ { BSSGP_CAUSE_MISSING_MAND_IE, "Missing mandatory IE" },
+ { BSSGP_CAUSE_MISSING_COND_IE, "Missing conditional IE" },
+ { BSSGP_CAUSE_UNEXP_COND_IE, "Unexpected conditional IE" },
+ { BSSGP_CAUSE_COND_IE_ERR, "Conditional IE error" },
+ { BSSGP_CAUSE_PDU_INCOMP_STATE, "PDU incompatible with protocol state" },
+ { BSSGP_CAUSE_PROTO_ERR_UNSPEC, "Protocol error - unspecified" },
+ { BSSGP_CAUSE_PDU_INCOMP_FEAT, "PDU not compatible with feature set" },
+ { 0, NULL },
+};
+
+const char *bssgp_cause_str(enum gprs_bssgp_cause cause)
+{
+ return get_value_string(bssgp_cause_strings, cause);
+}
+
+
+struct msgb *bssgp_msgb_alloc(void)
+{
+ return msgb_alloc_headroom(4096, 128, "BSSGP");
+}
+
+/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */
+int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei,
+ uint16_t bvci, uint16_t ns_bvci)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph =
+ (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ uint16_t _bvci;
+
+ msgb_nsei(msg) = nsei;
+ msgb_bvci(msg) = ns_bvci;
+
+ bgph->pdu_type = pdu_type;
+ _bvci = htons(bvci);
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
+
+ return gprs_ns_sendmsg(bssgp_nsi, msg);
+}
+
+/* Chapter 10.4.14: Status */
+int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph =
+ (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+
+ DEBUGPC(DBSSGP, "BSSGP: TX STATUS, cause=%s\n", bssgp_cause_str(cause));
+ msgb_nsei(msg) = msgb_nsei(orig_msg);
+ msgb_bvci(msg) = 0;
+
+ bgph->pdu_type = BSSGP_PDUT_STATUS;
+ msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause);
+ if (bvci) {
+ uint16_t _bvci = htons(*bvci);
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
+ }
+ if (orig_msg)
+ msgb_tvlv_put(msg, BSSGP_IE_PDU_IN_ERROR,
+ msgb_bssgp_len(orig_msg), msgb_bssgph(orig_msg));
+
+ return gprs_ns_sendmsg(bssgp_nsi, msg);
+}
diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c
new file mode 100644
index 000000000..9c75a3d4e
--- /dev/null
+++ b/openbsc/src/gprs/gprs_llc.c
@@ -0,0 +1,549 @@
+/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/linuxlist.h>
+#include <osmocore/timer.h>
+#include <osmocore/talloc.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/debug.h>
+#include <openbsc/gprs_bssgp.h>
+#include <openbsc/gprs_llc.h>
+#include <openbsc/crc24.h>
+
+/* Section 4.5.2 Logical Link States + Annex C.2 */
+enum gprs_llc_ll_state {
+ GPRS_LLS_UNASSIGNED = 1, /* No TLLI yet */
+ GPRS_LLS_ASSIGNED_ADM = 2, /* TLLI assigned */
+ GPRS_LLS_LOCAL_EST = 3, /* Local Establishment */
+ GPRS_LLS_REMOTE_EST = 4, /* Remote Establishment */
+ GPRS_LLS_ABM = 5,
+ GPRS_LLS_LOCAL_REL = 6, /* Local Release */
+ GPRS_LLS_TIMER_REC = 7, /* Timer Recovery */
+};
+
+/* Section 4.7.1: Logical Link Entity: One per DLCI (TLLI + SAPI) */
+struct gprs_llc_lle {
+ struct llist_head list;
+ struct timer_list t200;
+ struct timer_list t201; /* wait for acknowledgement */
+
+ enum gprs_llc_ll_state state;
+
+ uint32_t tlli;
+ uint32_t sapi;
+
+ uint8_t v_sent;
+ uint8_t v_ack;
+ uint8_t v_recv;
+
+ unsigned int n200;
+ unsigned int retrans_ctr;
+
+ /* over which BSSGP BTS ctx do we need to transmit */
+ uint16_t bvci;
+ uint16_t nsei;
+};
+
+static LLIST_HEAD(gprs_llc_lles);
+void *llc_tall_ctx;
+
+/* lookup LLC Entity based on DLCI (TLLI+SAPI tuple) */
+static struct gprs_llc_lle *lle_by_tlli_sapi(uint32_t tlli, uint32_t sapi)
+{
+ struct gprs_llc_lle *lle;
+
+ llist_for_each_entry(lle, &gprs_llc_lles, list) {
+ if (lle->tlli == tlli && lle->sapi == sapi)
+ return lle;
+ }
+ return NULL;
+}
+
+static struct gprs_llc_lle *lle_alloc(uint32_t tlli, uint32_t sapi)
+{
+ struct gprs_llc_lle *lle;
+
+ lle = talloc_zero(llc_tall_ctx, struct gprs_llc_lle);
+ if (!lle)
+ return NULL;
+
+ lle->tlli = tlli;
+ lle->sapi = sapi;
+ lle->state = GPRS_LLS_UNASSIGNED;
+ llist_add(&lle->list, &gprs_llc_lles);
+
+ return lle;
+}
+
+enum gprs_llc_cmd {
+ GPRS_LLC_NULL,
+ GPRS_LLC_RR,
+ GPRS_LLC_ACK,
+ GPRS_LLC_RNR,
+ GPRS_LLC_SACK,
+ GPRS_LLC_DM,
+ GPRS_LLC_DISC,
+ GPRS_LLC_UA,
+ GPRS_LLC_SABM,
+ GPRS_LLC_FRMR,
+ GPRS_LLC_XID,
+};
+
+struct gprs_llc_hdr_parsed {
+ uint8_t sapi;
+ uint8_t is_cmd:1,
+ ack_req:1,
+ is_encrypted:1;
+ uint32_t seq_rx;
+ uint32_t seq_tx;
+ uint32_t fcs;
+ uint32_t fcs_calc;
+ uint8_t *data;
+ uint16_t data_len;
+ enum gprs_llc_cmd cmd;
+};
+
+#define LLC_ALLOC_SIZE 16384
+#define UI_HDR_LEN 3
+#define N202 4
+#define CRC24_LENGTH 3
+
+static int gprs_llc_fcs(uint8_t *data, unsigned int len)
+{
+ uint32_t fcs_calc;
+
+ fcs_calc = crc24_calc(INIT_CRC24, data, len);
+ fcs_calc = ~fcs_calc;
+ fcs_calc &= 0xffffff;
+
+ return fcs_calc;
+}
+
+static void t200_expired(void *data)
+{
+ struct gprs_llc_lle *lle = data;
+
+ /* 8.5.1.3: Expiry of T200 */
+
+ if (lle->retrans_ctr >= lle->n200) {
+ /* FIXME: LLGM-STATUS-IND, LL-RELEASE-IND/CNF */
+ lle->state = GPRS_LLS_ASSIGNED_ADM;
+ }
+
+ switch (lle->state) {
+ case GPRS_LLS_LOCAL_EST:
+ /* retransmit SABM */
+ /* re-start T200 */
+ lle->retrans_ctr++;
+ break;
+ case GPRS_LLS_LOCAL_REL:
+ /* retransmit DISC */
+ /* re-start T200 */
+ lle->retrans_ctr++;
+ break;
+ }
+
+}
+
+static void t201_expired(void *data)
+{
+ struct gprs_llc_lle *lle = data;
+
+ if (lle->retrans_ctr < lle->n200) {
+ /* transmit apropriate supervisory frame (8.6.4.1) */
+ /* set timer T201 */
+ lle->retrans_ctr++;
+ }
+}
+
+int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command,
+ enum gprs_llc_u_cmd u_cmd, int pf_bit)
+{
+ uint8_t *fcs, *llch;
+ uint8_t addr, ctrl;
+ uint32_t fcs_calc;
+
+ /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */
+
+ /* Address Field */
+ addr = sapi & 0xf;
+ if (command)
+ addr |= 0x40;
+
+ /* 6.3 Figure 8 */
+ ctrl = 0xe0 | u_cmd;
+ if (pf_bit)
+ ctrl |= 0x10;
+
+ /* prepend LLC UI header */
+ llch = msgb_push(msg, 2);
+ llch[0] = addr;
+ llch[1] = ctrl;
+
+ /* append FCS to end of frame */
+ fcs = msgb_put(msg, 3);
+ fcs_calc = gprs_llc_fcs(llch, fcs - llch);
+ fcs[0] = fcs_calc & 0xff;
+ fcs[1] = (fcs_calc >> 8) & 0xff;
+ fcs[2] = (fcs_calc >> 16) & 0xff;
+
+ /* Identifiers passed down: (BVCI, NSEI) */
+
+ return gprs_bssgp_tx_dl_ud(msg);
+}
+
+/* Send XID response to LLE */
+static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg)
+{
+ /* copy identifiers from LLE to ensure lower layers can route */
+ msgb_tlli(msg) = lle->tlli;
+ msgb_bvci(msg) = lle->bvci;
+ msgb_nsei(msg) = lle->nsei;
+
+ return gprs_llc_tx_u(msg, lle->sapi, 0, GPRS_LLC_U_XID, 1);
+}
+
+/* Transmit a UI frame over the given SAPI */
+int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command)
+{
+ struct gprs_llc_lle *lle;
+ uint8_t *fcs, *llch;
+ uint8_t addr, ctrl[2];
+ uint32_t fcs_calc;
+ uint16_t nu = 0;
+
+ /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */
+
+ /* look-up or create the LL Entity for this (TLLI, SAPI) tuple */
+ lle = lle_by_tlli_sapi(msgb_tlli(msg), sapi);
+ if (!lle)
+ lle = lle_alloc(msgb_tlli(msg), sapi);
+ /* Update LLE's (BVCI, NSEI) tuple */
+ lle->bvci = msgb_bvci(msg);
+ lle->nsei = msgb_nsei(msg);
+
+ /* Address Field */
+ addr = sapi & 0xf;
+ if (command)
+ addr |= 0x40;
+
+ /* Control Field */
+ ctrl[0] = 0xc0;
+ ctrl[0] |= nu >> 6;
+ ctrl[1] = (nu << 2) & 0xfc;
+ ctrl[1] |= 0x01; /* Protected Mode */
+
+ /* prepend LLC UI header */
+ llch = msgb_push(msg, 3);
+ llch[0] = addr;
+ llch[1] = ctrl[0];
+ llch[2] = ctrl[1];
+
+ /* append FCS to end of frame */
+ fcs = msgb_put(msg, 3);
+ fcs_calc = gprs_llc_fcs(llch, fcs - llch);
+ fcs[0] = fcs_calc & 0xff;
+ fcs[1] = (fcs_calc >> 8) & 0xff;
+ fcs[2] = (fcs_calc >> 16) & 0xff;
+
+ /* Identifiers passed down: (BVCI, NSEI) */
+
+ return gprs_bssgp_tx_dl_ud(msg);
+}
+
+static int gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph)
+{
+ DEBUGP(DGPRS, "LLC SAPI=%u %c %c FCS=0x%06x(%s) ",
+ gph->sapi, gph->is_cmd ? 'C' : 'R', gph->ack_req ? 'A' : ' ',
+ gph->fcs, gph->fcs_calc == gph->fcs ? "correct" : "WRONG");
+
+ if (gph->cmd)
+ DEBUGPC(DGPRS, "CMD=%u ", gph->cmd);
+
+ if (gph->data)
+ DEBUGPC(DGPRS, "DATA ");
+
+ DEBUGPC(DGPRS, "\n");
+}
+static int gprs_llc_hdr_rx(struct gprs_llc_hdr_parsed *gph,
+ struct gprs_llc_lle *lle)
+{
+ switch (gph->cmd) {
+ case GPRS_LLC_SABM: /* Section 6.4.1.1 */
+ lle->v_sent = lle->v_ack = lle->v_recv = 0;
+ if (lle->state == GPRS_LLS_ASSIGNED_ADM) {
+ /* start re-establishment (8.7.1) */
+ }
+ lle->state = GPRS_LLS_REMOTE_EST;
+ /* FIXME: Send UA */
+ lle->state = GPRS_LLS_ABM;
+ /* FIXME: process data */
+ break;
+ case GPRS_LLC_DISC: /* Section 6.4.1.2 */
+ /* FIXME: Send UA */
+ /* terminate ABM */
+ lle->state = GPRS_LLS_ASSIGNED_ADM;
+ break;
+ case GPRS_LLC_UA: /* Section 6.4.1.3 */
+ if (lle->state == GPRS_LLS_LOCAL_EST)
+ lle->state = GPRS_LLS_ABM;
+ break;
+ case GPRS_LLC_DM: /* Section 6.4.1.4: ABM cannot be performed */
+ if (lle->state == GPRS_LLS_LOCAL_EST)
+ lle->state = GPRS_LLS_ASSIGNED_ADM;
+ break;
+ case GPRS_LLC_FRMR: /* Section 6.4.1.5 */
+ break;
+ case GPRS_LLC_XID: /* Section 6.4.1.6 */
+ /* FIXME: implement XID negotiation using SNDCP */
+ {
+ struct msgb *resp;
+ uint8_t *xid;
+ resp = msgb_alloc_headroom(4096, 1024, "LLC_XID");
+ xid = msgb_put(resp, gph->data_len);
+ memcpy(xid, gph->data, gph->data_len);
+ gprs_llc_tx_xid(lle, resp);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/* parse a GPRS LLC header, also check for invalid frames */
+static int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp,
+ const uint8_t *llc_hdr, int len)
+{
+ uint8_t *ctrl = llc_hdr+1;
+ int is_sack = 0;
+ unsigned int crc_length;
+ uint32_t fcs_calc;
+
+ if (len <= CRC24_LENGTH)
+ return -EIO;
+
+ crc_length = len - CRC24_LENGTH;
+
+ ghp->ack_req = 0;
+
+ /* Section 5.5: FCS */
+ ghp->fcs = *(llc_hdr + len - 3);
+ ghp->fcs |= *(llc_hdr + len - 2) << 8;
+ ghp->fcs |= *(llc_hdr + len - 1) << 16;
+
+ /* Section 6.2.1: invalid PD field */
+ if (llc_hdr[0] & 0x80)
+ return -EIO;
+
+ /* This only works for the MS->SGSN direction */
+ if (llc_hdr[0] & 0x40)
+ ghp->is_cmd = 0;
+ else
+ ghp->is_cmd = 1;
+
+ ghp->sapi = llc_hdr[0] & 0xf;
+
+ /* Section 6.2.3: check for reserved SAPI */
+ switch (ghp->sapi) {
+ case 0:
+ case 4:
+ case 6:
+ case 0xa:
+ case 0xc:
+ case 0xd:
+ case 0xf:
+ return -EINVAL;
+ }
+
+ if ((ctrl[0] & 0x80) == 0) {
+ /* I (Information transfer + Supervisory) format */
+ uint8_t k;
+
+ ghp->data = ctrl + 3;
+
+ if (ctrl[0] & 0x40)
+ ghp->ack_req = 1;
+
+ ghp->seq_tx = (ctrl[0] & 0x1f) << 4;
+ ghp->seq_tx |= (ctrl[1] >> 4);
+
+ ghp->seq_rx = (ctrl[1] & 0x7) << 6;
+ ghp->seq_rx |= (ctrl[2] >> 2);
+
+ switch (ctrl[2] & 0x03) {
+ case 0:
+ ghp->cmd = GPRS_LLC_RR;
+ break;
+ case 1:
+ ghp->cmd = GPRS_LLC_ACK;
+ break;
+ case 2:
+ ghp->cmd = GPRS_LLC_RNR;
+ break;
+ case 3:
+ ghp->cmd = GPRS_LLC_SACK;
+ k = ctrl[3] & 0x1f;
+ ghp->data += 1 + k;
+ break;
+ }
+ ghp->data_len = (llc_hdr + len - 3) - ghp->data;
+ } else if ((ctrl[0] & 0xc0) == 0x80) {
+ /* S (Supervisory) format */
+ ghp->data = NULL;
+ ghp->data_len = 0;
+
+ if (ctrl[0] & 0x20)
+ ghp->ack_req = 1;
+ ghp->seq_rx = (ctrl[0] & 0x7) << 6;
+ ghp->seq_rx |= (ctrl[1] >> 2);
+
+ switch (ctrl[1] & 0x03) {
+ case 0:
+ ghp->cmd = GPRS_LLC_RR;
+ break;
+ case 1:
+ ghp->cmd = GPRS_LLC_ACK;
+ break;
+ case 2:
+ ghp->cmd = GPRS_LLC_RNR;
+ break;
+ case 3:
+ ghp->cmd = GPRS_LLC_SACK;
+ break;
+ }
+ } else if ((ctrl[0] & 0xe0) == 0xc0) {
+ /* UI (Unconfirmed Inforamtion) format */
+ ghp->data = ctrl + 2;
+ ghp->data_len = (llc_hdr + len - 3) - ghp->data;
+
+ ghp->seq_tx = (ctrl[0] & 0x7) << 6;
+ ghp->seq_tx |= (ctrl[1] >> 2);
+ if (ctrl[1] & 0x02) {
+ ghp->is_encrypted = 1;
+ /* FIXME: encryption */
+ }
+ if (ctrl[1] & 0x01) {
+ /* FCS over hdr + all inf fields */
+ } else {
+ /* FCS over hdr + N202 octets (4) */
+ if (crc_length > UI_HDR_LEN + N202)
+ crc_length = UI_HDR_LEN + N202;
+ }
+ } else {
+ /* U (Unnumbered) format: 1 1 1 P/F M4 M3 M2 M1 */
+ ghp->data = NULL;
+ ghp->data_len = 0;
+
+ switch (ctrl[0] & 0xf) {
+ case GPRS_LLC_U_NULL_CMD:
+ ghp->cmd = GPRS_LLC_NULL;
+ break;
+ case GPRS_LLC_U_DM_RESP:
+ ghp->cmd = GPRS_LLC_DM;
+ break;
+ case GPRS_LLC_U_DISC_CMD:
+ ghp->cmd = GPRS_LLC_DISC;
+ break;
+ case GPRS_LLC_U_UA_RESP:
+ ghp->cmd = GPRS_LLC_UA;
+ break;
+ case GPRS_LLC_U_SABM_CMD:
+ ghp->cmd = GPRS_LLC_SABM;
+ break;
+ case GPRS_LLC_U_FRMR_RESP:
+ ghp->cmd = GPRS_LLC_FRMR;
+ break;
+ case GPRS_LLC_U_XID:
+ ghp->cmd = GPRS_LLC_XID;
+ ghp->data = ctrl + 1;
+ ghp->data_len = (llc_hdr + len - 3) - ghp->data;
+ break;
+ default:
+ return -EIO;
+ }
+ }
+
+ /* calculate what FCS we expect */
+ ghp->fcs_calc = gprs_llc_fcs(llc_hdr, crc_length);
+
+ /* FIXME: parse sack frame */
+}
+
+/* receive an incoming LLC PDU (BSSGP-UL-UNITDATA-IND, 7.2.4.2) */
+int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv)
+{
+ struct bssgp_ud_hdr *udh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
+ struct gprs_llc_hdr *lh = msgb_llch(msg);
+ struct gprs_llc_hdr_parsed llhp;
+ struct gprs_llc_lle *lle;
+ int rc = 0;
+
+ /* Identifiers from DOWN: NSEI, BVCI, TLLI */
+
+ rc = gprs_llc_hdr_parse(&llhp, lh, TLVP_LEN(tv, BSSGP_IE_LLC_PDU));
+ /* FIXME */
+
+ gprs_llc_hdr_dump(&llhp);
+
+ /* find the LLC Entity for this TLLI+SAPI tuple */
+ lle = lle_by_tlli_sapi(msgb_tlli(msg), llhp.sapi);
+ /* allocate a new LLE if needed */
+ if (!lle)
+ lle = lle_alloc(msgb_tlli(msg), llhp.sapi);
+
+ /* Update LLE's (BVCI, NSEI) tuple */
+ lle->bvci = msgb_bvci(msg);
+ lle->nsei = msgb_nsei(msg);
+
+ rc = gprs_llc_hdr_rx(&llhp, lle);
+ /* FIXME */
+
+ if (llhp.data) {
+ msgb_gmmh(msg) = llhp.data;
+ switch (llhp.sapi) {
+ case GPRS_SAPI_GMM:
+ rc = gsm0408_gprs_rcvmsg(msg);
+ break;
+ case GPRS_SAPI_TOM2:
+ case GPRS_SAPI_TOM8:
+ /* FIXME */
+ case GPRS_SAPI_SNDCP3:
+ case GPRS_SAPI_SNDCP5:
+ case GPRS_SAPI_SNDCP9:
+ case GPRS_SAPI_SNDCP11:
+ /* FIXME */
+ case GPRS_SAPI_SMS:
+ /* FIXME */
+ default:
+ LOGP(DGPRS, LOGL_NOTICE, "Unsupported SAPI %u\n", llhp.sapi);
+ rc = -EINVAL;
+ break;
+ }
+ }
+
+ return rc;
+}
diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c
new file mode 100644
index 000000000..dc12953e0
--- /dev/null
+++ b/openbsc/src/gprs/gprs_ns.c
@@ -0,0 +1,926 @@
+/* GPRS Networks Service (NS) messages on the Gb interfacebvci = msgb_bvci(msg);
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/* Some introduction into NS: NS is used typically on top of frame relay,
+ * but in the ip.access world it is encapsulated in UDP packets. It serves
+ * as an intermediate shim betwen BSSGP and the underlying medium. It doesn't
+ * do much, apart from providing congestion notification and status indication.
+ *
+ * Terms:
+ * NS Network Service
+ * NSVC NS Virtual Connection
+ * NSEI NS Entity Identifier
+ * NSVL NS Virtual Link
+ * NSVLI NS Virtual Link Identifier
+ * BVC BSSGP Virtual Connection
+ * BVCI BSSGP Virtual Connection Identifier
+ * NSVCG NS Virtual Connection Goup
+ * Blocked NS-VC cannot be used for user traffic
+ * Alive Ability of a NS-VC to provide communication
+ *
+ * There can be multiple BSSGP virtual connections over one (group of) NSVC's. BSSGP will
+ * therefore identify the BSSGP virtual connection by a BVCI passed down to NS.
+ * NS then has to firgure out which NSVC's are responsible for this BVCI.
+ * Those mappings are administratively configured.
+ */
+
+/* This implementation has the following limitations:
+ * o Only one NS-VC for each NSE: No load-sharing function
+ * o NSVCI 65535 and 65534 are reserved for internal use
+ * o Only UDP is supported as of now, no frame relay support
+ * o The IP Sub-Network-Service (SNS) as specified in 48.016 is not implemented
+ * o There are no BLOCK and UNBLOCK timers (yet?)
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <arpa/inet.h>
+
+#include <openbsc/gsm_data.h>
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/talloc.h>
+#include <osmocore/select.h>
+#include <osmocore/rate_ctr.h>
+#include <openbsc/debug.h>
+#include <openbsc/signal.h>
+#include <openbsc/gprs_ns.h>
+#include <openbsc/gprs_bssgp.h>
+
+#define NS_ALLOC_SIZE 1024
+
+static const struct tlv_definition ns_att_tlvdef = {
+ .def = {
+ [NS_IE_CAUSE] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_VCI] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_PDU] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_BVCI] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_NSEI] = { TLV_TYPE_TvLV, 0 },
+ },
+};
+
+enum ns_ctr {
+ NS_CTR_PKTS_IN,
+ NS_CTR_PKTS_OUT,
+ NS_CTR_BYTES_IN,
+ NS_CTR_BYTES_OUT,
+ NS_CTR_BLOCKED,
+ NS_CTR_DEAD,
+};
+
+static const struct rate_ctr_desc nsvc_ctr_description[] = {
+ { "packets.in", "Packets at NS Level ( In)" },
+ { "packets.out","Packets at NS Level (Out)" },
+ { "bytes.in", "Bytes at NS Level ( In)" },
+ { "bytes.out", "Bytes at NS Level (Out)" },
+ { "blocked", "NS-VC Block count " },
+ { "dead", "NS-VC gone dead count " },
+};
+
+static const struct rate_ctr_group_desc nsvc_ctrg_desc = {
+ .group_name_prefix = "ns.nsvc",
+ .group_description = "NSVC Peer Statistics",
+ .num_ctr = ARRAY_SIZE(nsvc_ctr_description),
+ .ctr_desc = nsvc_ctr_description,
+};
+
+/* Lookup struct gprs_nsvc based on NSVCI */
+static struct gprs_nsvc *nsvc_by_nsvci(struct gprs_ns_inst *nsi,
+ uint16_t nsvci)
+{
+ struct gprs_nsvc *nsvc;
+ llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
+ if (nsvc->nsvci == nsvci)
+ return nsvc;
+ }
+ return NULL;
+}
+
+/* Lookup struct gprs_nsvc based on NSVCI */
+struct gprs_nsvc *nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei)
+{
+ struct gprs_nsvc *nsvc;
+ llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
+ if (nsvc->nsei == nsei)
+ return nsvc;
+ }
+ return NULL;
+}
+
+/* Lookup struct gprs_nsvc based on remote peer socket addr */
+static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi,
+ struct sockaddr_in *sin)
+{
+ struct gprs_nsvc *nsvc;
+ llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
+ if (nsvc->ip.bts_addr.sin_addr.s_addr ==
+ sin->sin_addr.s_addr &&
+ nsvc->ip.bts_addr.sin_port == sin->sin_port)
+ return nsvc;
+ }
+ return NULL;
+}
+
+static void gprs_ns_timer_cb(void *data);
+
+struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci)
+{
+ struct gprs_nsvc *nsvc;
+
+ nsvc = talloc_zero(nsi, struct gprs_nsvc);
+ nsvc->nsvci = nsvci;
+ /* before RESET procedure: BLOCKED and DEAD */
+ nsvc->state = NSE_S_BLOCKED;
+ nsvc->nsi = nsi;
+ nsvc->timer.cb = gprs_ns_timer_cb;
+ nsvc->timer.data = nsvc;
+ nsvc->ctrg = rate_ctr_group_alloc(nsvc, &nsvc_ctrg_desc, nsvci);
+
+ llist_add(&nsvc->list, &nsi->gprs_nsvcs);
+
+ return nsvc;
+}
+
+void nsvc_delete(struct gprs_nsvc *nsvc)
+{
+ if (bsc_timer_pending(&nsvc->timer))
+ bsc_del_timer(&nsvc->timer);
+ llist_del(&nsvc->list);
+ talloc_free(nsvc);
+}
+
+static void ns_dispatch_signal(struct gprs_nsvc *nsvc, unsigned int signal,
+ uint8_t cause)
+{
+ struct ns_signal_data nssd;
+
+ nssd.nsvc = nsvc;
+ nssd.cause = cause;
+
+ dispatch_signal(SS_NS, signal, &nssd);
+}
+
+/* Section 10.3.2, Table 13 */
+static const struct value_string ns_cause_str[] = {
+ { NS_CAUSE_TRANSIT_FAIL, "Transit network failure" },
+ { NS_CAUSE_OM_INTERVENTION, "O&M intervention" },
+ { NS_CAUSE_EQUIP_FAIL, "Equipment failure" },
+ { NS_CAUSE_NSVC_BLOCKED, "NS-VC blocked" },
+ { NS_CAUSE_NSVC_UNKNOWN, "NS-VC unknown" },
+ { NS_CAUSE_BVCI_UNKNOWN, "BVCI unknown" },
+ { NS_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" },
+ { NS_CAUSE_PDU_INCOMP_PSTATE, "PDU not compatible with protocol state" },
+ { NS_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" },
+ { NS_CAUSE_INVAL_ESSENT_IE, "Invalid essential IE" },
+ { NS_CAUSE_MISSING_ESSENT_IE, "Missing essential IE" },
+ { 0, NULL }
+};
+
+const char *gprs_ns_cause_str(enum ns_cause cause)
+{
+ return get_value_string(ns_cause_str, cause);
+}
+
+static int nsip_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg);
+
+static int gprs_ns_tx(struct gprs_nsvc *nsvc, struct msgb *msg)
+{
+ int ret;
+
+ /* Increment number of Uplink bytes */
+ rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_OUT]);
+ rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_OUT], msgb_l2len(msg));
+
+ switch (nsvc->nsi->ll) {
+ case GPRS_NS_LL_UDP:
+ ret = nsip_sendmsg(nsvc, msg);
+ break;
+ default:
+ LOGP(DNS, LOGL_ERROR, "unsupported NS linklayer %u\n", nsvc->nsi->ll);
+ msgb_free(msg);
+ ret = -EIO;
+ break;
+ }
+ return ret;
+}
+
+static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, uint8_t pdu_type)
+{
+ struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS");
+ struct gprs_ns_hdr *nsh;
+
+ if (!msg)
+ return -ENOMEM;
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+
+ nsh->pdu_type = pdu_type;
+
+ return gprs_ns_tx(nsvc, msg);
+}
+
+int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause)
+{
+ struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS");
+ struct gprs_ns_hdr *nsh;
+ uint16_t nsvci = htons(nsvc->nsvci);
+ uint16_t nsei = htons(nsvc->nsei);
+
+ if (!msg)
+ return -ENOMEM;
+
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS RESET (NSVCI=%u, cause=%s)\n",
+ nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause));
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = NS_PDUT_RESET;
+
+ msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
+ msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei);
+
+ return gprs_ns_tx(nsvc, msg);
+
+}
+
+int gprs_ns_tx_status(struct gprs_nsvc *nsvc, uint8_t cause,
+ uint16_t bvci, struct msgb *orig_msg)
+{
+ struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS");
+ struct gprs_ns_hdr *nsh;
+ uint16_t nsvci = htons(nsvc->nsvci);
+
+ bvci = htons(bvci);
+
+ if (!msg)
+ return -ENOMEM;
+
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS STATUS (NSVCI=%u, cause=%s)\n",
+ nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause));
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = NS_PDUT_STATUS;
+
+ msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
+
+ /* Section 9.2.7.1: Static conditions for NS-VCI */
+ if (cause == NS_CAUSE_NSVC_BLOCKED ||
+ cause == NS_CAUSE_NSVC_UNKNOWN)
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
+
+ /* Section 9.2.7.2: Static conditions for NS PDU */
+ switch (cause) {
+ case NS_CAUSE_SEM_INCORR_PDU:
+ case NS_CAUSE_PDU_INCOMP_PSTATE:
+ case NS_CAUSE_PROTO_ERR_UNSPEC:
+ case NS_CAUSE_INVAL_ESSENT_IE:
+ case NS_CAUSE_MISSING_ESSENT_IE:
+ msgb_tvlv_put(msg, NS_IE_PDU, msgb_l2len(orig_msg),
+ orig_msg->l2h);
+ break;
+ default:
+ break;
+ }
+
+ /* Section 9.2.7.3: Static conditions for BVCI */
+ if (cause == NS_CAUSE_BVCI_UNKNOWN)
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&bvci);
+
+ return gprs_ns_tx(nsvc, msg);
+}
+
+int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause)
+{
+ struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS");
+ struct gprs_ns_hdr *nsh;
+ uint16_t nsvci = htons(nsvc->nsvci);
+
+ if (!msg)
+ return -ENOMEM;
+
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS BLOCK (NSVCI=%u, cause=%s)\n",
+ nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause));
+
+ /* be conservative and mark it as blocked even now! */
+ nsvc->state |= NSE_S_BLOCKED;
+ rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = NS_PDUT_BLOCK;
+
+ msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
+
+ return gprs_ns_tx(nsvc, msg);
+}
+
+int gprs_ns_tx_unblock(struct gprs_nsvc *nsvc)
+{
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n",
+ nsvc->nsei, nsvc->nsvci);
+
+ return gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK);
+}
+
+int gprs_ns_tx_alive(struct gprs_nsvc *nsvc)
+{
+ LOGP(DNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE (NSVCI=%u)\n",
+ nsvc->nsei, nsvc->nsvci);
+
+ return gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE);
+}
+
+int gprs_ns_tx_alive_ack(struct gprs_nsvc *nsvc)
+{
+ LOGP(DNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE_ACK (NSVCI=%u)\n",
+ nsvc->nsei, nsvc->nsvci);
+
+ return gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE_ACK);
+}
+
+static const enum ns_timeout timer_mode_tout[_NSVC_TIMER_NR] = {
+ [NSVC_TIMER_TNS_RESET] = NS_TOUT_TNS_RESET,
+ [NSVC_TIMER_TNS_ALIVE] = NS_TOUT_TNS_ALIVE,
+ [NSVC_TIMER_TNS_TEST] = NS_TOUT_TNS_TEST,
+};
+
+static const struct value_string timer_mode_strs[] = {
+ { NSVC_TIMER_TNS_RESET, "tns-reset" },
+ { NSVC_TIMER_TNS_ALIVE, "tns-alive" },
+ { NSVC_TIMER_TNS_TEST, "tns-test" },
+ { 0, NULL }
+};
+
+static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode)
+{
+ enum ns_timeout tout = timer_mode_tout[mode];
+ unsigned int seconds = nsvc->nsi->timeout[tout];
+
+ DEBUGP(DNS, "NSEI=%u Starting timer in mode %s (%u seconds)\n",
+ nsvc->nsei, get_value_string(timer_mode_strs, mode),
+ seconds);
+
+ if (bsc_timer_pending(&nsvc->timer))
+ bsc_del_timer(&nsvc->timer);
+
+ nsvc->timer_mode = mode;
+ bsc_schedule_timer(&nsvc->timer, seconds, 0);
+}
+
+static void gprs_ns_timer_cb(void *data)
+{
+ struct gprs_nsvc *nsvc = data;
+ enum ns_timeout tout = timer_mode_tout[nsvc->timer_mode];
+ unsigned int seconds = nsvc->nsi->timeout[tout];
+
+ DEBUGP(DNS, "NSEI=%u Timer expired in mode %s (%u seconds)\n",
+ nsvc->nsei, get_value_string(timer_mode_strs, nsvc->timer_mode),
+ seconds);
+
+ switch (nsvc->timer_mode) {
+ case NSVC_TIMER_TNS_ALIVE:
+ /* Tns-alive case: we expired without response ! */
+ nsvc->alive_retries++;
+ if (nsvc->alive_retries >
+ nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) {
+ /* mark as dead and blocked */
+ nsvc->state = NSE_S_BLOCKED;
+ rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+ rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_DEAD]);
+ LOGP(DNS, LOGL_NOTICE,
+ "NSEI=%u Tns-alive expired more then "
+ "%u times, blocking NS-VC\n", nsvc->nsei,
+ nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]);
+ ns_dispatch_signal(nsvc, S_NS_ALIVE_EXP, 0);
+ ns_dispatch_signal(nsvc, S_NS_BLOCK, NS_CAUSE_NSVC_BLOCKED);
+ return;
+ }
+ /* Tns-test case: send NS-ALIVE PDU */
+ gprs_ns_tx_alive(nsvc);
+ /* start Tns-alive timer */
+ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE);
+ break;
+ case NSVC_TIMER_TNS_TEST:
+ /* Tns-test case: send NS-ALIVE PDU */
+ gprs_ns_tx_alive(nsvc);
+ /* start Tns-alive timer (transition into faster
+ * alive retransmissions) */
+ nsvc->alive_retries = 0;
+ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE);
+ break;
+ case NSVC_TIMER_TNS_RESET:
+ /* Chapter 7.3: Re-send the RESET */
+ gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
+ /* Re-start Tns-reset timer */
+ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET);
+ break;
+ case _NSVC_TIMER_NR:
+ break;
+ }
+}
+
+/* Section 9.2.6 */
+static int gprs_ns_tx_reset_ack(struct gprs_nsvc *nsvc)
+{
+ struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "GPRS/NS");
+ struct gprs_ns_hdr *nsh;
+ uint16_t nsvci, nsei;
+
+ if (!msg)
+ return -ENOMEM;
+
+ nsvci = htons(nsvc->nsvci);
+ nsei = htons(nsvc->nsei);
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+
+ nsh->pdu_type = NS_PDUT_RESET_ACK;
+
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n",
+ nsvc->nsei, nsvc->nsvci);
+
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
+ msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
+
+ return gprs_ns_tx(nsvc, msg);
+}
+
+/* Section 9.2.10: transmit side / NS-UNITDATA-REQUEST primitive */
+int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg)
+{
+ struct gprs_nsvc *nsvc;
+ struct gprs_ns_hdr *nsh;
+ uint16_t bvci = msgb_bvci(msg);
+
+ nsvc = nsvc_by_nsei(nsi, msgb_nsei(msg));
+ if (!nsvc) {
+ LOGP(DNS, LOGL_ERROR, "Unable to resolve NSEI %u "
+ "to NS-VC!\n", msgb_nsei(msg));
+ return -EINVAL;
+ }
+
+ if (!(nsvc->state & NSE_S_ALIVE)) {
+ LOGP(DNS, LOGL_ERROR, "NSEI=%u is not alive, cannot send\n",
+ nsvc->nsei);
+ return -EBUSY;
+ }
+ if (nsvc->state & NSE_S_BLOCKED) {
+ LOGP(DNS, LOGL_ERROR, "NSEI=%u is blocked, cannot send\n",
+ nsvc->nsei);
+ return -EBUSY;
+ }
+
+ msg->l2h = msgb_push(msg, sizeof(*nsh) + 3);
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ if (!nsh) {
+ LOGP(DNS, LOGL_ERROR, "Not enough headroom for NS header\n");
+ return -EIO;
+ }
+
+ nsh->pdu_type = NS_PDUT_UNITDATA;
+ /* spare octet in data[0] */
+ nsh->data[1] = bvci >> 8;
+ nsh->data[2] = bvci & 0xff;
+
+ return gprs_ns_tx(nsvc, msg);
+}
+
+/* Section 9.2.10: receive side */
+static int gprs_ns_rx_unitdata(struct gprs_nsvc *nsvc, struct msgb *msg)
+{
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h;
+ uint16_t bvci;
+
+ if (nsvc->state & NSE_S_BLOCKED)
+ return gprs_ns_tx_status(nsvc, NS_CAUSE_NSVC_BLOCKED,
+ 0, msg);
+
+ /* spare octet in data[0] */
+ bvci = nsh->data[1] << 8 | nsh->data[2];
+ msgb_bssgph(msg) = &nsh->data[3];
+ msgb_bvci(msg) = bvci;
+
+ /* call upper layer (BSSGP) */
+ return nsvc->nsi->cb(GPRS_NS_EVT_UNIT_DATA, nsvc, msg, bvci);
+}
+
+/* Section 9.2.7 */
+static int gprs_ns_rx_status(struct gprs_nsvc *nsvc, struct msgb *msg)
+{
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
+ struct tlv_parsed tp;
+ uint8_t cause;
+ int rc;
+
+ LOGP(DNS, LOGL_INFO, "NSEI=%u NS STATUS ", nsvc->nsei);
+
+ rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0);
+
+ if (!TLVP_PRESENT(&tp, NS_IE_CAUSE)) {
+ LOGPC(DNS, LOGL_INFO, "missing cause IE\n");
+ return -EINVAL;
+ }
+
+ cause = *TLVP_VAL(&tp, NS_IE_CAUSE);
+ LOGPC(DNS, LOGL_INFO, "cause=%s\n", gprs_ns_cause_str(cause));
+
+ return 0;
+}
+
+/* Section 7.3 */
+static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg)
+{
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
+ struct tlv_parsed tp;
+ uint8_t *cause;
+ uint16_t *nsvci, *nsei;
+ int rc;
+
+ rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0);
+
+ if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) ||
+ !TLVP_PRESENT(&tp, NS_IE_VCI) ||
+ !TLVP_PRESENT(&tp, NS_IE_NSEI)) {
+ LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n");
+ gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg);
+ return -EINVAL;
+ }
+
+ cause = (uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE);
+ nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI);
+ nsei = (uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI);
+
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET (NSVCI=%u, cause=%s)\n",
+ nsvc->nsvci, nsvc->nsei, gprs_ns_cause_str(*cause));
+
+ /* Mark NS-VC as blocked and alive */
+ nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE;
+
+ nsvc->nsei = ntohs(*nsei);
+ nsvc->nsvci = ntohs(*nsvci);
+
+ /* start the test procedure */
+ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE);
+
+ /* inform interested parties about the fact that this NSVC
+ * has received RESET */
+ ns_dispatch_signal(nsvc, S_NS_RESET, *cause);
+
+ return gprs_ns_tx_reset_ack(nsvc);
+}
+
+static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg)
+{
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
+ struct tlv_parsed tp;
+ uint8_t *cause;
+ int rc;
+
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK\n", nsvc->nsei);
+
+ nsvc->state |= NSE_S_BLOCKED;
+
+ rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg), 0, 0);
+
+ if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) ||
+ !TLVP_PRESENT(&tp, NS_IE_VCI)) {
+ LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n");
+ gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg);
+ return -EINVAL;
+ }
+
+ cause = (uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE);
+ //nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI);
+
+ ns_dispatch_signal(nsvc, S_NS_BLOCK, *cause);
+ rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+
+ return gprs_ns_tx_simple(nsvc, NS_PDUT_BLOCK_ACK);
+}
+
+/* main entry point, here incoming NS frames enter */
+int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
+ struct sockaddr_in *saddr)
+{
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
+ struct gprs_nsvc *nsvc;
+ int rc = 0;
+
+ /* look up the NSVC based on source address */
+ nsvc = nsvc_by_rem_addr(nsi, saddr);
+ if (!nsvc) {
+ struct tlv_parsed tp;
+ uint16_t nsei;
+ /* Only the RESET procedure creates a new NSVC */
+ if (nsh->pdu_type != NS_PDUT_RESET) {
+ /* Since we have no NSVC, we have to use a fake */
+ nsvc = nsi->unknown_nsvc;
+ LOGP(DNS, LOGL_INFO, "Rejecting NS PDU type 0x%0x "
+ "from %s:%u for non-existing NS-VC\n",
+ nsh->pdu_type, inet_ntoa(saddr->sin_addr),
+ ntohs(saddr->sin_port));
+ nsvc->nsvci = nsvc->nsei = 0xfffe;
+ nsvc->ip.bts_addr = *saddr;
+ nsvc->state = NSE_S_ALIVE;
+ return gprs_ns_tx_status(nsvc,
+ NS_CAUSE_PDU_INCOMP_PSTATE, 0,
+ msg);
+ }
+ rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data,
+ msgb_l2len(msg), 0, 0);
+ if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) ||
+ !TLVP_PRESENT(&tp, NS_IE_VCI) ||
+ !TLVP_PRESENT(&tp, NS_IE_NSEI)) {
+ LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n");
+ gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0,
+ msg);
+ return -EINVAL;
+ }
+ nsei = ntohs(*(uint16_t *)TLVP_VAL(&tp, NS_IE_NSEI));
+ /* Check if we already know this NSEI, the remote end might
+ * simply have changed addresses, or it is a SGSN */
+ nsvc = nsvc_by_nsei(nsi, nsei);
+ if (!nsvc) {
+ LOGP(DNS, LOGL_INFO, "Creating NS-VC for BSS at %s:%u\n",
+ inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port));
+ nsvc = nsvc_create(nsi, 0xffff);
+ }
+ /* Update the remote peer IP address/port */
+ nsvc->ip.bts_addr = *saddr;
+ } else
+ msgb_nsei(msg) = nsvc->nsei;
+
+ /* Increment number of Incoming bytes */
+ rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_IN]);
+ rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_IN], msgb_l2len(msg));
+
+ switch (nsh->pdu_type) {
+ case NS_PDUT_ALIVE:
+ /* If we're dead and blocked and suddenly receive a
+ * NS-ALIVE out of the blue, we might have been re-started
+ * and should send a NS-RESET to make sure everything recovers
+ * fine. */
+ if (nsvc->state == NSE_S_BLOCKED)
+ rc = gprs_ns_tx_reset(nsvc, NS_CAUSE_PDU_INCOMP_PSTATE);
+ else
+ rc = gprs_ns_tx_alive_ack(nsvc);
+ break;
+ case NS_PDUT_ALIVE_ACK:
+ /* stop Tns-alive */
+ bsc_del_timer(&nsvc->timer);
+ /* start Tns-test */
+ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST);
+ if (nsvc->remote_end_is_sgsn) {
+ /* FIXME: this should be one level higher */
+ if (nsvc->state & NSE_S_BLOCKED)
+ rc = gprs_ns_tx_unblock(nsvc);
+ }
+ break;
+ case NS_PDUT_UNITDATA:
+ /* actual user data */
+ rc = gprs_ns_rx_unitdata(nsvc, msg);
+ break;
+ case NS_PDUT_STATUS:
+ rc = gprs_ns_rx_status(nsvc, msg);
+ break;
+ case NS_PDUT_RESET:
+ rc = gprs_ns_rx_reset(nsvc, msg);
+ break;
+ case NS_PDUT_RESET_ACK:
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET ACK\n", nsvc->nsei);
+ /* mark NS-VC as blocked + active */
+ nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE;
+ nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE;
+ rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+ if (nsvc->remote_end_is_sgsn) {
+ /* stop RESET timer */
+ bsc_del_timer(&nsvc->timer);
+ /* Initiate TEST proc.: Send ALIVE and start timer */
+ rc = gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE);
+ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE);
+ }
+ break;
+ case NS_PDUT_UNBLOCK:
+ /* Section 7.2: unblocking procedure */
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK\n", nsvc->nsei);
+ nsvc->state &= ~NSE_S_BLOCKED;
+ ns_dispatch_signal(nsvc, S_NS_UNBLOCK, 0);
+ rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK);
+ break;
+ case NS_PDUT_UNBLOCK_ACK:
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK ACK\n", nsvc->nsei);
+ /* mark NS-VC as unblocked + active */
+ nsvc->state = NSE_S_ALIVE;
+ nsvc->remote_state = NSE_S_ALIVE;
+ ns_dispatch_signal(nsvc, S_NS_UNBLOCK, 0);
+ break;
+ case NS_PDUT_BLOCK:
+ rc = gprs_ns_rx_block(nsvc, msg);
+ break;
+ case NS_PDUT_BLOCK_ACK:
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK ACK\n", nsvc->nsei);
+ /* mark remote NS-VC as blocked + active */
+ nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE;
+ break;
+ default:
+ LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx Unknown NS PDU type 0x%02x\n",
+ nsvc->nsei, nsh->pdu_type);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb)
+{
+ struct gprs_ns_inst *nsi = talloc_zero(tall_bsc_ctx, struct gprs_ns_inst);
+
+ nsi->cb = cb;
+ INIT_LLIST_HEAD(&nsi->gprs_nsvcs);
+ nsi->timeout[NS_TOUT_TNS_BLOCK] = 3;
+ nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES] = 3;
+ nsi->timeout[NS_TOUT_TNS_RESET] = 3;
+ nsi->timeout[NS_TOUT_TNS_RESET_RETRIES] = 3;
+ nsi->timeout[NS_TOUT_TNS_TEST] = 30;
+ nsi->timeout[NS_TOUT_TNS_ALIVE] = 3;
+ nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES] = 10;
+
+ /* Create the dummy NSVC that we use for sending
+ * messages to non-existant/unknown NS-VC's */
+ nsi->unknown_nsvc = nsvc_create(nsi, 0xfffe);
+ llist_del(&nsi->unknown_nsvc->list);
+
+ return nsi;
+}
+
+void gprs_ns_destroy(struct gprs_ns_inst *nsi)
+{
+ /* FIXME: clear all timers */
+
+ /* recursively free the NSI and all its NSVCs */
+ talloc_free(nsi);
+}
+
+
+/* NS-over-IP code, according to 3GPP TS 48.016 Chapter 6.2
+ * We don't support Size Procedure, Configuration Procedure, ChangeWeight Procedure */
+
+/* Read a single NS-over-IP message */
+static struct msgb *read_nsip_msg(struct bsc_fd *bfd, int *error,
+ struct sockaddr_in *saddr)
+{
+ struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Abis/IP/GPRS-NS");
+ int ret = 0;
+ socklen_t saddr_len = sizeof(*saddr);
+
+ if (!msg) {
+ *error = -ENOMEM;
+ return NULL;
+ }
+
+ ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0,
+ (struct sockaddr *)saddr, &saddr_len);
+ if (ret < 0) {
+ LOGP(DNS, LOGL_ERROR, "recv error %s during NSIP recv\n",
+ strerror(errno));
+ msgb_free(msg);
+ *error = ret;
+ return NULL;
+ } else if (ret == 0) {
+ msgb_free(msg);
+ *error = ret;
+ return NULL;
+ }
+
+ msg->l2h = msg->data;
+ msgb_put(msg, ret);
+
+ return msg;
+}
+
+static int handle_nsip_read(struct bsc_fd *bfd)
+{
+ int error;
+ struct sockaddr_in saddr;
+ struct gprs_ns_inst *nsi = bfd->data;
+ struct msgb *msg = read_nsip_msg(bfd, &error, &saddr);
+
+ if (!msg)
+ return error;
+
+ error = gprs_ns_rcvmsg(nsi, msg, &saddr);
+
+ msgb_free(msg);
+
+ return error;
+}
+
+static int handle_nsip_write(struct bsc_fd *bfd)
+{
+ /* FIXME: actually send the data here instead of nsip_sendmsg() */
+ return -EIO;
+}
+
+int nsip_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg)
+{
+ int rc;
+ struct gprs_ns_inst *nsi = nsvc->nsi;
+ struct sockaddr_in *daddr = &nsvc->ip.bts_addr;
+
+ rc = sendto(nsi->nsip.fd.fd, msg->data, msg->len, 0,
+ (struct sockaddr *)daddr, sizeof(*daddr));
+
+ talloc_free(msg);
+
+ return rc;
+}
+
+/* UDP Port 23000 carries the LLC-in-BSSGP-in-NS protocol stack */
+static int nsip_fd_cb(struct bsc_fd *bfd, unsigned int what)
+{
+ int rc = 0;
+
+ if (what & BSC_FD_READ)
+ rc = handle_nsip_read(bfd);
+ if (what & BSC_FD_WRITE)
+ rc = handle_nsip_write(bfd);
+
+ return rc;
+}
+
+extern int make_sock(struct bsc_fd *bfd, int proto, uint16_t port,
+ int (*cb)(struct bsc_fd *fd, unsigned int what));
+
+/* Listen for incoming GPRS packets */
+int nsip_listen(struct gprs_ns_inst *nsi, uint16_t udp_port)
+{
+ int ret;
+
+ ret = make_sock(&nsi->nsip.fd, IPPROTO_UDP, udp_port, nsip_fd_cb);
+ if (ret < 0)
+ return ret;
+
+ nsi->ll = GPRS_NS_LL_UDP;
+ nsi->nsip.fd.data = nsi;
+
+ return ret;
+}
+
+/* Establish a connection (from the BSS) to the SGSN */
+struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi,
+ struct sockaddr_in *dest, uint16_t nsei,
+ uint16_t nsvci)
+{
+ struct gprs_nsvc *nsvc;
+
+ nsvc = nsvc_by_rem_addr(nsi, dest);
+ if (!nsvc)
+ nsvc = nsvc_create(nsi, nsvci);
+ nsvc->ip.bts_addr = *dest;
+ nsvc->nsei = nsei;
+ nsvc->nsvci = nsvci;
+ nsvc->remote_end_is_sgsn = 1;
+
+ /* Initiate a RESET procedure */
+ /* Mark NS-VC locally as blocked and dead */
+ nsvc->state = NSE_S_BLOCKED;
+ /* Send NS-RESET PDU */
+ if (gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION) < 0) {
+ LOGP(DNS, LOGL_ERROR, "NSEI=%u, error resetting NS-VC\n",
+ nsei);
+ }
+ /* Start Tns-reset */
+ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET);
+
+ return nsvc;
+}
+
+
diff --git a/openbsc/src/gprs/gprs_ns_vty.c b/openbsc/src/gprs/gprs_ns_vty.c
new file mode 100644
index 000000000..8f0628afd
--- /dev/null
+++ b/openbsc/src/gprs/gprs_ns_vty.c
@@ -0,0 +1,292 @@
+/* VTY interface for our GPRS Networks Service (NS) implementation */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <arpa/inet.h>
+
+#include <openbsc/gsm_data.h>
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/talloc.h>
+#include <osmocore/select.h>
+#include <osmocore/rate_ctr.h>
+#include <openbsc/debug.h>
+#include <openbsc/signal.h>
+#include <openbsc/gprs_ns.h>
+#include <openbsc/gprs_bssgp.h>
+
+#include <vty/vty.h>
+#include <vty/command.h>
+
+static struct gprs_ns_inst *vty_nsi = NULL;
+
+/* FIXME: this should go to some common file as it is copied
+ * in vty_interface.c of the BSC */
+static const struct value_string gprs_ns_timer_strs[] = {
+ { 0, "tns-block" },
+ { 1, "tns-block-retries" },
+ { 2, "tns-reset" },
+ { 3, "tns-reset-retries" },
+ { 4, "tns-test" },
+ { 5, "tns-alive" },
+ { 6, "tns-alive-retries" },
+ { 0, NULL }
+};
+
+static struct cmd_node ns_node = {
+ NS_NODE,
+ "%s(ns)#",
+ 1,
+};
+
+static int config_write_ns(struct vty *vty)
+{
+ struct gprs_nsvc *nsvc;
+ unsigned int i;
+
+ vty_out(vty, "ns%s", VTY_NEWLINE);
+
+ llist_for_each_entry(nsvc, &vty_nsi->gprs_nsvcs, list) {
+ if (!nsvc->persistent)
+ continue;
+ vty_out(vty, " nse %u nsvci %u%s",
+ nsvc->nsei, nsvc->nsvci, VTY_NEWLINE);
+ vty_out(vty, " nse %u remote-role %s%s",
+ nsvc->nsei, nsvc->remote_end_is_sgsn ? "sgsn" : "bss",
+ VTY_NEWLINE);
+ if (nsvc->nsi->ll == GPRS_NS_LL_UDP) {
+ vty_out(vty, " nse %u remote-ip %s%s",
+ nsvc->nsei,
+ inet_ntoa(nsvc->ip.bts_addr.sin_addr),
+ VTY_NEWLINE);
+ vty_out(vty, " nse %u remote-port %u%s",
+ nsvc->nsei, ntohs(nsvc->ip.bts_addr.sin_port),
+ VTY_NEWLINE);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(vty_nsi->timeout); i++)
+ vty_out(vty, " timer %s %u%s",
+ get_value_string(gprs_ns_timer_strs, i),
+ vty_nsi->timeout[i], VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ns, cfg_ns_cmd,
+ "ns",
+ "Configure the GPRS Network Service")
+{
+ vty->node = NS_NODE;
+ return CMD_SUCCESS;
+}
+
+static void dump_ns(struct vty *vty, struct gprs_ns_inst *nsi, int stats)
+{
+ struct gprs_nsvc *nsvc;
+
+ llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
+ if (nsvc == nsi->unknown_nsvc)
+ continue;
+ vty_out(vty, "NSEI %5u, NS-VC %5u, Remote: %-4s, %5s %9s",
+ nsvc->nsei, nsvc->nsvci,
+ nsvc->remote_end_is_sgsn ? "SGSN" : "BSS",
+ nsvc->state & NSE_S_ALIVE ? "ALIVE" : "DEAD",
+ nsvc->state & NSE_S_BLOCKED ? "BLOCKED" : "UNBLOCKED");
+ if (nsvc->nsi->ll == GPRS_NS_LL_UDP)
+ vty_out(vty, ", %15s:%u",
+ inet_ntoa(nsvc->ip.bts_addr.sin_addr),
+ ntohs(nsvc->ip.bts_addr.sin_port));
+ vty_out(vty, "%s", VTY_NEWLINE);
+ if (stats)
+ vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
+ }
+}
+
+DEFUN(show_ns, show_ns_cmd, "show ns",
+ SHOW_STR "Display information about the NS protocol")
+{
+ struct gprs_ns_inst *nsi = vty_nsi;
+ dump_ns(vty, nsi, 0);
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_ns_stats, show_ns_stats_cmd, "show ns stats",
+ SHOW_STR
+ "Display information about the NS protocol\n"
+ "Include statistics\n")
+{
+ struct gprs_ns_inst *nsi = vty_nsi;
+ dump_ns(vty, nsi, 1);
+ return CMD_SUCCESS;
+}
+
+#define NSE_CMD_STR "NS Entity\n" "NS Entity ID (NSEI)\n"
+
+DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd,
+ "nse <0-65535> nsvci <0-65534>",
+ NSE_CMD_STR
+ "NS Virtual Connection\n"
+ "NS Virtual Connection ID (NSVCI)\n"
+ )
+{
+ uint16_t nsei = atoi(argv[0]);
+ uint16_t nsvci = atoi(argv[1]);
+ struct gprs_nsvc *nsvc;
+
+ nsvc = nsvc_by_nsei(vty_nsi, nsei);
+ if (!nsvc) {
+ nsvc = nsvc_create(vty_nsi, nsvci);
+ nsvc->nsei = nsei;
+ }
+ nsvc->nsvci = nsvci;
+ /* All NSVCs that are explicitly configured by VTY are
+ * marked as persistent so we can write them to the config
+ * file at some later point */
+ nsvc->persistent = 1;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nse_remoteip, cfg_nse_remoteip_cmd,
+ "nse <0-65535> remote-ip A.B.C.D",
+ NSE_CMD_STR
+ "Remote IP Address\n"
+ "Remote IP Address\n")
+{
+ uint16_t nsei = atoi(argv[0]);
+ struct gprs_nsvc *nsvc;
+
+ nsvc = nsvc_by_nsei(vty_nsi, nsei);
+ if (!nsvc) {
+ vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ inet_aton(argv[1], &nsvc->ip.bts_addr.sin_addr);
+
+ return CMD_SUCCESS;
+
+}
+
+DEFUN(cfg_nse_remoteport, cfg_nse_remoteport_cmd,
+ "nse <0-65535> remote-port <0-65535>",
+ NSE_CMD_STR
+ "Remote UDP Port\n"
+ "Remote UDP Port Number\n")
+{
+ uint16_t nsei = atoi(argv[0]);
+ uint16_t port = atoi(argv[1]);
+ struct gprs_nsvc *nsvc;
+
+ nsvc = nsvc_by_nsei(vty_nsi, nsei);
+ if (!nsvc) {
+ vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ nsvc->ip.bts_addr.sin_port = htons(port);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nse_remoterole, cfg_nse_remoterole_cmd,
+ "nse <0-65535> remote-role (sgsn|bss)",
+ NSE_CMD_STR
+ "Remote NSE Role\n"
+ "Remote Peer is SGSN\n"
+ "Remote Peer is BSS\n")
+{
+ uint16_t nsei = atoi(argv[0]);
+ struct gprs_nsvc *nsvc;
+
+ nsvc = nsvc_by_nsei(vty_nsi, nsei);
+ if (!nsvc) {
+ vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[1], "sgsn"))
+ nsvc->remote_end_is_sgsn = 1;
+ else
+ nsvc->remote_end_is_sgsn = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_nse, cfg_no_nse_cmd,
+ "no nse <0-65535>",
+ "Delete NS Entity\n"
+ "Delete " NSE_CMD_STR)
+{
+ uint16_t nsei = atoi(argv[0]);
+ struct gprs_nsvc *nsvc;
+
+ nsvc = nsvc_by_nsei(vty_nsi, nsei);
+ if (!nsvc) {
+ vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ nsvc_delete(nsvc);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ns_timer, cfg_ns_timer_cmd,
+ "timer " NS_TIMERS " <0-65535>",
+ "Network Service Timer\n"
+ NS_TIMERS_HELP "Timer Value\n")
+{
+ int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
+ int val = atoi(argv[1]);
+
+ if (idx < 0 || idx >= ARRAY_SIZE(vty_nsi->timeout))
+ return CMD_WARNING;
+
+ vty_nsi->timeout[idx] = val;
+
+ return CMD_SUCCESS;
+}
+
+int gprs_ns_vty_init(struct gprs_ns_inst *nsi)
+{
+ vty_nsi = nsi;
+
+ install_element_ve(&show_ns_cmd);
+ install_element_ve(&show_ns_stats_cmd);
+
+ install_element(CONFIG_NODE, &cfg_ns_cmd);
+ install_node(&ns_node, config_write_ns);
+ install_default(NS_NODE);
+ install_element(NS_NODE, &cfg_nse_nsvci_cmd);
+ install_element(NS_NODE, &cfg_nse_remoteip_cmd);
+ install_element(NS_NODE, &cfg_nse_remoteport_cmd);
+ install_element(NS_NODE, &cfg_nse_remoterole_cmd);
+ install_element(NS_NODE, &cfg_no_nse_cmd);
+ install_element(NS_NODE, &cfg_ns_timer_cmd);
+
+ return 0;
+}
diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c
new file mode 100644
index 000000000..ba4671955
--- /dev/null
+++ b/openbsc/src/gprs/gprs_sgsn.c
@@ -0,0 +1,96 @@
+/* GPRS SGSN functionality */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+
+#include <osmocore/linuxlist.h>
+#include <osmocore/talloc.h>
+#include <osmocore/timer.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/debug.h>
+#include <openbsc/gprs_sgsn.h>
+#include <openbsc/gprs_ns.h>
+#include <openbsc/gprs_bssgp.h>
+
+static LLIST_HEAD(sgsn_mm_ctxts);
+
+static int ra_id_equals(const struct gprs_ra_id *id1,
+ const struct gprs_ra_id *id2)
+{
+ return (id1->mcc == id2->mcc && id1->mnc == id2->mnc &&
+ id1->lac == id2->lac && id1->rac == id2->rac);
+}
+
+/* look-up a SGSN MM context based on TLLI + RAI */
+struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
+ const struct gprs_ra_id *raid)
+{
+ struct sgsn_mm_ctx *ctx;
+
+ llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
+ if (tlli == ctx->tlli &&
+ ra_id_equals(raid, &ctx->ra))
+ return ctx;
+ }
+ return NULL;
+}
+
+struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t p_tmsi)
+{
+ struct sgsn_mm_ctx *ctx;
+
+ llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
+ if (p_tmsi == ctx->p_tmsi)
+ return ctx;
+ }
+ return NULL;
+}
+
+struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi)
+{
+ struct sgsn_mm_ctx *ctx;
+
+ llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
+ if (!strcmp(imsi, ctx->imsi))
+ return ctx;
+ }
+ return NULL;
+
+}
+
+/* Allocate a new SGSN MM context */
+struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli,
+ const struct gprs_ra_id *raid)
+{
+ struct sgsn_mm_ctx *ctx = talloc_zero(NULL, struct sgsn_mm_ctx);
+
+ if (!ctx)
+ return NULL;
+
+ memcpy(&ctx->ra, raid, sizeof(ctx->ra));
+ ctx->tlli = tlli;
+ ctx->mm_state = GMM_DEREGISTERED;
+
+ llist_add(&ctx->list, &sgsn_mm_ctxts);
+
+ return ctx;
+}
diff --git a/openbsc/src/gprs/gprs_sndcp.c b/openbsc/src/gprs/gprs_sndcp.c
new file mode 100644
index 000000000..0d1a39004
--- /dev/null
+++ b/openbsc/src/gprs/gprs_sndcp.c
@@ -0,0 +1,70 @@
+/* GPRS SNDCP protocol implementation as per 3GPP TS 04.65 */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/linuxlist.h>
+#include <osmocore/timer.h>
+#include <osmocore/talloc.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/debug.h>
+#include <openbsc/gprs_bssgp.h>
+#include <openbsc/gprs_llc.h>
+
+/* Chapter 7.2: SN-PDU Formats */
+struct sndcp_common_hdr {
+ /* octet 1 */
+ uint8_t nsapi:4;
+ uint8_t more:1;
+ uint8_t type:1;
+ uint8_t first:1;
+ uint8_t spare:1;
+ /* octet 2 */
+ uint8_t pcomp;
+ uint8_t dcomp;
+};
+
+struct sndcp_udata_hdr {
+ /* octet 3 */
+ uint8_t npdu_high:4;
+ uint8_t seg_nr:4;
+ /* octet 4 */
+ uint8_t npdu_low;
+};
+
+/* Entry point for the LL-UNITDATA.indication */
+int sndcp_unitdata_ind(struct msgb *msg, uint8_t sapi, uint8_t *hdr, uint8_t len)
+{
+ struct sndcp_udata_hdr *suh;
+ uint16_t npdu;
+
+ if (suh->type == 0) {
+ LOGP(DGPRS, LOGL_ERROR, "SN-DATA PDU at unitdata_ind() function\n");
+ return -EINVAL;
+ }
+
+ npdu = (suh->npdu_high << 8) | suh->npdu_low;
+}
+
diff --git a/openbsc/src/gprs/gsm_04_08_gprs.c b/openbsc/src/gprs/gsm_04_08_gprs.c
new file mode 100644
index 000000000..4a42113f0
--- /dev/null
+++ b/openbsc/src/gprs/gsm_04_08_gprs.c
@@ -0,0 +1,762 @@
+/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
+ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <openbsc/db.h>
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/gsm_utils.h>
+#include <osmocore/signal.h>
+#include <osmocore/talloc.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/gsm_04_08_gprs.h>
+#include <openbsc/paging.h>
+#include <openbsc/transaction.h>
+#include <openbsc/gprs_bssgp.h>
+#include <openbsc/gprs_llc.h>
+#include <openbsc/gprs_sgsn.h>
+
+/* 10.5.5.14 GPRS MM Cause / Table 10.5.147 */
+struct value_string gmm_cause_names[] = {
+ /* FIXME */
+ { GMM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" },
+ { GMM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" },
+ { GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL,
+ "Message type non-existant or not implemented" },
+ { GMM_CAUSE_MSGT_INCOMP_P_STATE,
+ "Message type not compatible with protocol state" },
+ { GMM_CAUSE_IE_NOTEXIST_NOTIMPL,
+ "Information element non-existent or not implemented" },
+ { GMM_CAUSE_COND_IE_ERR, "Conditional IE error" },
+ { GMM_CAUSE_MSG_INCOMP_P_STATE,
+ "Message not compatible with protocol state " },
+ { GMM_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" },
+ { 0, NULL }
+};
+
+/* 10.5.6.6 SM Cause / Table 10.5.157 */
+struct value_string gsm_cause_names[] = {
+ { GSM_CAUSE_INSUFF_RSRC, "Insufficient resources" },
+ { GSM_CAUSE_MISSING_APN, "Missing or unknown APN" },
+ { GSM_CAUSE_UNKNOWN_PDP, "Unknown PDP address or PDP type" },
+ { GSM_CAUSE_AUTH_FAILED, "User Authentication failed" },
+ { GSM_CAUSE_ACT_REJ_GGSN, "Activation rejected by GGSN" },
+ { GSM_CAUSE_ACT_REJ_UNSPEC, "Activation rejected, unspecified" },
+ { GSM_CAUSE_SERV_OPT_NOTSUPP, "Service option not supported" },
+ { GSM_CAUSE_REQ_SERV_OPT_NOTSUB,
+ "Requested service option not subscribed" },
+ { GSM_CAUSE_SERV_OPT_TEMP_OOO,
+ "Service option temporarily out of order" },
+ { GSM_CAUSE_NSAPI_IN_USE, "NSAPI already used" },
+ { GSM_CAUSE_DEACT_REGULAR, "Regular deactivation" },
+ { GSM_CAUSE_QOS_NOT_ACCEPTED, "QoS not accepted" },
+ { GSM_CAUSE_NET_FAIL, "Network Failure" },
+ { GSM_CAUSE_REACT_RQD, "Reactivation required" },
+ { GSM_CAUSE_FEATURE_NOTSUPP, "Feature not supported " },
+ { GSM_CAUSE_INVALID_TRANS_ID, "Invalid transaction identifier" },
+ { GSM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" },
+ { GSM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" },
+ { GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL,
+ "Message type non-existant or not implemented" },
+ { GSM_CAUSE_MSGT_INCOMP_P_STATE,
+ "Message type not compatible with protocol state" },
+ { GSM_CAUSE_IE_NOTEXIST_NOTIMPL,
+ "Information element non-existent or not implemented" },
+ { GSM_CAUSE_COND_IE_ERR, "Conditional IE error" },
+ { GSM_CAUSE_MSG_INCOMP_P_STATE,
+ "Message not compatible with protocol state " },
+ { GSM_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" },
+ { 0, NULL }
+};
+
+static const char *att_name(uint8_t type)
+{
+ switch (type) {
+ case GPRS_ATT_T_ATTACH:
+ return "GPRS attach";
+ case GPRS_ATT_T_ATT_WHILE_IMSI:
+ return "GPRS attach while IMSI attached";
+ case GPRS_ATT_T_COMBINED:
+ return "Combined GPRS/IMSI attach";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *upd_name(uint8_t type)
+{
+ switch (type) {
+ case GPRS_UPD_T_RA:
+ return "RA updating";
+ case GPRS_UPD_T_RA_LA:
+ return "combined RA/LA updating";
+ case GPRS_UPD_T_RA_LA_IMSI_ATT:
+ return "combined RA/LA updating + IMSI attach";
+ case GPRS_UPD_T_PERIODIC:
+ return "periodic updating";
+ }
+ return "unknown";
+}
+
+/* Send a message through the underlying layer */
+static int gsm48_gmm_sendmsg(struct msgb *msg, int command)
+{
+ /* caller needs to provide TLLI, BVCI and NSEI */
+ return gprs_llc_tx_ui(msg, GPRS_SAPI_GMM, command);
+}
+
+/* copy identifiers from old message to new message, this
+ * is required so lower layers can route it correctly */
+static void gmm_copy_id(struct msgb *msg, const struct msgb *old)
+{
+ msgb_tlli(msg) = msgb_tlli(old);
+ msgb_bvci(msg) = msgb_bvci(old);
+ msgb_nsei(msg) = msgb_nsei(old);
+}
+
+static struct gsm48_qos default_qos = {
+ .delay_class = 4, /* best effort */
+ .reliab_class = GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT,
+ .peak_tput = GSM48_QOS_PEAK_TPUT_32000bps,
+ .preced_class = GSM48_QOS_PC_NORMAL,
+ .mean_tput = GSM48_QOS_MEAN_TPUT_BEST_EFFORT,
+ .traf_class = GSM48_QOS_TC_INTERACTIVE,
+ .deliv_order = GSM48_QOS_DO_UNORDERED,
+ .deliv_err_sdu = GSM48_QOS_ERRSDU_YES,
+ .max_sdu_size = GSM48_QOS_MAXSDU_1520,
+ .max_bitrate_up = GSM48_QOS_MBRATE_63k,
+ .max_bitrate_down = GSM48_QOS_MBRATE_63k,
+ .resid_ber = GSM48_QOS_RBER_5e_2,
+ .sdu_err_ratio = GSM48_QOS_SERR_1e_2,
+ .handling_prio = 3,
+ .xfer_delay = 0x10, /* 200ms */
+ .guar_bitrate_up = GSM48_QOS_MBRATE_0k,
+ .guar_bitrate_down = GSM48_QOS_MBRATE_0k,
+ .sig_ind = 0, /* not optimised for signalling */
+ .max_bitrate_down_ext = 0, /* use octet 9 */
+ .guar_bitrate_down_ext = 0, /* use octet 13 */
+};
+
+/* Chapter 9.4.2: Attach accept */
+static int gsm48_tx_gmm_att_ack(struct msgb *old_msg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+ struct gsm48_attach_ack *aa;
+ struct gprs_ra_id ra_id;
+
+ DEBUGP(DMM, "<- GPRS ATTACH ACCEPT\n");
+
+ gmm_copy_id(msg, old_msg);
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ gh->proto_discr = GSM48_PDISC_MM_GPRS;
+ gh->msg_type = GSM48_MT_GMM_ATTACH_ACK;
+
+ aa = (struct gsm48_attach_ack *) msgb_put(msg, sizeof(*aa));
+ aa->force_stby = 0; /* not indicated */
+ aa->att_result = 1; /* GPRS only */
+ aa->ra_upd_timer = GPRS_TMR_MINUTE | 10;
+ aa->radio_prio = 4; /* lowest */
+ bssgp_parse_cell_id(&ra_id, msgb_bcid(old_msg));
+ gsm48_construct_ra(aa->ra_id.digits, &ra_id);
+
+ /* Option: P-TMSI signature, allocated P-TMSI, MS ID, ... */
+ return gsm48_gmm_sendmsg(msg, 0);
+}
+
+/* Chapter 9.4.5: Attach reject */
+static int gsm48_tx_gmm_att_rej(struct msgb *old_msg, uint8_t gmm_cause)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+
+ DEBUGP(DMM, "<- GPRS ATTACH REJECT\n");
+
+ gmm_copy_id(msg, old_msg);
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
+ gh->proto_discr = GSM48_PDISC_MM_GPRS;
+ gh->msg_type = GSM48_MT_GMM_ATTACH_REJ;
+ gh->data[0] = gmm_cause;
+
+ return gsm48_gmm_sendmsg(msg, 0);
+}
+
+/* Transmit Chapter 9.4.12 Identity Request */
+static int gsm48_tx_gmm_id_req(struct msgb *old_msg, uint8_t id_type)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+
+ DEBUGP(DMM, "-> GPRS IDENTITY REQUEST: mi_type=%02x\n", id_type);
+
+ gmm_copy_id(msg, old_msg);
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
+ gh->proto_discr = GSM48_PDISC_MM_GPRS;
+ gh->msg_type = GSM48_MT_GMM_ID_REQ;
+ /* 10.5.5.9 ID type 2 + identity type and 10.5.5.7 'force to standby' IE */
+ gh->data[0] = id_type & 0xf;
+
+ return gsm48_gmm_sendmsg(msg, 0);
+}
+
+/* Check if we can already authorize a subscriber */
+static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx, struct msgb *msg)
+{
+ if (strlen(ctx->imei) && strlen(ctx->imsi)) {
+ ctx->mm_state = GMM_REGISTERED_NORMAL;
+ return gsm48_tx_gmm_att_ack(msg);
+ }
+ if (!strlen(ctx->imei))
+ return gsm48_tx_gmm_id_req(msg, GSM_MI_TYPE_IMEI);
+
+ if (!strlen(ctx->imsi))
+ return gsm48_tx_gmm_id_req(msg, GSM_MI_TYPE_IMSI);
+
+ return 0;
+}
+
+/* Parse Chapter 9.4.13 Identity Response */
+static int gsm48_rx_gmm_id_resp(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
+ uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK;
+ char mi_string[GSM48_MI_SIZE];
+ struct gprs_ra_id ra_id;
+ struct sgsn_mm_ctx *ctx;
+
+ gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]);
+ DEBUGP(DMM, "GMM IDENTITY RESPONSE: mi_type=0x%02x MI(%s) ",
+ mi_type, mi_string);
+
+ bssgp_parse_cell_id(&ra_id, msgb_bcid(msg));
+ ctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id);
+ if (!ctx) {
+ DEBUGP(DMM, "from unknown TLLI 0x%08x?!?\n", msgb_tlli(msg));
+ return -EINVAL;
+ }
+
+ switch (mi_type) {
+ case GSM_MI_TYPE_IMSI:
+ /* we already have a mm context with current TLLI, but no
+ * P-TMSI / IMSI yet. What we now need to do is to fill
+ * this initial context with data from the HLR */
+ strncpy(ctx->imsi, mi_string, sizeof(ctx->imei));
+ break;
+ case GSM_MI_TYPE_IMEI:
+ strncpy(ctx->imei, mi_string, sizeof(ctx->imei));
+ break;
+ case GSM_MI_TYPE_IMEISV:
+ break;
+ }
+
+ DEBUGPC(DMM, "\n");
+ /* Check if we can let the mobile station enter */
+ return gsm48_gmm_authorize(ctx, msg);
+}
+
+static void attach_rej_cb(void *data)
+{
+ struct sgsn_mm_ctx *ctx = data;
+
+ /* FIXME: determine through which BTS/TRX to send this */
+ //gsm48_tx_gmm_att_rej(ctx->tlli, GMM_CAUSE_MS_ID_NOT_DERIVED);
+ ctx->mm_state = GMM_DEREGISTERED;
+ /* FIXME: release the context */
+}
+
+static void schedule_reject(struct sgsn_mm_ctx *ctx)
+{
+ ctx->T = 3370;
+ ctx->timer.cb = attach_rej_cb;
+ ctx->timer.data = ctx;
+ bsc_schedule_timer(&ctx->timer, 6, 0);
+}
+
+/* Section 9.4.1 Attach request */
+static int gsm48_rx_gmm_att_req(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
+ uint8_t *cur = gh->data, *msnc, *mi, *old_ra_info;
+ uint8_t msnc_len, att_type, mi_len, mi_type;
+ uint16_t drx_par;
+ uint32_t tmsi;
+ char mi_string[GSM48_MI_SIZE];
+ struct gprs_ra_id ra_id;
+ uint16_t cid;
+ struct sgsn_mm_ctx *ctx;
+
+ DEBUGP(DMM, "GMM ATTACH REQUEST ");
+
+ /* As per TS 04.08 Chapter 4.7.1.4, the attach request arrives either
+ * with a foreign TLLI (P-TMSI that was allocated to the MS before),
+ * or with random TLLI. */
+
+ cid = bssgp_parse_cell_id(&ra_id, msgb_bcid(msg));
+
+ /* MS network capability 10.5.5.12 */
+ msnc_len = *cur++;
+ msnc = cur;
+ if (msnc_len > 2)
+ goto err_inval;
+ cur += msnc_len;
+
+ /* aTTACH Type 10.5.5.2 */
+ att_type = *cur++ & 0x0f;
+
+ /* DRX parameter 10.5.5.6 */
+ drx_par = *cur++;
+ drx_par |= *cur++ << 8;
+
+ /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */
+ mi_len = *cur++;
+ mi = cur;
+ if (mi_len > 8)
+ goto err_inval;
+ mi_type = *mi & GSM_MI_TYPE_MASK;
+ cur += mi_len;
+
+ gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len);
+
+ DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, att_name(att_type));
+
+ /* Old routing area identification 10.5.5.15 */
+ old_ra_info = cur;
+ cur += 6;
+
+ /* MS Radio Access Capability 10.5.5.12a */
+
+ /* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status */
+
+ switch (mi_type) {
+ case GSM_MI_TYPE_IMSI:
+ /* Try to find MM context based on IMSI */
+ ctx = sgsn_mm_ctx_by_imsi(mi_string);
+ if (!ctx) {
+#if 0
+ return gsm48_tx_gmm_att_rej(msg, GMM_CAUSE_IMSI_UNKNOWN);
+#else
+ /* As a temorary hack, we simply assume that the IMSI exists */
+ ctx = sgsn_mm_ctx_alloc(0, &ra_id);
+ if (!ctx)
+ return gsm48_tx_gmm_att_rej(msg, GMM_CAUSE_NET_FAIL);
+ strncpy(ctx->imsi, mi_string, sizeof(ctx->imsi));
+#endif
+ }
+ /* FIXME: Start some timer */
+ ctx->mm_state = GMM_COMMON_PROC_INIT;
+ ctx->tlli = msgb_tlli(msg);
+ break;
+ case GSM_MI_TYPE_TMSI:
+ tmsi = strtoul(mi_string, NULL, 10);
+ /* Try to find MM context based on P-TMSI */
+ ctx = sgsn_mm_ctx_by_ptmsi(tmsi);
+ if (!ctx) {
+ ctx = sgsn_mm_ctx_alloc(msgb_tlli(msg), &ra_id);
+ /* FIXME: Start some timer */
+ ctx->mm_state = GMM_COMMON_PROC_INIT;
+ ctx->tlli = msgb_tlli(msg);
+ }
+ break;
+ default:
+ return 0;
+ }
+ /* Update MM Context with currient RA and Cell ID */
+ ctx->ra = ra_id;
+ ctx->cell_id = cid;
+
+ /* FIXME: allocate a new P-TMSI (+ P-TMSI signature) */
+ /* FIXME: update the TLLI with the new local TLLI based on the P-TMSI */
+
+ DEBUGPC(DMM, "\n");
+
+ return ctx ? gsm48_gmm_authorize(ctx, msg) : 0;
+
+err_inval:
+ DEBUGPC(DMM, "\n");
+ return gsm48_tx_gmm_att_rej(msg, GMM_CAUSE_SEM_INCORR_MSG);
+}
+
+/* Chapter 9.4.15: Routing area update accept */
+static int gsm48_tx_gmm_ra_upd_ack(struct msgb *old_msg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+ struct gsm48_ra_upd_ack *rua;
+ struct gprs_ra_id ra_id;
+
+ DEBUGP(DMM, "<- ROUTING AREA UPDATE ACCEPT\n");
+
+ gmm_copy_id(msg, old_msg);
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ gh->proto_discr = GSM48_PDISC_MM_GPRS;
+ gh->msg_type = GSM48_MT_GMM_RA_UPD_ACK;
+
+ rua = (struct gsm48_ra_upd_ack *) msgb_put(msg, sizeof(*rua));
+ rua->force_stby = 0; /* not indicated */
+ rua->upd_result = 0; /* RA updated */
+ rua->ra_upd_timer = GPRS_TMR_MINUTE | 10;
+
+ bssgp_parse_cell_id(&ra_id, msgb_bcid(old_msg));
+ gsm48_construct_ra(rua->ra_id.digits, &ra_id);
+
+ /* Option: P-TMSI signature, allocated P-TMSI, MS ID, ... */
+ return gsm48_gmm_sendmsg(msg, 0);
+}
+
+/* Chapter 9.4.17: Routing area update reject */
+static int gsm48_tx_gmm_ra_upd_rej(struct msgb *old_msg, uint8_t cause)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+
+ DEBUGP(DMM, "<- ROUTING AREA UPDATE REJECT\n");
+
+ gmm_copy_id(msg, old_msg);
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2);
+ gh->proto_discr = GSM48_PDISC_MM_GPRS;
+ gh->msg_type = GSM48_MT_GMM_RA_UPD_REJ;
+ gh->data[0] = cause;
+ gh->data[1] = 0; /* ? */
+
+ /* Option: P-TMSI signature, allocated P-TMSI, MS ID, ... */
+ return gsm48_gmm_sendmsg(msg, 0);
+}
+
+/* Chapter 9.4.14: Routing area update request */
+static int gsm48_rx_gmm_ra_upd_req(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
+ struct sgsn_mm_ctx *mmctx;
+ uint8_t *cur = gh->data;
+ struct gprs_ra_id old_ra_id;
+ uint8_t upd_type;
+
+ /* Update Type 10.5.5.18 */
+ upd_type = *cur++ & 0x0f;
+
+ DEBUGP(DMM, "GMM RA UPDATE REQUEST type=\"%s\" ", upd_name(upd_type));
+
+ /* Old routing area identification 10.5.5.15 */
+ gsm48_parse_ra(&old_ra_id, cur);
+ cur += 6;
+
+ /* MS Radio Access Capability 10.5.5.12a */
+
+ /* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status,
+ * DRX parameter, MS network capability */
+
+ switch (upd_type) {
+ case GPRS_UPD_T_RA_LA:
+ case GPRS_UPD_T_RA_LA_IMSI_ATT:
+ DEBUGPC(DMM, " unsupported in Mode III, is your SI13 corrupt?\n");
+ return gsm48_tx_gmm_ra_upd_rej(msg, GMM_CAUSE_PROTO_ERR_UNSPEC);
+ break;
+ case GPRS_UPD_T_RA:
+ case GPRS_UPD_T_PERIODIC:
+ break;
+ }
+
+ /* Look-up the MM context based on old RA-ID and TLLI */
+ mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &old_ra_id);
+ if (!mmctx || mmctx->mm_state == GMM_DEREGISTERED) {
+ /* The MS has to perform GPRS attach */
+ DEBUGPC(DMM, " REJECT\n");
+ return gsm48_tx_gmm_ra_upd_rej(msg, GMM_CAUSE_IMPL_DETACHED);
+ }
+
+ /* Update the MM context with the new RA-ID */
+ bssgp_parse_cell_id(&mmctx->ra, msgb_bcid(msg));
+ /* Update the MM context with the new TLLI */
+ mmctx->tlli = msgb_tlli(msg);
+ /* FIXME: Update the MM context with the MS radio acc capabilities */
+ /* FIXME: Update the MM context with the MS network capabilities */
+
+ DEBUGPC(DMM, " ACCEPT\n");
+ return gsm48_tx_gmm_ra_upd_ack(msg);
+}
+
+static int gsm48_rx_gmm_status(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+
+ DEBUGP(DMM, "GPRS MM STATUS (cause: %s)\n",
+ get_value_string(gmm_cause_names, gh->data[0]));
+
+ return 0;
+}
+
+/* GPRS Mobility Management */
+static int gsm0408_rcv_gmm(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
+ int rc;
+
+ switch (gh->msg_type) {
+ case GSM48_MT_GMM_RA_UPD_REQ:
+ rc = gsm48_rx_gmm_ra_upd_req(msg);
+ break;
+ case GSM48_MT_GMM_ATTACH_REQ:
+ rc = gsm48_rx_gmm_att_req(msg);
+ break;
+ case GSM48_MT_GMM_ID_RESP:
+ rc = gsm48_rx_gmm_id_resp(msg);
+ break;
+ case GSM48_MT_GMM_STATUS:
+ rc = gsm48_rx_gmm_status(msg);
+ break;
+ case GSM48_MT_GMM_RA_UPD_COMPL:
+ /* only in case SGSN offered new P-TMSI */
+ case GSM48_MT_GMM_ATTACH_COMPL:
+ /* only in case SGSN offered new P-TMSI */
+ case GSM48_MT_GMM_DETACH_REQ:
+ case GSM48_MT_GMM_PTMSI_REALL_COMPL:
+ case GSM48_MT_GMM_AUTH_CIPH_RESP:
+ DEBUGP(DMM, "Unimplemented GSM 04.08 GMM msg type 0x%02x\n",
+ gh->msg_type);
+ break;
+ default:
+ DEBUGP(DMM, "Unknown GSM 04.08 GMM msg type 0x%02x\n",
+ gh->msg_type);
+ break;
+ }
+
+ return rc;
+}
+
+static void msgb_put_pdp_addr_ipv4(struct msgb *msg, uint32_t ipaddr)
+{
+ uint8_t v[6];
+
+ v[0] = PDP_TYPE_ORG_IETF;
+ v[1] = PDP_TYPE_N_IETF_IPv4;
+ *(uint32_t *)(v+2) = htonl(ipaddr);
+
+ msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v);
+}
+
+static void msgb_put_pdp_addr_ppp(struct msgb *msg)
+{
+ uint8_t v[2];
+
+ v[0] = PDP_TYPE_ORG_ETSI;
+ v[1] = PDP_TYPE_N_ETSI_PPP;
+
+ msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v);
+}
+
+/* Section 9.5.2: Ativate PDP Context Accept */
+static int gsm48_tx_gsm_act_pdp_acc(struct msgb *old_msg, struct gsm48_act_pdp_ctx_req *req)
+{
+ struct gsm48_hdr *old_gh = (struct gsm48_hdr *) msgb_gmmh(old_msg);
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_act_pdp_ctx_ack *act_ack;
+ struct gsm48_hdr *gh;
+ uint8_t transaction_id = ((old_gh->proto_discr >> 4) ^ 0x8); /* flip */
+
+ DEBUGP(DMM, "<- ACTIVATE PDP CONTEXT ACK\n");
+
+ gmm_copy_id(msg, old_msg);
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4);
+ gh->msg_type = GSM48_MT_GSM_ACT_PDP_ACK;
+
+ /* Negotiated LLC SAPI */
+ msgb_v_put(msg, req->req_llc_sapi);
+ /* copy QoS parameters from original request */
+ msgb_lv_put(msg, sizeof(default_qos), (uint8_t *)&default_qos);
+ /* Radio priority 10.5.7.2 */
+ msgb_v_put(msg, 4);
+ /* PDP address */
+ msgb_put_pdp_addr_ipv4(msg, 0x01020304);
+ /* Optional: Protocol configuration options */
+ /* Optional: Packet Flow Identifier */
+
+ return gsm48_gmm_sendmsg(msg, 0);
+}
+
+/* Section 9.5.9: Deactivate PDP Context Accept */
+static int gsm48_tx_gsm_deact_pdp_acc(struct msgb *old_msg)
+{
+ struct gsm48_hdr *old_gh = (struct gsm48_hdr *) msgb_gmmh(old_msg);
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+ uint8_t transaction_id = ((old_gh->proto_discr >> 4) ^ 0x8); /* flip */
+
+ DEBUGP(DMM, "<- DEACTIVATE PDP CONTEXT ACK\n");
+
+ gmm_copy_id(msg, old_msg);
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4);
+ gh->msg_type = GSM48_MT_GSM_DEACT_PDP_ACK;
+
+ return gsm48_gmm_sendmsg(msg, 0);
+}
+
+/* Section 9.5.1: Activate PDP Context Request */
+static int gsm48_rx_gsm_act_pdp_req(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
+ struct gsm48_act_pdp_ctx_req *act_req = (struct gsm48_act_pdp_ctx_req *) gh->data;
+ uint8_t *pdp_addr_lv = act_req->data;
+ uint8_t req_qos_len, req_pdpa_len;
+ uint8_t *req_qos, *req_pdpa;
+ struct tlv_parsed tp;
+
+ DEBUGP(DMM, "ACTIVATE PDP CONTEXT REQ: ");
+ req_qos_len = act_req->data[0];
+ req_qos = act_req->data + 1; /* 10.5.6.5 */
+ req_pdpa_len = act_req->data[1 + req_qos_len];
+ req_pdpa = act_req->data + 1 + req_qos_len + 1; /* 10.5.6.4 */
+
+ switch (req_pdpa[0] & 0xf) {
+ case 0x0:
+ DEBUGPC(DMM, "ETSI ");
+ break;
+ case 0x1:
+ DEBUGPC(DMM, "IETF ");
+ break;
+ case 0xf:
+ DEBUGPC(DMM, "Empty ");
+ break;
+ }
+
+ switch (req_pdpa[1]) {
+ case 0x21:
+ DEBUGPC(DMM, "IPv4 ");
+ if (req_pdpa_len >= 6) {
+ struct in_addr ia;
+ ia.s_addr = ntohl(*((uint32_t *) (req_pdpa+2)));
+ DEBUGPC(DMM, "%s ", inet_ntoa(ia));
+ }
+ break;
+ case 0x57:
+ DEBUGPC(DMM, "IPv6 ");
+ if (req_pdpa_len >= 18) {
+ /* FIXME: print IPv6 address */
+ }
+ break;
+ default:
+ DEBUGPC(DMM, "0x%02x ", req_pdpa[1]);
+ break;
+ }
+
+ /* FIXME: parse TLV for AP name and protocol config options */
+ if (TLVP_PRESENT(&tp, GSM48_IE_GSM_APN)) {}
+ if (TLVP_PRESENT(&tp, GSM48_IE_GSM_PROTO_CONF_OPT)) {}
+
+ return gsm48_tx_gsm_act_pdp_acc(msg, act_req);
+}
+
+/* Section 9.5.8: Deactivate PDP Context Request */
+static int gsm48_rx_gsm_deact_pdp_req(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
+
+ DEBUGP(DMM, "DEACTIVATE PDP CONTEXT REQ (cause: %s)\n",
+ get_value_string(gsm_cause_names, gh->data[0]));
+
+ return gsm48_tx_gsm_deact_pdp_acc(msg);
+}
+
+static int gsm48_rx_gsm_status(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+
+ DEBUGP(DMM, "GPRS SM STATUS (cause: %s)\n",
+ get_value_string(gsm_cause_names, gh->data[0]));
+
+ return 0;
+}
+
+/* GPRS Session Management */
+static int gsm0408_rcv_gsm(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
+ int rc;
+
+ switch (gh->msg_type) {
+ case GSM48_MT_GSM_ACT_PDP_REQ:
+ rc = gsm48_rx_gsm_act_pdp_req(msg);
+ break;
+ case GSM48_MT_GSM_DEACT_PDP_REQ:
+ rc = gsm48_rx_gsm_deact_pdp_req(msg);
+ case GSM48_MT_GSM_STATUS:
+ rc = gsm48_rx_gsm_status(msg);
+ break;
+ case GSM48_MT_GSM_REQ_PDP_ACT_REJ:
+ case GSM48_MT_GSM_ACT_AA_PDP_REQ:
+ case GSM48_MT_GSM_DEACT_AA_PDP_REQ:
+ DEBUGP(DMM, "Unimplemented GSM 04.08 GSM msg type 0x%02x\n",
+ gh->msg_type);
+ break;
+ default:
+ DEBUGP(DMM, "Unknown GSM 04.08 GSM msg type 0x%02x\n",
+ gh->msg_type);
+ break;
+
+ }
+
+ return rc;
+}
+
+/* Main entry point for incoming 04.08 GPRS messages */
+int gsm0408_gprs_rcvmsg(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
+ uint8_t pdisc = gh->proto_discr & 0x0f;
+ int rc = -EINVAL;
+
+ switch (pdisc) {
+ case GSM48_PDISC_MM_GPRS:
+ rc = gsm0408_rcv_gmm(msg);
+ break;
+ case GSM48_PDISC_SM_GPRS:
+ rc = gsm0408_rcv_gsm(msg);
+ break;
+ default:
+ DEBUGP(DMM, "Unknown GSM 04.08 discriminator 0x%02x\n",
+ pdisc);
+ break;
+ }
+
+ return rc;
+}
diff --git a/openbsc/src/gprs/osmo_gbproxy.cfg b/openbsc/src/gprs/osmo_gbproxy.cfg
new file mode 100644
index 000000000..d51b04a11
--- /dev/null
+++ b/openbsc/src/gprs/osmo_gbproxy.cfg
@@ -0,0 +1,13 @@
+!
+! OpenBSC configuration saved from vty
+! !
+!
+line vty
+ no login
+!
+gbproxy
+ nsip bss local port 23000
+ nsip sgsn remote ip 127.0.0.1
+ nsip sgsn remote port 7777
+ nsip sgsn nsei 101
+ nsip sgsn nsvci 101
diff --git a/openbsc/src/gprs/osmo_sgsn.cfg b/openbsc/src/gprs/osmo_sgsn.cfg
new file mode 100644
index 000000000..f39e8536f
--- /dev/null
+++ b/openbsc/src/gprs/osmo_sgsn.cfg
@@ -0,0 +1,9 @@
+!
+! OpenBSC configuration saved from vty
+! !
+!
+line vty
+ no login
+!
+sgsn
+ nsip local port 23000
diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c
new file mode 100644
index 000000000..b355fb58d
--- /dev/null
+++ b/openbsc/src/gprs/sgsn_main.c
@@ -0,0 +1,183 @@
+/* GPRS SGSN Implementation */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by On Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocore/talloc.h>
+#include <osmocore/select.h>
+#include <osmocore/rate_ctr.h>
+
+#include <openbsc/signal.h>
+#include <openbsc/debug.h>
+#include <openbsc/telnet_interface.h>
+#include <openbsc/vty.h>
+#include <openbsc/sgsn.h>
+#include <openbsc/gprs_ns.h>
+#include <openbsc/gprs_bssgp.h>
+
+#include "../../bscconfig.h"
+
+/* this is here for the vty... it will never be called */
+void subscr_put() { abort(); }
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+void *tall_bsc_ctx;
+
+struct gprs_ns_inst *sgsn_nsi;
+
+const char *openbsc_version = "Osmocom NSIP Proxy " PACKAGE_VERSION;
+const char *openbsc_copyright =
+ "Copyright (C) 2010 Harald Welte and On-Waves\n"
+ "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\n"
+ "Dieter Spaar, Andreas Eversberg, Holger Freyther\n\n"
+ "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n";
+
+static char *config_file = "osmo_sgsn.cfg";
+static struct sgsn_config sgcfg;
+
+/* call-back function for the NS protocol */
+static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
+ struct msgb *msg, u_int16_t bvci)
+{
+ int rc = 0;
+
+ switch (event) {
+ case GPRS_NS_EVT_UNIT_DATA:
+ /* hand the message into the BSSGP implementation */
+ rc = gprs_bssgp_rcvmsg(msg);
+ break;
+ default:
+ LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
+ if (msg)
+ talloc_free(msg);
+ rc = -EIO;
+ break;
+ }
+ return rc;
+}
+
+static void signal_handler(int signal)
+{
+ fprintf(stdout, "signal %u received\n", signal);
+
+ switch (signal) {
+ case SIGINT:
+ dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL);
+ sleep(1);
+ exit(0);
+ break;
+ case SIGABRT:
+ /* in case of abort, we want to obtain a talloc report
+ * and then return to the caller, who will abort the process */
+ case SIGUSR1:
+ talloc_report(tall_vty_ctx, stderr);
+ talloc_report_full(tall_bsc_ctx, stderr);
+ break;
+ case SIGUSR2:
+ talloc_report_full(tall_vty_ctx, stderr);
+ break;
+ default:
+ break;
+ }
+}
+
+/* NSI that BSSGP uses when transmitting on NS */
+extern struct gprs_ns_inst *bssgp_nsi;
+extern void *tall_msgb_ctx;
+
+int main(int argc, char **argv)
+{
+ struct gsm_network dummy_network;
+ struct log_target *stderr_target;
+ struct sockaddr_in sin;
+ int rc;
+
+ tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn");
+ tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb");
+
+ signal(SIGINT, &signal_handler);
+ signal(SIGABRT, &signal_handler);
+ signal(SIGUSR1, &signal_handler);
+ signal(SIGUSR2, &signal_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ log_init(&log_info);
+ stderr_target = log_target_create_stderr();
+ log_add_target(stderr_target);
+ log_set_all_filter(stderr_target, 1);
+
+ rate_ctr_init(tall_bsc_ctx);
+ telnet_init(&dummy_network, 4245);
+
+ sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb);
+ if (!sgsn_nsi) {
+ LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
+ exit(1);
+ }
+ bssgp_nsi = sgcfg.nsi = sgsn_nsi;
+ gprs_ns_vty_init(bssgp_nsi);
+ /* FIXME: register signal handler for SS_NS */
+
+ rc = sgsn_parse_config(config_file, &sgcfg);
+ if (rc < 0) {
+ LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n");
+ exit(2);
+ }
+
+ nsip_listen(sgsn_nsi, sgcfg.nsip_listen_port);
+
+ while (1) {
+ rc = bsc_select_main(0);
+ if (rc < 0)
+ exit(3);
+ }
+
+ exit(0);
+}
+
+struct gsm_network;
+int bsc_vty_init(struct gsm_network *dummy)
+{
+ cmd_init(1);
+ vty_init();
+
+ openbsc_vty_add_cmds();
+ sgsn_vty_init();
+ return 0;
+}
+
diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c
new file mode 100644
index 000000000..ec18fcbf9
--- /dev/null
+++ b/openbsc/src/gprs/sgsn_vty.c
@@ -0,0 +1,146 @@
+/*
+ * (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocore/talloc.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/sgsn.h>
+#include <openbsc/gprs_ns.h>
+
+#include <vty/command.h>
+#include <vty/vty.h>
+
+static struct sgsn_config *g_cfg = NULL;
+
+static struct cmd_node sgsn_node = {
+ SGSN_NODE,
+ "%s(sgsn)#",
+ 1,
+};
+
+static int config_write_sgsn(struct vty *vty)
+{
+ struct in_addr ia;
+
+ vty_out(vty, "sgsn%s", VTY_NEWLINE);
+
+ if (g_cfg->nsip_listen_ip) {
+ ia.s_addr = htonl(g_cfg->nsip_listen_ip);
+ vty_out(vty, " nsip local ip %s%s", inet_ntoa(ia),
+ VTY_NEWLINE);
+ }
+ vty_out(vty, " nsip local port %u%s", g_cfg->nsip_listen_port,
+ VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn",
+ SHOW_STR "Display information about the SGSN")
+{
+ /* FIXME: iterate over list of NS-VC's and display their state */
+ struct gprs_ns_inst *nsi = g_cfg->nsi;
+ struct gprs_nsvc *nsvc;
+
+ llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
+ vty_out(vty, "NSEI %5u, NS-VC %5u, %s-mode, %s %s%s",
+ nsvc->nsei, nsvc->nsvci,
+ nsvc->remote_end_is_sgsn ? "BSS" : "SGSN",
+ nsvc->state & NSE_S_ALIVE ? "ALIVE" : "DEAD",
+ nsvc->state & NSE_S_BLOCKED ? "BLOCKED" : "UNBLOCKED",
+ VTY_NEWLINE);
+ if (nsvc->nsi->ll == GPRS_NS_LL_UDP)
+ vty_out(vty, " remote peer %s:%u%s",
+ inet_ntoa(nsvc->ip.bts_addr.sin_addr),
+ ntohs(nsvc->ip.bts_addr.sin_port), VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_sgsn,
+ cfg_sgsn_cmd,
+ "sgsn",
+ "Configure the SGSN")
+{
+ vty->node = SGSN_NODE;
+ return CMD_SUCCESS;
+}
+
+
+DEFUN(cfg_nsip_local_ip,
+ cfg_nsip_local_ip_cmd,
+ "nsip local ip A.B.C.D",
+ "Set the IP address on which we listen for BSS connects")
+{
+ struct in_addr ia;
+
+ inet_aton(argv[0], &ia);
+ g_cfg->nsip_listen_ip = ntohl(ia.s_addr);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nsip_local_port,
+ cfg_nsip_local_port_cmd,
+ "nsip local port <0-65534>",
+ "Set the UDP port on which we listen for BSS connects")
+{
+ unsigned int port = atoi(argv[0]);
+
+ g_cfg->nsip_listen_port = port;
+ return CMD_SUCCESS;
+}
+
+
+
+
+int sgsn_vty_init(void)
+{
+ install_element(VIEW_NODE, &show_sgsn_cmd);
+
+ install_element(CONFIG_NODE, &cfg_sgsn_cmd);
+ install_node(&sgsn_node, config_write_sgsn);
+ install_default(SGSN_NODE);
+ install_element(SGSN_NODE, &cfg_nsip_local_ip_cmd);
+ install_element(SGSN_NODE, &cfg_nsip_local_port_cmd);
+
+ return 0;
+}
+
+int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg)
+{
+ int rc;
+
+ g_cfg = cfg;
+ rc = vty_read_config_file(config_file);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c
index b770b52fc..1b3ed2537 100644
--- a/openbsc/src/gsm_04_08_utils.c
+++ b/openbsc/src/gsm_04_08_utils.c
@@ -36,19 +36,10 @@
#include <openbsc/paging.h>
#include <openbsc/signal.h>
-#define GSM48_ALLOC_SIZE 1024
-#define GSM48_ALLOC_HEADROOM 128
-
/* should ip.access BTS use direct RTP streams between each other (1),
* or should OpenBSC always act as RTP relay/proxy in between (0) ? */
int ipacc_rtp_direct = 1;
-struct msgb *gsm48_msgb_alloc(void)
-{
- return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM,
- "GSM 04.08");
-}
-
int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans)
{
struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data;
@@ -288,7 +279,7 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr)
sig_data.bts = msg->lchan->ts->trx->bts;
sig_data.lchan = msg->lchan;
- bts->network->stats.paging.completed++;
+ counter_inc(bts->network->stats.paging.completed);
dispatch_signal(SS_PAGING, S_PAGING_SUCCEEDED, &sig_data);
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 511ad47e7..3492f1f3b 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -675,7 +675,7 @@ static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans,
GSM411_RP_CAUSE_INV_MAND_INF);
return -EIO;
}
- msg->smsh = tpdu;
+ msg->l4h = tpdu;
DEBUGP(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len));
diff --git a/openbsc/src/gsm_04_80.c b/openbsc/src/gsm_04_80.c
index 2112e3f99..ef10b1702 100644
--- a/openbsc/src/gsm_04_80.c
+++ b/openbsc/src/gsm_04_80.c
@@ -257,7 +257,6 @@ int gsm0480_send_ussd_response(const struct msgb *in_msg, const char *response_t
if (((strlen(response_text) * 7) % 8) != 0)
response_len += 1;
- msg->bts_link = in_msg->bts_link;
msg->lchan = in_msg->lchan;
/* First put the payload text into the message */
@@ -304,7 +303,6 @@ int gsm0480_send_ussd_reject(const struct msgb *in_msg,
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh;
- msg->bts_link = in_msg->bts_link;
msg->lchan = in_msg->lchan;
/* First insert the problem code */
diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
index 176367dc7..a4b321266 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -171,6 +171,10 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
return trx;
}
+static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 };
+static const uint8_t bts_cell_timer_default[] =
+ { 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 };
+
struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
u_int8_t tsc, u_int8_t bsic)
{
@@ -212,6 +216,10 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
bts->gprs.nsvc[i].bts = bts;
bts->gprs.nsvc[i].id = i;
}
+ memcpy(&bts->gprs.nse.timer, bts_nse_timer_default,
+ sizeof(bts->gprs.nse.timer));
+ memcpy(&bts->gprs.cell.timer, bts_cell_timer_default,
+ sizeof(bts->gprs.cell.timer));
/* create our primary TRX */
bts->c0 = gsm_bts_trx_alloc(bts);
@@ -221,6 +229,8 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
}
bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4;
+ bts->rach_b_thresh = -1;
+ bts->rach_ldavg_slots = -1;
llist_add_tail(&bts->list, &net->bts_list);
return bts;
@@ -280,6 +290,10 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c
net->stats.call.dialled = counter_alloc("net.call.dialled");
net->stats.call.alerted = counter_alloc("net.call.alerted");
net->stats.call.connected = counter_alloc("net.call.connected");
+ net->stats.chan.rf_fail = counter_alloc("net.chan.rf_fail");
+ net->stats.chan.rll_err = counter_alloc("net.chan.rll_err");
+ net->stats.bts.oml_fail = counter_alloc("net.bts.oml_fail");
+ net->stats.bts.rsl_fail = counter_alloc("net.bts.rsl_fail");
net->mncc_recv = mncc_recv;
@@ -436,33 +450,6 @@ const char *gsm_auth_policy_name(enum gsm_auth_policy policy)
return get_value_string(auth_policy_names, policy);
}
-/* this should not be here but in gsm_04_08... but that creates
- in turn a dependency nightmare (abis_nm depending on 04_08, ...) */
-static int gsm48_construct_ra(u_int8_t *buf, const struct gprs_ra_id *raid)
-{
- u_int16_t mcc = raid->mcc;
- u_int16_t mnc = raid->mnc;
-
- buf[0] = ((mcc / 100) % 10) | (((mcc / 10) % 10) << 4);
- buf[1] = (mcc % 10);
-
- /* I wonder who came up with the stupidity of encoding the MNC
- * differently depending on how many digits its decimal number has! */
- if (mnc < 100) {
- buf[1] |= 0xf0;
- buf[2] = ((mnc / 10) % 10) | ((mnc % 10) << 4);
- } else {
- buf[1] |= (mnc % 10) << 4;
- buf[2] = ((mnc / 100) % 10) | (((mcc / 10) % 10) << 4);
- }
-
- *(u_int16_t *)(buf+3) = htons(raid->lac);
-
- buf[5] = raid->rac;
-
- return 6;
-}
-
void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts)
{
raid->mcc = bts->network->country_code;
@@ -498,6 +485,23 @@ const char *rrlp_mode_name(enum rrlp_mode mode)
return get_value_string(rrlp_mode_names, mode);
}
+static const struct value_string bts_gprs_mode_names[] = {
+ { BTS_GPRS_NONE, "none" },
+ { BTS_GPRS_GPRS, "gprs" },
+ { BTS_GPRS_EGPRS, "egprs" },
+ { 0, NULL }
+};
+
+enum bts_gprs_mode bts_gprs_mode_parse(const char *arg)
+{
+ return get_string_value(bts_gprs_mode_names, arg);
+}
+
+const char *bts_gprs_mode_name(enum bts_gprs_mode mode)
+{
+ return get_value_string(bts_gprs_mode_names, mode);
+}
+
struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan)
{
struct gsm_meas_rep *meas_rep;
diff --git a/openbsc/src/handover_logic.c b/openbsc/src/handover_logic.c
index 7fb0b13e1..b2ffe4616 100644
--- a/openbsc/src/handover_logic.c
+++ b/openbsc/src/handover_logic.c
@@ -134,6 +134,7 @@ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts)
return rc;
}
+ rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ);
llist_add(&ho->list, &bsc_handovers);
/* we continue in the SS_LCHAN handler / ho_chan_activ_ack */
@@ -229,7 +230,7 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
/* update lchan pointer of transaction */
trans_lchan_change(&ho->old_lchan->conn, &new_lchan->conn);
- ho->old_lchan->state = LCHAN_S_INACTIVE;
+ rsl_lchan_set_state(ho->old_lchan, LCHAN_S_INACTIVE);
lchan_auto_release(ho->old_lchan);
/* do something to re-route the actual speech frames ! */
diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c
index 323540f48..721cadd23 100644
--- a/openbsc/src/input/ipaccess.c
+++ b/openbsc/src/input/ipaccess.c
@@ -1,6 +1,8 @@
/* OpenBSC Abis input driver for ip.access */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Holger Hans Peter Freyther
+ * (C) 2010 by On-Waves
*
* All Rights Reserved
*
@@ -57,7 +59,7 @@ struct ia_e1_handle {
static struct ia_e1_handle *e1h;
-#define TS1_ALLOC_SIZE 300
+#define TS1_ALLOC_SIZE 900
static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG };
static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK };
@@ -234,6 +236,8 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
}
DEBUGP(DINP, "Identified BTS %u/%u/%u\n", site_id, bts_id, trx_id);
if (bfd->priv_nr == PRIV_OML) {
+ /* drop any old oml connection */
+ ipaccess_drop_oml(bts);
bts->oml_link = e1inp_sign_link_create(&line->ts[PRIV_OML - 1],
E1INP_SIGN_OML, bts->c0,
bts->oml_tei, 0);
@@ -241,7 +245,18 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
struct e1inp_ts *e1i_ts;
struct bsc_fd *newbfd;
struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_id);
-
+
+ /* drop any old rsl connection */
+ ipaccess_drop_rsl(trx);
+
+ if (!bts->oml_link) {
+ bsc_unregister_fd(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+ talloc_free(bfd);
+ return 0;
+ }
+
bfd->data = line = bts->oml_link->ts->line;
e1i_ts = &line->ts[PRIV_RSL + trx_id - 1];
newbfd = &e1i_ts->driver.ipaccess.fd;
@@ -251,19 +266,13 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
E1INP_SIGN_RSL, trx,
trx->rsl_tei, 0);
- if (newbfd->fd >= 0) {
- LOGP(DINP, LOGL_ERROR, "BTS is still registered. Closing old connection.\n");
- bsc_unregister_fd(newbfd);
- close(newbfd->fd);
- newbfd->fd = -1;
- }
-
/* get rid of our old temporary bfd */
memcpy(newbfd, bfd, sizeof(*newbfd));
newbfd->priv_nr = PRIV_RSL + trx_id;
bsc_unregister_fd(bfd);
- bsc_register_fd(newbfd);
+ bfd->fd = -1;
talloc_free(bfd);
+ bsc_register_fd(newbfd);
}
break;
}
@@ -328,6 +337,103 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
return msg;
}
+int ipaccess_drop_oml(struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
+ struct e1inp_ts *ts;
+ struct e1inp_line *line;
+ struct bsc_fd *bfd;
+
+ if (!bts || !bts->oml_link)
+ return -1;
+
+ /* send OML down */
+ ts = bts->oml_link->ts;
+ line = ts->line;
+ e1inp_event(ts, EVT_E1_TEI_DN, bts->oml_link->tei, bts->oml_link->sapi);
+
+ bfd = &ts->driver.ipaccess.fd;
+ bsc_unregister_fd(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+
+ /* clean up OML and RSL */
+ e1inp_sign_link_destroy(bts->oml_link);
+ bts->oml_link = NULL;
+ bts->ip_access.flags = 0;
+
+ /* drop all RSL connections too */
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ ipaccess_drop_rsl(trx);
+
+ /* kill the E1 line now... as we have no one left to use it */
+ talloc_free(line);
+
+ return -1;
+}
+
+static int ipaccess_drop(struct e1inp_ts *ts, struct bsc_fd *bfd)
+{
+ struct e1inp_sign_link *link;
+ int bts_nr;
+
+ if (!ts) {
+ /*
+ * If we don't have a TS this means that this is a RSL
+ * connection but we are not past the authentication
+ * handling yet. So we can safely delete this bfd and
+ * wait for a reconnect.
+ */
+ bsc_unregister_fd(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+ talloc_free(bfd);
+ return -1;
+ }
+
+ /* attempt to find a signalling link */
+ if (ts->type == E1INP_TS_TYPE_SIGN) {
+ llist_for_each_entry(link, &ts->sign.sign_links, list) {
+ bts_nr = link->trx->bts->bts_nr;
+ /* we have issues just reconnecting RLS so we drop OML */
+ ipaccess_drop_oml(link->trx->bts);
+ return bts_nr;
+ }
+ }
+
+ /* error case */
+ LOGP(DINP, LOGL_ERROR, "Failed to find a signalling link for ts: %p\n", ts);
+ bsc_unregister_fd(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+ return -1;
+}
+
+int ipaccess_drop_rsl(struct gsm_bts_trx *trx)
+{
+ struct bsc_fd *bfd;
+ struct e1inp_ts *ts;
+
+ if (!trx || !trx->rsl_link)
+ return -1;
+
+ /* send RSL down */
+ ts = trx->rsl_link->ts;
+ e1inp_event(ts, EVT_E1_TEI_DN, trx->rsl_link->tei, trx->rsl_link->sapi);
+
+ /* close the socket */
+ bfd = &ts->driver.ipaccess.fd;
+ bsc_unregister_fd(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+
+ /* destroy */
+ e1inp_sign_link_destroy(trx->rsl_link);
+ trx->rsl_link = NULL;
+
+ return -1;
+}
+
static int handle_ts1_read(struct bsc_fd *bfd)
{
struct e1inp_line *line = bfd->data;
@@ -341,18 +447,12 @@ static int handle_ts1_read(struct bsc_fd *bfd)
msg = ipaccess_read_msg(bfd, &error);
if (!msg) {
if (error == 0) {
- link = e1inp_lookup_sign_link(e1i_ts, IPAC_PROTO_OML, 0);
- if (link) {
- link->trx->bts->ip_access.flags = 0;
+ int ret = ipaccess_drop(e1i_ts, bfd);
+ if (ret >= 0)
LOGP(DINP, LOGL_NOTICE, "BTS %u disappeared, dead socket\n",
- link->trx->bts->nr);
- } else
+ ret);
+ else
LOGP(DINP, LOGL_NOTICE, "unknown BTS disappeared, dead socket\n");
- e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL);
- e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML);
- bsc_unregister_fd(bfd);
- close(bfd->fd);
- bfd->fd = -1;
}
return error;
}
@@ -362,13 +462,8 @@ static int handle_ts1_read(struct bsc_fd *bfd)
hh = (struct ipaccess_head *) msg->data;
if (hh->proto == IPAC_PROTO_IPACCESS) {
ret = ipaccess_rcvmsg(line, msg, bfd);
- if (ret < 0) {
- e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL);
- e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML);
- bsc_unregister_fd(bfd);
- close(bfd->fd);
- bfd->fd = -1;
- }
+ if (ret < 0)
+ ipaccess_drop(e1i_ts, bfd);
msgb_free(msg);
return ret;
}
@@ -475,7 +570,9 @@ static int handle_ts1_write(struct bsc_fd *bfd)
/* set tx delay timer for next event */
e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
e1i_ts->sign.tx_timer.data = e1i_ts;
- bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 100);
+
+ /* Reducing this might break the nanoBTS 900 init. */
+ bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 100000);
return ret;
}
@@ -505,7 +602,6 @@ static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
return rc;
}
-
struct e1inp_driver ipaccess_driver = {
.name = "ip.access",
.want_write = ts_want_write,
@@ -611,53 +707,6 @@ static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
return 0;
}
-static int make_sock(struct bsc_fd *bfd, u_int16_t port,
- int (*cb)(struct bsc_fd *fd, unsigned int what))
-{
- struct sockaddr_in addr;
- int ret, on = 1;
-
- bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- bfd->cb = cb;
- bfd->when = BSC_FD_READ;
- //bfd->data = line;
-
- if (bfd->fd < 0) {
- LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n");
- return -EIO;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = INADDR_ANY;
-
- setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
- ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
- if (ret < 0) {
- LOGP(DINP, LOGL_ERROR, "could not bind l2 socket %s\n",
- strerror(errno));
- close(bfd->fd);
- return -EIO;
- }
-
- ret = listen(bfd->fd, 1);
- if (ret < 0) {
- perror("listen");
- close(bfd->fd);
- return ret;
- }
-
- ret = bsc_register_fd(bfd);
- if (ret < 0) {
- perror("register_listen_fd");
- close(bfd->fd);
- return ret;
- }
- return 0;
-}
-
/* Actively connect to a BTS. Currently used by ipaccess-config.c */
int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa)
{
@@ -714,12 +763,16 @@ int ipaccess_setup(struct gsm_network *gsmnet)
e1h->gsmnet = gsmnet;
/* Listen for OML connections */
- ret = make_sock(&e1h->listen_fd, IPA_TCP_PORT_OML, listen_fd_cb);
+ ret = make_sock(&e1h->listen_fd, IPPROTO_TCP, IPA_TCP_PORT_OML,
+ listen_fd_cb);
if (ret < 0)
return ret;
/* Listen for RSL connections */
- ret = make_sock(&e1h->rsl_listen_fd, IPA_TCP_PORT_RSL, rsl_listen_fd_cb);
+ ret = make_sock(&e1h->rsl_listen_fd, IPPROTO_TCP,
+ IPA_TCP_PORT_RSL, rsl_listen_fd_cb);
+ if (ret < 0)
+ return ret;
return ret;
}
diff --git a/openbsc/src/ipaccess/Makefile.am b/openbsc/src/ipaccess/Makefile.am
new file mode 100644
index 000000000..533932142
--- /dev/null
+++ b/openbsc/src/ipaccess/Makefile.am
@@ -0,0 +1,13 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
+AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
+AM_LDFLAGS = $(LIBOSMOCORE_LIBS)
+
+sbin_PROGRAMS = ipaccess-find ipaccess-config ipaccess-proxy
+
+ipaccess_find_SOURCES = ipaccess-find.c
+
+ipaccess_config_SOURCES = ipaccess-config.c ipaccess-firmware.c
+ipaccess_config_LDADD = $(top_builddir)/src/libbsc.a $(top_builddir)/src/libmsc.a \
+ $(top_builddir)/src/libbsc.a $(top_builddir)/src/libvty.a -ldl -ldbi $(LIBCRYPT)
+
+ipaccess_proxy_SOURCES = ipaccess-proxy.c ../debug.c
diff --git a/openbsc/src/ipaccess/ipaccess-config.c b/openbsc/src/ipaccess/ipaccess-config.c
index 670e3f11a..f27e51fdd 100644
--- a/openbsc/src/ipaccess/ipaccess-config.c
+++ b/openbsc/src/ipaccess/ipaccess-config.c
@@ -1,8 +1,8 @@
/* ip.access nanoBTS configuration tool */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009 by Holger Hans Peter Freyther
- * (C) 2009 by On Waves
+ * (C) 2009,2010 by Holger Hans Peter Freyther
+ * (C) 2009,2010 by On Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -59,6 +59,7 @@ static int sw_load_state = 0;
static int oml_state = 0;
static int dump_files = 0;
static char *firmware_analysis = NULL;
+static int found_trx = 0;
struct sw_load {
u_int8_t file_id[255];
@@ -91,23 +92,23 @@ static int ipacc_msg_nack(u_int8_t mt)
return 0;
}
-static int ipacc_msg_ack(u_int8_t mt, struct gsm_bts *bts)
+static void check_restart_or_exit(struct gsm_bts_trx *trx)
+{
+ if (restart) {
+ abis_nm_ipaccess_restart(trx);
+ } else {
+ exit(0);
+ }
+}
+
+static int ipacc_msg_ack(u_int8_t mt, struct gsm_bts_trx *trx)
{
if (sw_load_state == 1) {
fprintf(stderr, "The new software is activaed.\n");
-
- if (restart) {
- abis_nm_ipaccess_restart(bts);
- } else {
- exit(0);
- }
+ check_restart_or_exit(trx);
} else if (oml_state == 1) {
fprintf(stderr, "Set the primary OML IP.\n");
- if (restart) {
- abis_nm_ipaccess_restart(bts);
- } else {
- exit(0);
- }
+ check_restart_or_exit(trx);
}
return 0;
@@ -202,7 +203,7 @@ static int nm_sig_cb(unsigned int subsys, unsigned int signal,
return ipacc_msg_nack(ipacc_data->msg_type);
case S_NM_IPACC_ACK:
ipacc_data = signal_data;
- return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->bts);
+ return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->trx);
case S_NM_TEST_REP:
return test_rep(signal_data);
case S_NM_IPACC_RESTART_ACK:
@@ -227,12 +228,12 @@ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg,
void *data, void *param)
{
struct msgb *msg;
- struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
if (hook != GSM_HOOK_NM_SWLOAD)
return 0;
- bts = (struct gsm_bts *) data;
+ trx = (struct gsm_bts_trx *) data;
switch (event) {
case NM_MT_LOAD_INIT_ACK:
@@ -271,7 +272,7 @@ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg,
msg->l2h[1] = msgb_l3len(msg) >> 8;
msg->l2h[2] = msgb_l3len(msg) & 0xff;
printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg));
- abis_nm_ipaccess_set_nvattr(bts->c0, msg->l2h, msgb_l2len(msg));
+ abis_nm_ipaccess_set_nvattr(trx, msg->l2h, msgb_l2len(msg));
msgb_free(msg);
break;
case NM_MT_LOAD_END_NACK:
@@ -285,7 +286,7 @@ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg,
case NM_MT_ACTIVATE_SW_ACK:
break;
case NM_MT_LOAD_SEG_ACK:
- percent = abis_nm_software_load_status(bts);
+ percent = abis_nm_software_load_status(trx->bts);
if (percent > percent_old)
printf("Software Download Progress: %d%%\n", percent);
percent_old = percent;
@@ -298,13 +299,13 @@ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg,
return 0;
}
-static void bootstrap_om(struct gsm_bts *bts)
+static void bootstrap_om(struct gsm_bts_trx *trx)
{
int len;
static u_int8_t buf[1024];
u_int8_t *cur = buf;
- printf("OML link established\n");
+ printf("OML link established using TRX %d\n", trx->nr);
if (unit_id) {
len = strlen(unit_id);
@@ -316,7 +317,7 @@ static void bootstrap_om(struct gsm_bts *bts)
memcpy(buf+3, unit_id, len);
buf[3+len] = 0;
printf("setting Unit ID to '%s'\n", unit_id);
- abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len+1);
+ abis_nm_ipaccess_set_nvattr(trx, buf, 3+len+1);
}
if (prim_oml_ip) {
struct in_addr ia;
@@ -340,7 +341,7 @@ static void bootstrap_om(struct gsm_bts *bts)
*cur++ = 0;
printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia));
oml_state = 1;
- abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len);
+ abis_nm_ipaccess_set_nvattr(trx, buf, 3+len);
}
if (nv_mask) {
len = 4;
@@ -354,12 +355,12 @@ static void bootstrap_om(struct gsm_bts *bts)
*cur++ = nv_mask >> 8;
printf("setting NV Flags/Mask to 0x%04x/0x%04x\n",
nv_flags, nv_mask);
- abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len);
+ abis_nm_ipaccess_set_nvattr(trx, buf, 3+len);
}
if (restart && !prim_oml_ip && !software) {
printf("restarting BTS\n");
- abis_nm_ipaccess_restart(bts);
+ abis_nm_ipaccess_restart(trx);
}
}
@@ -370,7 +371,6 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
case EVT_E1_TEI_UP:
switch (type) {
case E1INP_SIGN_OML:
- bootstrap_om(trx->bts);
break;
case E1INP_SIGN_RSL:
/* FIXME */
@@ -389,22 +389,29 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
}
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
- struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
+ struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
+ struct abis_om_obj_inst *obj_inst)
{
- if (evt == EVT_STATECHG_OPER &&
+ if (obj_class == NM_OC_BASEB_TRANSC) {
+ if (!found_trx && obj_inst->trx_nr != 0xff) {
+ struct gsm_bts_trx *trx = container_of(obj, struct gsm_bts_trx, bb_transc);
+ bootstrap_om(trx);
+ found_trx = 1;
+ }
+ } else if (evt == EVT_STATECHG_OPER &&
obj_class == NM_OC_RADIO_CARRIER &&
new_state->availability == 3) {
struct gsm_bts_trx *trx = obj;
if (net_listen_testnr) {
u_int8_t phys_config[] = { 0x02, 0x0a, 0x00, 0x01, 0x02 };
- abis_nm_perform_test(trx->bts, 2, 0, 0, 0xff,
+ abis_nm_perform_test(trx->bts, 2, 0, trx->nr, 0xff,
net_listen_testnr, 1,
phys_config, sizeof(phys_config));
} else if (software) {
int rc;
printf("Attempting software upload with '%s'\n", software);
- rc = abis_nm_software_load(trx->bts, software, 19, 0, swload_cbfn, trx->bts);
+ rc = abis_nm_software_load(trx->bts, trx->nr, software, 19, 0, swload_cbfn, trx);
if (rc < 0) {
fprintf(stderr, "Failed to start software load\n");
exit(-3);
@@ -639,6 +646,7 @@ int main(int argc, char **argv)
{ "software", 1, 0, 'd' },
{ "firmware", 1, 0, 'f' },
{ "write-firmware", 0, 0, 'w' },
+ { 0, 0, 0, 0 },
};
c = getopt_long(argc, argv, "u:o:rn:l:hs:d:f:w", long_options,
diff --git a/openbsc/src/mgcp/mgcp_network.c b/openbsc/src/mgcp/mgcp_network.c
index e9c067ebc..1f51233d3 100644
--- a/openbsc/src/mgcp/mgcp_network.c
+++ b/openbsc/src/mgcp/mgcp_network.c
@@ -151,7 +151,7 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
*/
#warning "Slight spec violation. With connection mode recvonly we should attempt to forward."
dest = memcmp(&addr.sin_addr, &endp->remote, sizeof(addr.sin_addr)) == 0 &&
- (endp->net_rtp == addr.sin_port || endp->net_rtcp == addr.sin_port)
+ (endp->net_rtp == addr.sin_port || endp->net_rtcp == addr.sin_port)
? DEST_BTS : DEST_NETWORK;
proto = fd == &endp->local_rtp ? PROTO_RTP : PROTO_RTCP;
diff --git a/openbsc/src/mgcp/mgcp_vty.c b/openbsc/src/mgcp/mgcp_vty.c
index b05888284..38d3daf5f 100644
--- a/openbsc/src/mgcp/mgcp_vty.c
+++ b/openbsc/src/mgcp/mgcp_vty.c
@@ -286,8 +286,8 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg)
/*
* This application supports two modes.
- * 1.) a true MGCP gateway with support for AUEP, CRCX, MDCX, DLCX
- * 2.) plain forwarding of RTP packets on the endpoints.
+ * 1.) a true MGCP gateway with support for AUEP, CRCX, MDCX, DLCX
+ * 2.) plain forwarding of RTP packets on the endpoints.
* both modes are mutual exclusive
*/
if (g_cfg->forward_ip) {
diff --git a/openbsc/src/openbsc.cfg.nanobts b/openbsc/src/openbsc.cfg.nanobts
index a1ceaec79..da0ba74e1 100644
--- a/openbsc/src/openbsc.cfg.nanobts
+++ b/openbsc/src/openbsc.cfg.nanobts
@@ -1,6 +1,6 @@
!
! OpenBSC configuration saved from vty
-!
+! !
password foo
!
line vty
@@ -11,17 +11,52 @@ network
mobile network code 1
short name OpenBSC
long name OpenBSC
+ auth policy closed
+ location updating reject cause 13
+ encryption a5 0
+ neci 0
+ rrlp mode none
+ mm info 1
+ handover 0
+ handover window rxlev averaging 10
+ handover window rxqual averaging 1
+ handover window rxlev neighbor averaging 10
+ handover power budget interval 6
+ handover power budget hysteresis 3
+ handover maximum distance 9999
timer t3101 10
+ timer t3103 0
+ timer t3105 0
+ timer t3107 0
+ timer t3109 0
+ timer t3111 0
timer t3113 60
+ timer t3115 0
+ timer t3117 0
+ timer t3119 0
+ timer t3141 0
bts 0
type nanobts
- ip.access unit_id 1801 0
- band GSM1800
+ band DCS1800
+ cell_identity 0
location_area_code 1
training_sequence_code 7
base_station_id_code 63
+ ms max power 15
+ cell reselection hysteresis 4
+ rxlev access min 0
+ channel allocator ascending
+ rach tx integer 9
+ rach max transmission 7
+ ip.access unit_id 1801 0
+ oml ip.access stream_id 255
+ gprs mode none
trx 0
+ rf_locked 0
arfcn 514
+ nominal power 23
+ max_power_red 20
+ rsl e1 tei 0
timeslot 0
phys_chan_config CCCH+SDCCH4
timeslot 1
diff --git a/openbsc/src/openbsc.cfg.nanobts.multitrx b/openbsc/src/openbsc.cfg.nanobts.multitrx
new file mode 100644
index 000000000..6e27ff514
--- /dev/null
+++ b/openbsc/src/openbsc.cfg.nanobts.multitrx
@@ -0,0 +1,97 @@
+!
+! OpenBSC configuration saved from vty
+! !
+password foo
+!
+line vty
+ no login
+!
+network
+ network country code 1
+ mobile network code 1
+ short name OpenBSC
+ long name OpenBSC
+ auth policy closed
+ location updating reject cause 13
+ encryption a5 0
+ neci 0
+ rrlp mode none
+ mm info 0
+ handover 0
+ handover window rxlev averaging 10
+ handover window rxqual averaging 1
+ handover window rxlev neighbor averaging 10
+ handover power budget interval 6
+ handover power budget hysteresis 3
+ handover maximum distance 9999
+ timer t3101 10
+ timer t3103 0
+ timer t3105 0
+ timer t3107 0
+ timer t3109 0
+ timer t3111 0
+ timer t3113 60
+ timer t3115 0
+ timer t3117 0
+ timer t3119 0
+ timer t3141 0
+ bts 0
+ type nanobts
+ band DCS1800
+ cell_identity 0
+ location_area_code 1
+ training_sequence_code 7
+ base_station_id_code 63
+ ms max power 15
+ cell reselection hysteresis 4
+ rxlev access min 0
+ channel allocator ascending
+ rach tx integer 9
+ rach max transmission 7
+ ip.access unit_id 1800 0
+ oml ip.access stream_id 255
+ gprs mode none
+ trx 0
+ rf_locked 0
+ arfcn 871
+ nominal power 23
+ max_power_red 0
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ timeslot 1
+ phys_chan_config SDCCH8
+ timeslot 2
+ phys_chan_config TCH/F
+ timeslot 3
+ phys_chan_config TCH/F
+ timeslot 4
+ phys_chan_config TCH/F
+ timeslot 5
+ phys_chan_config TCH/F
+ timeslot 6
+ phys_chan_config TCH/F
+ timeslot 7
+ phys_chan_config TCH/F
+ trx 1
+ rf_locked 0
+ arfcn 873
+ nominal power 23
+ max_power_red 0
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config SDCCH8
+ timeslot 1
+ phys_chan_config TCH/F
+ timeslot 2
+ phys_chan_config TCH/F
+ timeslot 3
+ phys_chan_config TCH/F
+ timeslot 4
+ phys_chan_config TCH/F
+ timeslot 5
+ phys_chan_config TCH/F
+ timeslot 6
+ phys_chan_config TCH/F
+ timeslot 7
+ phys_chan_config TCH/F
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index 7c3750d66..12ed90341 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -45,6 +45,7 @@
#include <openbsc/signal.h>
#include <openbsc/abis_rsl.h>
#include <openbsc/gsm_data.h>
+#include <openbsc/chan_alloc.h>
void *tall_paging_ctx;
@@ -70,14 +71,6 @@ static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber *
static void paging_remove_request(struct gsm_bts_paging_state *paging_bts,
struct gsm_paging_request *to_be_deleted)
{
- /* Update the last_request if that is necessary */
- if (to_be_deleted == paging_bts->last_request) {
- paging_bts->last_request =
- (struct gsm_paging_request *)paging_bts->last_request->entry.next;
- if (&to_be_deleted->entry == &paging_bts->pending_requests)
- paging_bts->last_request = NULL;
- }
-
bsc_del_timer(&to_be_deleted->T3113);
llist_del(&to_be_deleted->entry);
subscr_put(to_be_deleted->subscr);
@@ -90,7 +83,7 @@ static void page_ms(struct gsm_paging_request *request)
unsigned int mi_len;
unsigned int page_group;
- DEBUGP(DPAG, "Going to send paging commands: imsi: '%s' tmsi: '0x%x'\n",
+ LOGP(DPAG, LOGL_INFO, "Going to send paging commands: imsi: '%s' tmsi: '0x%x'\n",
request->subscr->imsi, request->subscr->tmsi);
if (request->subscr->tmsi == GSM_RESERVED_TMSI)
@@ -103,14 +96,6 @@ static void page_ms(struct gsm_paging_request *request)
request->chan_type);
}
-static void paging_move_to_next(struct gsm_bts_paging_state *paging_bts)
-{
- paging_bts->last_request =
- (struct gsm_paging_request *)paging_bts->last_request->entry.next;
- if (&paging_bts->last_request->entry == &paging_bts->pending_requests)
- paging_bts->last_request = NULL;
-}
-
/*
* This is kicked by the periodic PAGING LOAD Indicator
* coming from abis_rsl.c
@@ -128,17 +113,26 @@ static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_b
* return then.
*/
if (llist_empty(&paging_bts->pending_requests)) {
- paging_bts->last_request = NULL;
/* since the list is empty, no need to reschedule the timer */
return;
}
- if (!paging_bts->last_request)
- paging_bts->last_request =
- (struct gsm_paging_request *)paging_bts->pending_requests.next;
+ /*
+ * In case the BTS does not provide us with load indication just fill
+ * up our slots for this round. We should be able to page 20 subscribers
+ * every two seconds. So we will just give the BTS some extra credit.
+ * We will have to see how often we run out of this credit, so we might
+ * need a low watermark and then add credit or give 20 every run when
+ * the bts sets an option for that.
+ */
+ if (paging_bts->available_slots == 0) {
+ LOGP(DPAG, LOGL_NOTICE, "No slots available on bts nr %d\n",
+ paging_bts->bts->nr);
+ paging_bts->available_slots = 20;
+ }
- assert(paging_bts->last_request);
- initial_request = paging_bts->last_request;
+ initial_request = llist_entry(paging_bts->pending_requests.next,
+ struct gsm_paging_request, entry);
current_request = initial_request;
do {
@@ -146,21 +140,17 @@ static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_b
page_ms(current_request);
paging_bts->available_slots--;
- /*
- * move to the next item. We might wrap around
- * this means last_request will be NULL and we just
- * call paging_page_to_next again. It it guranteed
- * that the list is not empty.
- */
- paging_move_to_next(paging_bts);
- if (!paging_bts->last_request)
- paging_bts->last_request =
- (struct gsm_paging_request *)paging_bts->pending_requests.next;
- current_request = paging_bts->last_request;
+ /* take the current and add it to the back */
+ llist_del(&current_request->entry);
+ llist_add_tail(&current_request->entry, &paging_bts->pending_requests);
+
+ /* take the next request */
+ current_request = llist_entry(paging_bts->pending_requests.next,
+ struct gsm_paging_request, entry);
} while (paging_bts->available_slots > 0
&& initial_request != current_request);
- bsc_schedule_timer(&paging_bts->work_timer, 1, 0);
+ bsc_schedule_timer(&paging_bts->work_timer, 2, 0);
}
static void paging_worker(void *data)
@@ -178,7 +168,7 @@ void paging_init(struct gsm_bts *bts)
bts->paging.work_timer.data = &bts->paging;
/* Large number, until we get a proper message */
- bts->paging.available_slots = 100;
+ bts->paging.available_slots = 20;
}
static int paging_pending_request(struct gsm_bts_paging_state *bts,
@@ -200,7 +190,7 @@ static void paging_T3113_expired(void *data)
void *cbfn_param;
gsm_cbfn *cbfn;
- DEBUGP(DPAG, "T3113 expired for request %p (%s)\n",
+ LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n",
req, req->subscr->imsi);
sig_data.subscr = req->subscr;
@@ -208,11 +198,11 @@ static void paging_T3113_expired(void *data)
sig_data.lchan = NULL;
/* must be destroyed before calling cbfn, to prevent double free */
+ counter_inc(req->bts->network->stats.paging.expired);
cbfn_param = req->cbfn_param;
cbfn = req->cbfn;
paging_remove_request(&req->bts->paging, req);
- counter_inc(req->bts->network->stats.paging.expired);
dispatch_signal(SS_PAGING, S_PAGING_EXPIRED, &sig_data);
if (cbfn)
@@ -227,11 +217,11 @@ static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
struct gsm_paging_request *req;
if (paging_pending_request(bts_entry, subscr)) {
- DEBUGP(DPAG, "Paging request already pending\n");
+ LOGP(DPAG, LOGL_INFO, "Paging request already pending for %s\n", subscr->imsi);
return -EEXIST;
}
- DEBUGP(DPAG, "Start paging of subscriber %llu on bts %d.\n",
+ LOGP(DPAG, LOGL_DEBUG, "Start paging of subscriber %llu on bts %d.\n",
subscr->id, bts->nr);
req = talloc_zero(tall_paging_ctx, struct gsm_paging_request);
req->subscr = subscr_get(subscr);
@@ -245,7 +235,7 @@ static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
llist_add_tail(&req->entry, &bts_entry->pending_requests);
if (!bsc_timer_pending(&bts_entry->work_timer))
- bsc_schedule_timer(&bts_entry->work_timer, 1, 0);
+ bsc_schedule_timer(&bts_entry->work_timer, 2, 0);
return 0;
}
@@ -296,11 +286,11 @@ static void _paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *sub
entry) {
if (req->subscr == subscr) {
if (lchan && req->cbfn) {
- DEBUGP(DPAG, "Stop paging on bts %d, calling cbfn.\n", bts->nr);
+ LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d, calling cbfn.\n", bts->nr);
req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED,
NULL, lchan, req->cbfn_param);
} else
- DEBUGP(DPAG, "Stop paging on bts %d silently.\n", bts->nr);
+ LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d silently.\n", bts->nr);
paging_remove_request(&bts->paging, req);
break;
}
@@ -328,7 +318,7 @@ void paging_request_stop(struct gsm_bts *_bts, struct gsm_subscriber *subscr,
break;
/* Stop paging */
- if (bts != _bts)
+ if (bts != _bts)
_paging_request_stop(bts, subscr, NULL);
} while (1);
}
diff --git a/openbsc/src/rest_octets.c b/openbsc/src/rest_octets.c
index 16996cec2..039d2c83a 100644
--- a/openbsc/src/rest_octets.c
+++ b/openbsc/src/rest_octets.c
@@ -133,6 +133,7 @@ static int append_lsa_params(struct bitvec *bv,
const struct gsm48_lsa_params *lsa_params)
{
/* FIXME */
+ return -1;
}
/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */
@@ -318,8 +319,31 @@ static int append_gprs_cell_opt(struct bitvec *bv,
/* hard-code no PAN_{DEC,INC,MAX} */
bitvec_set_bit(bv, 0);
- /* no extension information (EDGE) */
- bitvec_set_bit(bv, 0);
+ if (!gco->ext_info_present) {
+ /* no extension information */
+ bitvec_set_bit(bv, 0);
+ } else {
+ /* extension information */
+ bitvec_set_bit(bv, 1);
+ if (!gco->ext_info.egprs_supported) {
+ /* 6bit length of extension */
+ bitvec_set_uint(bv, (1 + 3)-1, 6);
+ /* EGPRS supported in the cell */
+ bitvec_set_bit(bv, 0);
+ } else {
+ /* 6bit length of extension */
+ bitvec_set_uint(bv, (1 + 5 + 3)-1, 6);
+ /* EGPRS supported in the cell */
+ bitvec_set_bit(bv, 1);
+ /* 1bit EGPRS PACKET CHANNEL REQUEST */
+ bitvec_set_bit(bv, gco->ext_info.use_egprs_p_ch_req);
+ /* 4bit BEP PERIOD */
+ bitvec_set_uint(bv, gco->ext_info.bep_period, 4);
+ }
+ bitvec_set_bit(bv, gco->ext_info.pfc_supported);
+ bitvec_set_bit(bv, gco->ext_info.dtm_supported);
+ bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination);
+ }
return 0;
}
@@ -334,7 +358,7 @@ static void append_gprs_pwr_ctrl_pars(struct bitvec *bv,
bitvec_set_uint(bv, pcp->n_avg_i, 4);
}
-/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */
+/* Generate SI13 Rest Octests (04.08 Chapter 10.5.2.37b) */
int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13)
{
struct bitvec bv;
@@ -390,6 +414,11 @@ int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13)
break;
}
}
+ /* 3GPP TS 44.018 Release 6 / 10.5.2.37b */
+ bitvec_set_bit(&bv, H); /* added Release 99 */
+ /* claim our SGSN is compatible with Release 99, as EDGE and EGPRS
+ * was only added in this Release */
+ bitvec_set_bit(&bv, 1);
}
bitvec_spare_padding(&bv, (bv.data_len*8)-1);
return bv.data_len;
diff --git a/openbsc/src/rtp_proxy.c b/openbsc/src/rtp_proxy.c
index 375204e97..924173dd2 100644
--- a/openbsc/src/rtp_proxy.c
+++ b/openbsc/src/rtp_proxy.c
@@ -91,9 +91,6 @@ struct rtp_x_hdr {
#define RTP_VERSION 2
-#define RTP_PT_GSM_FULL 3
-#define RTP_PT_GSM_EFR 97
-
/* decode an rtp frame and create a new buffer with payload */
static int rtp_decode(struct msgb *msg, u_int32_t callref, struct msgb **data)
{
diff --git a/openbsc/src/sccp/sccp.c b/openbsc/src/sccp/sccp.c
index e0fd02e0e..de18614c3 100644
--- a/openbsc/src/sccp/sccp.c
+++ b/openbsc/src/sccp/sccp.c
@@ -81,7 +81,7 @@ static struct sccp_data_callback *_find_ssn(u_int8_t ssn)
/* need to add one */
cb = talloc_zero(tall_sccp_ctx, struct sccp_data_callback);
if (!cb) {
- DEBUGP(DSCCP, "Failed to allocate sccp callback.\n");
+ LOGP(DSCCP, LOGL_ERROR, "Failed to allocate sccp callback.\n");
return NULL;
}
@@ -108,13 +108,13 @@ static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb
u_int8_t length;
if (room <= 0) {
- DEBUGP(DSCCP, "Not enough room for an address: %u\n", room);
+ LOGP(DSCCP, LOGL_ERROR, "Not enough room for an address: %u\n", room);
return -1;
}
length = msgb->l2h[offset];
if (room <= length) {
- DEBUGP(DSCCP, "Not enough room for optional data %u %u\n", room, length);
+ LOGP(DSCCP, LOGL_ERROR, "Not enough room for optional data %u %u\n", room, length);
return -1;
}
@@ -122,7 +122,7 @@ static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb
party = (struct sccp_called_party_address *)(msgb->l2h + offset + 1);
if (party->point_code_indicator) {
if (length <= read + 2) {
- DEBUGP(DSCCP, "POI does not fit %u\n", length);
+ LOGP(DSCCP, LOGL_ERROR, "POI does not fit %u\n", length);
return -1;
}
@@ -133,7 +133,7 @@ static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb
if (party->ssn_indicator) {
if (length <= read + 1) {
- DEBUGP(DSCCP, "SSN does not fit %u\n", length);
+ LOGP(DSCCP, LOGL_ERROR, "SSN does not fit %u\n", length);
return -1;
}
@@ -142,7 +142,7 @@ static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb
}
if (party->global_title_indicator) {
- DEBUGP(DSCCP, "GTI not supported %u\n", *(u_int8_t *)party);
+ LOGP(DSCCP, LOGL_ERROR, "GTI not supported %u\n", *(u_int8_t *)party);
return -1;
}
@@ -156,7 +156,8 @@ static int check_address(struct sccp_address *addr)
if (addr->address.ssn_indicator != 1
|| addr->address.global_title_indicator == 1
|| addr->address.routing_indicator != 1) {
- DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+ LOGP(DSCCP, LOGL_ERROR,
+ "Invalid called address according to 08.06: 0x%x 0x%x\n",
*(u_int8_t *)&addr->address, addr->ssn);
return -1;
}
@@ -176,7 +177,7 @@ static int _sccp_parse_optional_data(const int offset,
return 0;
if (read + 1 >= room) {
- DEBUGP(DSCCP, "no place for length\n");
+ LOGP(DSCCP, LOGL_ERROR, "no place for length\n");
return 0;
}
@@ -185,7 +186,8 @@ static int _sccp_parse_optional_data(const int offset,
if (room <= read) {
- DEBUGP(DSCCP, "no space for the data: type: %d read: %d room: %d l2: %d\n",
+ LOGP(DSCCP, LOGL_ERROR,
+ "no space for the data: type: %d read: %d room: %d l2: %d\n",
type, read, room, msgb_l2len(msgb));
return 0;
}
@@ -214,7 +216,7 @@ int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result *
/* header check */
if (msgb_l2len(msgb) < header_size) {
- DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
msgb_l2len(msgb), header_size);
return -1;
}
@@ -224,7 +226,7 @@ int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result *
return -1;
if (check_address(&result->called) != 0) {
- DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+ LOGP(DSCCP, LOGL_ERROR, "Invalid called address according to 08.06: 0x%x 0x%x\n",
*(u_int8_t *)&result->called.address, result->called.ssn);
return -1;
}
@@ -236,7 +238,7 @@ int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result *
*/
memset(&optional_data, 0, sizeof(optional_data));
if (_sccp_parse_optional_data(optional_offset + req->optional_start, msgb, &optional_data) != 0) {
- DEBUGP(DSCCP, "parsing of optional data failed.\n");
+ LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n");
return -1;
}
@@ -260,14 +262,14 @@ int _sccp_parse_connection_released(struct msgb *msgb, struct sccp_parse_result
/* we don't have enough size for the struct */
if (msgb_l2len(msgb) < header_size) {
- DEBUGP(DSCCP, "msgb > header_size %u %u\n",
+ LOGP(DSCCP, LOGL_ERROR, "msgb > header_size %u %u\n",
msgb_l2len(msgb), header_size);
return -1;
}
memset(&optional_data, 0, sizeof(optional_data));
if (_sccp_parse_optional_data(optional_offset + rls->optional_start, msgb, &optional_data) != 0) {
- DEBUGP(DSCCP, "parsing of optional data failed.\n");
+ LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n");
return -1;
}
@@ -295,7 +297,7 @@ int _sccp_parse_connection_refused(struct msgb *msgb, struct sccp_parse_result *
/* header check */
if (msgb_l2len(msgb) < header_size) {
- DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
msgb_l2len(msgb), header_size);
return -1;
}
@@ -306,7 +308,7 @@ int _sccp_parse_connection_refused(struct msgb *msgb, struct sccp_parse_result *
memset(&optional_data, 0, sizeof(optional_data));
if (_sccp_parse_optional_data(optional_offset + ref->optional_start, msgb, &optional_data) != 0) {
- DEBUGP(DSCCP, "parsing of optional data failed.\n");
+ LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n");
return -1;
}
@@ -333,7 +335,7 @@ int _sccp_parse_connection_confirm(struct msgb *msgb, struct sccp_parse_result *
/* header check */
if (msgb_l2len(msgb) < header_size) {
- DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
msgb_l2len(msgb), header_size);
return -1;
}
@@ -344,7 +346,7 @@ int _sccp_parse_connection_confirm(struct msgb *msgb, struct sccp_parse_result *
memset(&optional_data, 0, sizeof(optional_data));
if (_sccp_parse_optional_data(optional_offset + con->optional_start, msgb, &optional_data) != 0) {
- DEBUGP(DSCCP, "parsing of optional data failed.\n");
+ LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n");
return -1;
}
@@ -366,7 +368,7 @@ int _sccp_parse_connection_release_complete(struct msgb *msgb, struct sccp_parse
/* header check */
if (msgb_l2len(msgb) < header_size) {
- DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
msgb_l2len(msgb), header_size);
return -1;
}
@@ -387,13 +389,13 @@ int _sccp_parse_connection_dt1(struct msgb *msgb, struct sccp_parse_result *resu
/* we don't have enough size for the struct */
if (msgb_l2len(msgb) < header_size) {
- DEBUGP(DSCCP, "msgb > header_size %u %u\n",
+ LOGP(DSCCP, LOGL_ERROR, "msgb > header_size %u %u\n",
msgb_l2len(msgb), header_size);
return -1;
}
if (dt1->segmenting != 0) {
- DEBUGP(DSCCP, "This packet has segmenting, not supported: %d\n", dt1->segmenting);
+ LOGP(DSCCP, LOGL_ERROR, "This packet has segmenting, not supported: %d\n", dt1->segmenting);
return -1;
}
@@ -401,7 +403,7 @@ int _sccp_parse_connection_dt1(struct msgb *msgb, struct sccp_parse_result *resu
/* some more size checks in here */
if (msgb_l2len(msgb) < variable_offset + dt1->variable_start + 1) {
- DEBUGP(DSCCP, "Not enough space for variable start: %u %u\n",
+ LOGP(DSCCP, LOGL_ERROR, "Not enough space for variable start: %u %u\n",
msgb_l2len(msgb), dt1->variable_start);
return -1;
}
@@ -410,7 +412,7 @@ int _sccp_parse_connection_dt1(struct msgb *msgb, struct sccp_parse_result *resu
msgb->l3h = &msgb->l2h[dt1->variable_start + variable_offset + 1];
if (msgb_l3len(msgb) < result->data_len) {
- DEBUGP(DSCCP, "Not enough room for the payload: %u %u\n",
+ LOGP(DSCCP, LOGL_ERROR, "Not enough room for the payload: %u %u\n",
msgb_l3len(msgb), result->data_len);
return -1;
}
@@ -428,7 +430,7 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result)
struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msgb->l2h;
if (msgb_l2len(msgb) < header_size) {
- DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
msgb_l2len(msgb), header_size);
return -1;
}
@@ -438,7 +440,7 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result)
return -1;
if (check_address(&result->called) != 0) {
- DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+ LOGP(DSCCP, LOGL_ERROR, "Invalid called address according to 08.06: 0x%x 0x%x\n",
*(u_int8_t *)&result->called.address, result->called.ssn);
return -1;
}
@@ -447,13 +449,13 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result)
return -1;
if (check_address(&result->calling) != 0) {
- DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+ LOGP(DSCCP, LOGL_ERROR, "Invalid called address according to 08.06: 0x%x 0x%x\n",
*(u_int8_t *)&result->called.address, result->called.ssn);
}
/* we don't have enough size for the data */
if (msgb_l2len(msgb) < data_offset + udt->variable_data + 1) {
- DEBUGP(DSCCP, "msgb < header + offset %u %u %u\n",
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header + offset %u %u %u\n",
msgb_l2len(msgb), header_size, udt->variable_data);
return -1;
}
@@ -463,7 +465,7 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result)
result->data_len = msgb_l3len(msgb);
if (msgb_l3len(msgb) != msgb->l3h[-1]) {
- DEBUGP(DSCCP, "msgb is truncated is: %u should: %u\n",
+ LOGP(DSCCP, LOGL_ERROR, "msgb is truncated is: %u should: %u\n",
msgb_l3len(msgb), msgb->l3h[-1]);
return -1;
}
@@ -478,7 +480,7 @@ static int _sccp_parse_it(struct msgb *msgb, struct sccp_parse_result *result)
struct sccp_data_it *it;
if (msgb_l2len(msgb) < header_size) {
- DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
msgb_l2len(msgb), header_size);
return -1;
}
@@ -490,6 +492,23 @@ static int _sccp_parse_it(struct msgb *msgb, struct sccp_parse_result *result)
return 0;
}
+static int _sccp_parse_err(struct msgb *msgb, struct sccp_parse_result *result)
+{
+ static const u_int32_t header_size = sizeof(struct sccp_proto_err);
+
+ struct sccp_proto_err *err;
+
+ if (msgb_l2len(msgb) < header_size) {
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ err = (struct sccp_proto_err *) msgb->l2h;
+ result->data_len = 0;
+ result->destination_local_reference = &err->destination_local_reference;
+ return 0;
+}
/*
* Send UDT. Currently we have a fixed address...
@@ -501,7 +520,7 @@ static int _sccp_send_data(int class, const struct sockaddr_sccp *in,
u_int8_t *data;
if (msgb_l3len(payload) > 256) {
- DEBUGP(DSCCP, "The payload is too big for one udt\n");
+ LOGP(DSCCP, LOGL_ERROR, "The payload is too big for one udt\n");
return -1;
}
@@ -546,7 +565,7 @@ static int _sccp_handle_read(struct msgb *msgb)
cb = _find_ssn(result.called.ssn);
if (!cb || !cb->read_cb) {
- DEBUGP(DSCCP, "No routing for UDT for called SSN: %u\n", result.called.ssn);
+ LOGP(DSCCP, LOGL_ERROR, "No routing for UDT for called SSN: %u\n", result.called.ssn);
return -1;
}
@@ -595,7 +614,7 @@ static int assign_source_local_reference(struct sccp_connection *connection)
++last_ref;
/* do not use the reversed word and wrap around */
if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) {
- DEBUGP(DSCCP, "Wrapped searching for a free code\n");
+ LOGP(DSCCP, LOGL_DEBUG, "Wrapped searching for a free code\n");
last_ref = 0;
++wrapped;
}
@@ -606,7 +625,7 @@ static int assign_source_local_reference(struct sccp_connection *connection)
}
} while (wrapped != 2);
- DEBUGP(DSCCP, "Finding a free reference failed\n");
+ LOGP(DSCCP, LOGL_ERROR, "Finding a free reference failed\n");
return -1;
}
@@ -686,13 +705,13 @@ static int _sccp_send_connection_request(struct sccp_connection *connection,
if (msg && (msgb_l3len(msg) < 3 || msgb_l3len(msg) > 130)) {
- DEBUGP(DSCCP, "Invalid amount of data... %d\n", msgb_l3len(msg));
+ LOGP(DSCCP, LOGL_ERROR, "Invalid amount of data... %d\n", msgb_l3len(msg));
return -1;
}
/* try to find a id */
if (assign_source_local_reference(connection) != 0) {
- DEBUGP(DSCCP, "Assigning a local reference failed.\n");
+ LOGP(DSCCP, LOGL_ERROR, "Assigning a local reference failed.\n");
_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_SETUP_ERROR);
return -1;
}
@@ -744,7 +763,7 @@ static int _sccp_send_connection_data(struct sccp_connection *conn, struct msgb
int extra_size;
if (msgb_l3len(_data) < 2 || msgb_l3len(_data) > 256) {
- DEBUGP(DSCCP, "data size too big, segmenting unimplemented.\n");
+ LOGP(DSCCP, LOGL_ERROR, "data size too big, segmenting unimplemented.\n");
return -1;
}
@@ -840,14 +859,14 @@ static int _sccp_handle_connection_request(struct msgb *msgb)
cb = _find_ssn(result.called.ssn);
if (!cb || !cb->accept_cb) {
- DEBUGP(DSCCP, "No routing for CR for called SSN: %u\n", result.called.ssn);
+ LOGP(DSCCP, LOGL_ERROR, "No routing for CR for called SSN: %u\n", result.called.ssn);
return -1;
}
/* check if the system wants this connection */
connection = talloc_zero(tall_sccp_ctx, struct sccp_connection);
if (!connection) {
- DEBUGP(DSCCP, "Allocation failed\n");
+ LOGP(DSCCP, LOGL_ERROR, "Allocation failed\n");
return -1;
}
@@ -859,7 +878,7 @@ static int _sccp_handle_connection_request(struct msgb *msgb)
* one....
*/
if (destination_local_reference_is_free(result.source_local_reference) != 0) {
- DEBUGP(DSCCP, "Need to reject connection with existing reference\n");
+ LOGP(DSCCP, LOGL_ERROR, "Need to reject connection with existing reference\n");
_sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE);
talloc_free(connection);
return -1;
@@ -879,7 +898,7 @@ static int _sccp_handle_connection_request(struct msgb *msgb)
llist_add_tail(&connection->list, &sccp_connections);
if (_sccp_send_connection_confirm(connection) != 0) {
- DEBUGP(DSCCP, "Sending confirm failed... no available source reference?\n");
+ LOGP(DSCCP, LOGL_ERROR, "Sending confirm failed... no available source reference?\n");
_sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE);
_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
@@ -922,7 +941,7 @@ static int _sccp_handle_connection_release_complete(struct msgb *msgb)
}
- DEBUGP(DSCCP, "Release complete of unknown connection\n");
+ LOGP(DSCCP, LOGL_ERROR, "Release complete of unknown connection\n");
return -1;
found:
@@ -950,7 +969,7 @@ static int _sccp_handle_connection_dt1(struct msgb *msgb)
}
}
- DEBUGP(DSCCP, "No connection found for dt1 data\n");
+ LOGP(DSCCP, LOGL_ERROR, "No connection found for dt1 data\n");
return -1;
found:
@@ -1009,7 +1028,7 @@ static int _sccp_handle_connection_released(struct msgb *msgb)
}
- DEBUGP(DSCCP, "Unknown connection was released.\n");
+ LOGP(DSCCP, LOGL_ERROR, "Unknown connection was released.\n");
return -1;
/* we have found a connection */
@@ -1021,7 +1040,7 @@ found:
/* generate a response */
if (_sccp_send_connection_release_complete(conn) != 0) {
- DEBUGP(DSCCP, "Sending release confirmed failed\n");
+ LOGP(DSCCP, LOGL_ERROR, "Sending release confirmed failed\n");
return -1;
}
@@ -1046,7 +1065,7 @@ static int _sccp_handle_connection_refused(struct msgb *msgb)
}
}
- DEBUGP(DSCCP, "Refused but no connection found\n");
+ LOGP(DSCCP, LOGL_ERROR, "Refused but no connection found\n");
return -1;
found:
@@ -1079,7 +1098,7 @@ static int _sccp_handle_connection_confirm(struct msgb *msgb)
}
}
- DEBUGP(DSCCP, "Confirmed but no connection found\n");
+ LOGP(DSCCP, LOGL_ERROR, "Confirmed but no connection found\n");
return -1;
found:
@@ -1108,7 +1127,7 @@ int sccp_system_init(void (*outgoing)(struct msgb *data, void *ctx), void *ctx)
int sccp_system_incoming(struct msgb *msgb)
{
if (msgb_l2len(msgb) < 1 ) {
- DEBUGP(DSCCP, "Too short packet\n");
+ LOGP(DSCCP, LOGL_ERROR, "Too short packet\n");
return -1;
}
@@ -1137,7 +1156,7 @@ int sccp_system_incoming(struct msgb *msgb)
return _sccp_handle_read(msgb);
break;
default:
- DEBUGP(DSCCP, "unimplemented msg type: %d\n", type);
+ LOGP(DSCCP, LOGL_ERROR, "unimplemented msg type: %d\n", type);
};
return -1;
@@ -1148,7 +1167,7 @@ int sccp_connection_write(struct sccp_connection *connection, struct msgb *data)
{
if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
|| connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
- DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n",
+ LOGP(DSCCP, LOGL_ERROR, "sccp_connection_write: Wrong connection state: %p %d\n",
connection, connection->connection_state);
return -1;
}
@@ -1165,7 +1184,7 @@ int sccp_connection_send_it(struct sccp_connection *connection)
{
if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
|| connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
- DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n",
+ LOGP(DSCCP, LOGL_ERROR, "sccp_connection_write: Wrong connection state: %p %d\n",
connection, connection->connection_state);
return -1;
}
@@ -1178,7 +1197,7 @@ int sccp_connection_close(struct sccp_connection *connection, int cause)
{
if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
|| connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
- DEBUGPC(DSCCP, "Can not close the connection. It was never opened: %p %d\n",
+ LOGP(DSCCP, LOGL_ERROR, "Can not close the connection. It was never opened: %p %d\n",
connection, connection->connection_state);
return -1;
}
@@ -1190,7 +1209,7 @@ int sccp_connection_free(struct sccp_connection *connection)
{
if (connection->connection_state > SCCP_CONNECTION_STATE_NONE
&& connection->connection_state < SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
- DEBUGP(DSCCP, "The connection needs to be released before it is freed");
+ LOGP(DSCCP, LOGL_ERROR, "The connection needs to be released before it is freed");
return -1;
}
@@ -1318,6 +1337,9 @@ int sccp_parse_header(struct msgb *msg, struct sccp_parse_result *result)
case SCCP_MSG_TYPE_IT:
return _sccp_parse_it(msg, result);
break;
+ case SCCP_MSG_TYPE_ERR:
+ return _sccp_parse_err(msg, result);
+ break;
};
LOGP(DSCCP, LOGL_ERROR, "Unimplemented MSG Type: 0x%x\n", type);
diff --git a/openbsc/src/socket.c b/openbsc/src/socket.c
new file mode 100644
index 000000000..3ed4d425a
--- /dev/null
+++ b/openbsc/src/socket.c
@@ -0,0 +1,94 @@
+/* OpenBSC sokcet code, taken from Abis input driver for ip.access */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Holger Hans Peter Freyther
+ * (C) 2010 by On-Waves
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+
+#include <osmocore/select.h>
+#include <osmocore/tlv.h>
+#include <osmocore/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <osmocore/talloc.h>
+
+int make_sock(struct bsc_fd *bfd, int proto, u_int16_t port,
+ int (*cb)(struct bsc_fd *fd, unsigned int what))
+{
+ struct sockaddr_in addr;
+ int ret, on = 1;
+ int type = SOCK_STREAM;
+
+ if (proto == IPPROTO_UDP)
+ type = SOCK_DGRAM;
+
+ bfd->fd = socket(AF_INET, type, proto);
+ bfd->cb = cb;
+ bfd->when = BSC_FD_READ;
+ //bfd->data = line;
+
+ if (bfd->fd < 0) {
+ LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n");
+ return -EIO;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
+ if (ret < 0) {
+ LOGP(DINP, LOGL_ERROR, "could not bind l2 socket %s\n",
+ strerror(errno));
+ close(bfd->fd);
+ return -EIO;
+ }
+
+ if (proto != IPPROTO_UDP) {
+ ret = listen(bfd->fd, 1);
+ if (ret < 0) {
+ perror("listen");
+ return ret;
+ }
+ }
+
+ ret = bsc_register_fd(bfd);
+ if (ret < 0) {
+ perror("register_listen_fd");
+ close(bfd->fd);
+ return ret;
+ }
+ return 0;
+}
diff --git a/openbsc/src/system_information.c b/openbsc/src/system_information.c
index 3f9d60954..3bd833a93 100644
--- a/openbsc/src/system_information.c
+++ b/openbsc/src/system_information.c
@@ -402,6 +402,16 @@ static struct gsm48_si13_info si13_default = {
.t3192 = 500,
.drx_timer_max = 3,
.bs_cv_max = 15,
+ .ext_info_present = 0,
+ .ext_info = {
+ /* The values below are just guesses ! */
+ .egprs_supported = 0,
+ .use_egprs_p_ch_req = 1,
+ .bep_period = 4,
+ .pfc_supported = 0,
+ .dtm_supported = 0,
+ .bss_paging_coordination = 0,
+ },
},
.pwr_ctrl_pars = {
.alpha = 10, /* a = 1.0 */
@@ -448,7 +458,18 @@ static int generate_si13(u_int8_t *output, struct gsm_bts *bts)
int gsm_generate_si(u_int8_t *output, struct gsm_bts *bts, int type)
{
- si_info.gprs_ind.present = bts->gprs.enabled;
+ switch (bts->gprs.mode) {
+ case BTS_GPRS_EGPRS:
+ si13_default.cell_opts.ext_info_present = 1;
+ si13_default.cell_opts.ext_info.egprs_supported = 1;
+ /* fallthrough */
+ case BTS_GPRS_GPRS:
+ si_info.gprs_ind.present = 1;
+ break;
+ case BTS_GPRS_NONE:
+ si_info.gprs_ind.present = 0;
+ break;
+ }
switch (type) {
case RSL_SYSTEM_INFO_1:
diff --git a/openbsc/src/vty/buffer.c b/openbsc/src/vty/buffer.c
index 195d06209..0bc1760cc 100644
--- a/openbsc/src/vty/buffer.c
+++ b/openbsc/src/vty/buffer.c
@@ -65,11 +65,11 @@ struct buffer_data {
#define BUFFER_DATA_FREE(D) talloc_free((D))
/* Make new buffer. */
-struct buffer *buffer_new(size_t size)
+struct buffer *buffer_new(void *ctx, size_t size)
{
struct buffer *b;
- b = talloc_zero(tall_vty_ctx, struct buffer);
+ b = talloc_zero(ctx, struct buffer);
if (size)
b->size = size;
@@ -138,7 +138,7 @@ static struct buffer_data *buffer_add(struct buffer *b)
{
struct buffer_data *d;
- d = _talloc_zero(tall_vty_ctx,
+ d = _talloc_zero(b,
offsetof(struct buffer_data, data[b->size]),
"buffer_add");
if (!d)
diff --git a/openbsc/src/vty/command.c b/openbsc/src/vty/command.c
index a38ed0424..a1130b68e 100644
--- a/openbsc/src/vty/command.c
+++ b/openbsc/src/vty/command.c
@@ -478,6 +478,13 @@ void install_element(enum node_type ntype, struct cmd_element *cmd)
cmd->cmdsize = cmd_cmdsize(cmd->strvec);
}
+/* Install a command into VIEW and ENABLE node */
+void install_element_ve(struct cmd_element *cmd)
+{
+ install_element(VIEW_NODE, cmd);
+ install_element(ENABLE_NODE, cmd);
+}
+
#ifdef VTY_CRYPT_PW
static unsigned char itoa64[] =
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
@@ -2311,7 +2318,7 @@ DEFUN(disable,
}
/* Down vty node level. */
-DEFUN(config_exit,
+gDEFUN(config_exit,
config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
{
switch (vty->node) {
@@ -2363,6 +2370,9 @@ DEFUN(config_exit,
vty->node = CONFIG_NODE;
break;
case MGCP_NODE:
+ case GBPROXY_NODE:
+ case SGSN_NODE:
+ case NS_NODE:
vty->node = CONFIG_NODE;
vty->index = NULL;
default:
@@ -2372,11 +2382,11 @@ DEFUN(config_exit,
}
/* quit is alias of exit. */
-ALIAS(config_exit,
+gALIAS(config_exit,
config_quit_cmd, "quit", "Exit current mode and down to previous mode\n")
/* End of configuration. */
- DEFUN(config_end,
+ gDEFUN(config_end,
config_end_cmd, "end", "End current mode and change to enable mode.")
{
switch (vty->node) {
@@ -2407,7 +2417,7 @@ DEFUN(show_version,
}
/* Help display function for all node. */
-DEFUN(config_help,
+gDEFUN(config_help,
config_help_cmd, "help", "Description of the interactive help system\n")
{
vty_out(vty,
@@ -2428,7 +2438,7 @@ argument.%s\
}
/* Help display function for all node. */
-DEFUN(config_list, config_list_cmd, "list", "Print command list\n")
+gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
{
unsigned int i;
struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
diff --git a/openbsc/src/vty/utils.c b/openbsc/src/vty/utils.c
new file mode 100644
index 000000000..d8dfb8ef9
--- /dev/null
+++ b/openbsc/src/vty/utils.c
@@ -0,0 +1,50 @@
+/* utility routines for printing common objects in the Osmocom world */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <osmocore/linuxlist.h>
+#include <osmocore/talloc.h>
+#include <osmocore/timer.h>
+#include <osmocore/rate_ctr.h>
+
+#include <vty/vty.h>
+
+void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
+ struct rate_ctr_group *ctrg)
+{
+ unsigned int i;
+
+ vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE);
+ for (i = 0; i < ctrg->desc->num_ctr; i++) {
+ struct rate_ctr *ctr = &ctrg->ctr[i];
+ vty_out(vty, " %s%s: %8" PRIu64 " "
+ "(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s",
+ prefix, ctrg->desc->ctr_desc[i].description, ctr->current,
+ ctr->intv[RATE_CTR_INTV_SEC].rate,
+ ctr->intv[RATE_CTR_INTV_MIN].rate,
+ ctr->intv[RATE_CTR_INTV_HOUR].rate,
+ ctr->intv[RATE_CTR_INTV_DAY].rate,
+ VTY_NEWLINE);
+ };
+}
diff --git a/openbsc/src/vty/vty.c b/openbsc/src/vty/vty.c
index 0ac9530f5..08068560b 100644
--- a/openbsc/src/vty/vty.c
+++ b/openbsc/src/vty/vty.c
@@ -51,10 +51,10 @@ struct vty *vty_new()
if (!new)
goto out;
- new->obuf = buffer_new(0); /* Use default buffer size. */
+ new->obuf = buffer_new(new, 0); /* Use default buffer size. */
if (!new->obuf)
goto out_new;
- new->buf = _talloc_zero(tall_vty_ctx, VTY_BUFSIZ, "vty_new->buf");
+ new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf");
if (!new->buf)
goto out_obuf;
@@ -170,8 +170,7 @@ void vty_close(struct vty *vty)
/* Check configure. */
vty_config_unlock(vty);
- /* FIXME: memory leak. We need to call telnet_close_client() but don't
- * have bfd */
+ /* VTY_CLOSED is handled by the telnet_interface */
vty_event(VTY_CLOSED, vty->fd, vty);
/* OK free vty. */
@@ -211,7 +210,7 @@ int vty_out(struct vty *vty, const char *format, ...)
else
size = size * 2;
- p = talloc_realloc_size(tall_vty_ctx, p, size);
+ p = talloc_realloc_size(vty, p, size);
if (!p)
return -1;
@@ -358,7 +357,7 @@ static void vty_ensure(struct vty *vty, int length)
{
if (vty->max <= length) {
vty->max *= 2;
- vty->buf = talloc_realloc_size(tall_vty_ctx, vty->buf, vty->max);
+ vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
// FIXME: check return
}
}
@@ -459,7 +458,7 @@ static void vty_hist_add(struct vty *vty)
/* Insert history entry. */
if (vty->hist[vty->hindex])
talloc_free(vty->hist[vty->hindex]);
- vty->hist[vty->hindex] = talloc_strdup(tall_vty_ctx, vty->buf);
+ vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
/* History index rotation. */
vty->hindex++;
@@ -916,7 +915,7 @@ static void vty_complete_command(struct vty *vty)
vty_backward_pure_word(vty);
vty_insert_word_overwrite(vty, matched[0]);
vty_self_insert(vty, ' ');
- //talloc_free(matched[0]);
+ talloc_free(matched[0]);
break;
case CMD_COMPLETE_MATCH:
vty_prompt(vty);
@@ -924,8 +923,6 @@ static void vty_complete_command(struct vty *vty)
vty_backward_pure_word(vty);
vty_insert_word_overwrite(vty, matched[0]);
talloc_free(matched[0]);
- vector_only_index_free(matched);
- return;
break;
case CMD_COMPLETE_LIST_MATCH:
for (i = 0; matched[i] != NULL; i++) {
@@ -966,7 +963,7 @@ vty_describe_fold(struct vty *vty, int cmd_width,
return;
}
- buf = _talloc_zero(tall_vty_ctx, strlen(desc->str) + 1, "describe_fold");
+ buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
if (!buf)
return;
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index 956bf1e78..279970649 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -39,9 +39,37 @@
#include <osmocore/talloc.h>
#include <openbsc/telnet_interface.h>
#include <openbsc/vty.h>
+#include <openbsc/gprs_ns.h>
static struct gsm_network *gsmnet;
+/* FIXME: this should go to some common file */
+static const struct value_string gprs_ns_timer_strs[] = {
+ { 0, "tns-block" },
+ { 1, "tns-block-retries" },
+ { 2, "tns-reset" },
+ { 3, "tns-reset-retries" },
+ { 4, "tns-test" },
+ { 5, "tns-alive" },
+ { 6, "tns-alive-retries" },
+ { 0, NULL }
+};
+
+static const struct value_string gprs_bssgp_cfg_strs[] = {
+ { 0, "blocking-timer" },
+ { 1, "blocking-retries" },
+ { 2, "unblocking-retries" },
+ { 3, "reset-timer" },
+ { 4, "reset-retries" },
+ { 5, "suspend-timer" },
+ { 6, "suspend-retries" },
+ { 7, "resume-timer" },
+ { 8, "resume-retries" },
+ { 9, "capability-update-timer" },
+ { 10, "capability-update-retries" },
+ { 0, NULL }
+};
+
struct cmd_node net_node = {
GSMNET_NODE,
"%s(network)#",
@@ -266,6 +294,9 @@ static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx)
int i;
vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE);
+ vty_out(vty, " rf_locked %u%s",
+ trx->nm_state.administrative == NM_STATE_LOCKED ? 1 : 0,
+ VTY_NEWLINE);
vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE);
vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE);
vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE);
@@ -276,10 +307,48 @@ static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx)
config_write_ts_single(vty, &trx->ts[i]);
}
+static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts)
+{
+ unsigned int i;
+ vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode),
+ VTY_NEWLINE);
+ if (bts->gprs.mode == BTS_GPRS_NONE)
+ return;
+
+ vty_out(vty, " gprs routing area %u%s", bts->gprs.rac,
+ VTY_NEWLINE);
+ vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci,
+ VTY_NEWLINE);
+ for (i = 0; i < ARRAY_SIZE(bts->gprs.cell.timer); i++)
+ vty_out(vty, " gprs cell timer %s %u%s",
+ get_value_string(gprs_bssgp_cfg_strs, i),
+ bts->gprs.cell.timer[i], VTY_NEWLINE);
+ vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei,
+ VTY_NEWLINE);
+ for (i = 0; i < ARRAY_SIZE(bts->gprs.nse.timer); i++)
+ vty_out(vty, " gprs ns timer %s %u%s",
+ get_value_string(gprs_ns_timer_strs, i),
+ bts->gprs.nse.timer[i], VTY_NEWLINE);
+ for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
+ struct gsm_bts_gprs_nsvc *nsvc =
+ &bts->gprs.nsvc[i];
+ struct in_addr ia;
+
+ ia.s_addr = htonl(nsvc->remote_ip);
+ vty_out(vty, " gprs nsvc %u nsvci %u%s", i,
+ nsvc->nsvci, VTY_NEWLINE);
+ vty_out(vty, " gprs nsvc %u local udp port %u%s", i,
+ nsvc->local_port, VTY_NEWLINE);
+ vty_out(vty, " gprs nsvc %u remote udp port %u%s", i,
+ nsvc->remote_port, VTY_NEWLINE);
+ vty_out(vty, " gprs nsvc %u remote ip %s%s", i,
+ inet_ntoa(ia), VTY_NEWLINE);
+ }
+}
+
static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
{
struct gsm_bts_trx *trx;
- int i;
vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE);
vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE);
@@ -305,6 +374,13 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
vty_out(vty, " rach max transmission %u%s",
rach_max_trans_raw2val(bts->si_common.rach_control.max_trans),
VTY_NEWLINE);
+
+ if (bts->rach_b_thresh != -1)
+ vty_out(vty, " rach nm busy threshold %u%s",
+ bts->rach_b_thresh, VTY_NEWLINE);
+ if (bts->rach_ldavg_slots != -1)
+ vty_out(vty, " rach nm load average %u%s",
+ bts->rach_ldavg_slots, VTY_NEWLINE);
if (bts->si_common.rach_control.cell_bar)
vty_out(vty, " cell barred 1%s", VTY_NEWLINE);
if ((bts->si_common.rach_control.t2 & 0x4) == 0)
@@ -317,30 +393,7 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
config_write_e1_link(vty, &bts->oml_e1_link, " oml ");
vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE);
}
- vty_out(vty, " gprs enabled %u%s", bts->gprs.enabled, VTY_NEWLINE);
- if (bts->gprs.enabled) {
- vty_out(vty, " gprs routing area %u%s", bts->gprs.rac,
- VTY_NEWLINE);
- vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci,
- VTY_NEWLINE);
- vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei,
- VTY_NEWLINE);
- for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
- struct gsm_bts_gprs_nsvc *nsvc =
- &bts->gprs.nsvc[i];
- struct in_addr ia;
-
- ia.s_addr = htonl(nsvc->remote_ip);
- vty_out(vty, " gprs nsvc %u nsvci %u%s", i,
- nsvc->nsvci, VTY_NEWLINE);
- vty_out(vty, " gprs nsvc %u local udp port %u%s", i,
- nsvc->local_port, VTY_NEWLINE);
- vty_out(vty, " gprs nsvc %u remote udp port %u%s", i,
- nsvc->remote_port, VTY_NEWLINE);
- vty_out(vty, " gprs nsvc %u remote ip %s%s", i,
- inet_ntoa(ia), VTY_NEWLINE);
- }
- }
+ config_write_bts_gprs(vty, bts);
llist_for_each_entry(trx, &bts->trx_list, list)
config_write_trx_single(vty, trx);
@@ -423,7 +476,9 @@ static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx)
DEFUN(show_trx,
show_trx_cmd,
"show trx [bts_nr] [trx_nr]",
- SHOW_STR "Display information about a TRX\n")
+ SHOW_STR "Display information about a TRX\n"
+ "BTS Number\n"
+ "TRX Number\n")
{
struct gsm_network *net = gsmnet;
struct gsm_bts *bts = NULL;
@@ -488,7 +543,8 @@ static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts)
DEFUN(show_ts,
show_ts_cmd,
"show timeslot [bts_nr] [trx_nr] [ts_nr]",
- SHOW_STR "Display information about a TS\n")
+ SHOW_STR "Display information about a TS\n"
+ "BTS Number\n" "TRX Number\n" "Timeslot Number\n")
{
struct gsm_network *net = gsmnet;
struct gsm_bts *bts;
@@ -542,10 +598,6 @@ DEFUN(show_ts,
static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
{
- int rc;
- struct gsm_auth_info ainfo;
- struct gsm_auth_tuple atuple;
-
vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
subscr->authorized, VTY_NEWLINE);
if (subscr->name)
@@ -596,7 +648,7 @@ static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr,
meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul");
}
-static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan)
+static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan)
{
int idx;
@@ -631,37 +683,28 @@ static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan)
meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " ");
}
-#if 0
-TODO: callref and remote callref of call must be resolved to get gsm_trans object
-static void call_dump_vty(struct vty *vty, struct gsm_call *call)
+static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan)
{
- vty_out(vty, "Call Type %u, State %u, Transaction ID %u%s",
- call->type, call->state, call->transaction_id, VTY_NEWLINE);
-
- if (call->local_lchan) {
- vty_out(vty, "Call Local Channel:%s", VTY_NEWLINE);
- lchan_dump_vty(vty, call->local_lchan);
- } else
- vty_out(vty, "Call has no Local Channel%s", VTY_NEWLINE);
+ struct gsm_meas_rep *mr;
+ int idx;
- if (call->remote_lchan) {
- vty_out(vty, "Call Remote Channel:%s", VTY_NEWLINE);
- lchan_dump_vty(vty, call->remote_lchan);
- } else
- vty_out(vty, "Call has no Remote Channel%s", VTY_NEWLINE);
+ /* we want to report the last measurement report */
+ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
+ lchan->meas_rep_idx, 1);
+ mr = &lchan->meas_rep[idx];
- if (call->called_subscr) {
- vty_out(vty, "Called Subscriber:%s", VTY_NEWLINE);
- subscr_dump_vty(vty, call->called_subscr);
- } else
- vty_out(vty, "Call has no Called Subscriber%s", VTY_NEWLINE);
+ vty_out(vty, "Lchan: %u Timeslot: %u TRX: %u BTS: %u Type: %s - L1 MS Power: %u dBm "
+ "RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s",
+ lchan->nr, lchan->ts->nr, lchan->ts->trx->nr,
+ lchan->ts->trx->bts->nr, gsm_lchant_name(lchan->type),
+ mr->ms_l1.pwr,
+ rxlev2dbm(mr->dl.full.rx_lev),
+ rxlev2dbm(mr->ul.full.rx_lev),
+ VTY_NEWLINE);
}
-#endif
-DEFUN(show_lchan,
- show_lchan_cmd,
- "show lchan [bts_nr] [trx_nr] [ts_nr] [lchan_nr]",
- SHOW_STR "Display information about a logical channel\n")
+static int lchan_summary(struct vty *vty, int argc, const char **argv,
+ void (*dump_cb)(struct vty *, struct gsm_lchan *))
{
struct gsm_network *net = gsmnet;
struct gsm_bts *bts;
@@ -706,7 +749,7 @@ DEFUN(show_lchan,
return CMD_WARNING;
}
lchan = &ts->lchan[lchan_nr];
- lchan_dump_vty(vty, lchan);
+ dump_cb(vty, lchan);
return CMD_SUCCESS;
}
for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
@@ -720,7 +763,7 @@ DEFUN(show_lchan,
lchan = &ts->lchan[lchan_nr];
if (lchan->type == GSM_LCHAN_NONE)
continue;
- lchan_dump_vty(vty, lchan);
+ dump_cb(vty, lchan);
}
}
}
@@ -729,6 +772,28 @@ DEFUN(show_lchan,
return CMD_SUCCESS;
}
+
+DEFUN(show_lchan,
+ show_lchan_cmd,
+ "show lchan [bts_nr] [trx_nr] [ts_nr] [lchan_nr]",
+ SHOW_STR "Display information about a logical channel\n"
+ "BTS Number\n" "TRX Number\n" "Timeslot Number\n"
+ "Logical Channel Number\n")
+
+{
+ return lchan_summary(vty, argc, argv, lchan_dump_full_vty);
+}
+
+DEFUN(show_lchan_summary,
+ show_lchan_summary_cmd,
+ "show lchan summary [bts_nr] [trx_nr] [ts_nr] [lchan_nr]",
+ SHOW_STR "Display information about a logical channel\n"
+ "BTS Number\n" "TRX Number\n" "Timeslot Number\n"
+ "Logical Channel Number\n")
+{
+ return lchan_summary(vty, argc, argv, lchan_dump_short_vty);
+}
+
static void e1drv_dump_vty(struct vty *vty, struct e1inp_driver *drv)
{
vty_out(vty, "E1 Input Driver %s%s", drv->name, VTY_NEWLINE);
@@ -757,7 +822,8 @@ static void e1line_dump_vty(struct vty *vty, struct e1inp_line *line)
DEFUN(show_e1line,
show_e1line_cmd,
"show e1_line [line_nr]",
- SHOW_STR "Display information about a E1 line\n")
+ SHOW_STR "Display information about a E1 line\n"
+ "E1 Line Number\n")
{
struct e1inp_line *line;
@@ -790,7 +856,8 @@ static void e1ts_dump_vty(struct vty *vty, struct e1inp_ts *ts)
DEFUN(show_e1ts,
show_e1ts_cmd,
"show e1_timeslot [line_nr] [ts_nr]",
- SHOW_STR "Display information about a E1 timeslot\n")
+ SHOW_STR "Display information about a E1 timeslot\n"
+ "E1 Line Number\n" "E1 Timeslot Number\n")
{
struct e1inp_line *line = NULL;
struct e1inp_ts *ts;
@@ -854,7 +921,8 @@ static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts)
DEFUN(show_paging,
show_paging_cmd,
"show paging [bts_nr]",
- SHOW_STR "Display information about paging reuqests of a BTS\n")
+ SHOW_STR "Display information about paging reuqests of a BTS\n"
+ "BTS Number\n")
{
struct gsm_network *net = gsmnet;
struct gsm_bts *bts;
@@ -881,50 +949,11 @@ DEFUN(show_paging,
return CMD_SUCCESS;
}
-DEFUN(show_stats,
- show_stats_cmd,
- "show statistics",
- SHOW_STR "Display network statistics\n")
-{
- struct gsm_network *net = gsmnet;
-
- vty_out(vty, "Channel Requests : %lu total, %lu no channel%s",
- counter_get(net->stats.chreq.total),
- counter_get(net->stats.chreq.no_channel), VTY_NEWLINE);
- vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s",
- counter_get(net->stats.loc_upd_type.attach),
- counter_get(net->stats.loc_upd_type.normal),
- counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE);
- vty_out(vty, "IMSI Detach Indications : %lu%s",
- counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE);
- vty_out(vty, "Location Update Response: %lu accept, %lu reject%s",
- counter_get(net->stats.loc_upd_resp.accept),
- counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE);
- vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s",
- counter_get(net->stats.paging.attempted),
- counter_get(net->stats.paging.completed),
- counter_get(net->stats.paging.expired), VTY_NEWLINE);
- vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, "
- "%lu completed, %lu failed%s",
- counter_get(net->stats.handover.attempted),
- counter_get(net->stats.handover.no_channel),
- counter_get(net->stats.handover.timeout),
- counter_get(net->stats.handover.completed),
- counter_get(net->stats.handover.failed), VTY_NEWLINE);
- vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s",
- counter_get(net->stats.sms.submitted),
- counter_get(net->stats.sms.no_receiver), VTY_NEWLINE);
- vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s",
- counter_get(net->stats.sms.delivered),
- counter_get(net->stats.sms.rp_err_mem),
- counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE);
- return CMD_SUCCESS;
-}
+#define NETWORK_STR "Configure the GSM network\n"
DEFUN(cfg_net,
cfg_net_cmd,
- "network",
- "Configure the GSM network")
+ "network", NETWORK_STR)
{
vty->index = gsmnet;
vty->node = GSMNET_NODE;
@@ -982,7 +1011,11 @@ DEFUN(cfg_net_name_long,
DEFUN(cfg_net_auth_policy,
cfg_net_auth_policy_cmd,
"auth policy (closed|accept-all|token)",
- "Set the GSM network authentication policy\n")
+ "Authentication (not cryptographic)\n"
+ "Set the GSM network authentication policy\n"
+ "Require the MS to be activated in HLR\n"
+ "Accept all MS, whether in HLR or not\n"
+ "Use SMS-token based authentication\n")
{
enum gsm_auth_policy policy = gsm_auth_policy_parse(argv[0]);
@@ -1022,7 +1055,12 @@ DEFUN(cfg_net_neci,
DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd,
"rrlp mode (none|ms-based|ms-preferred|ass-preferred)",
- "Set the Radio Resource Location Protocol Mode")
+ "Radio Resource Location Protocol\n"
+ "Set the Radio Resource Location Protocol Mode\n"
+ "Don't send RRLP request\n"
+ "Request MS-based location\n"
+ "Request any location, prefer MS-based\n"
+ "Request any location, prefer MS-assisted\n")
{
gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]);
@@ -1038,9 +1076,13 @@ DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd,
return CMD_SUCCESS;
}
+#define HANDOVER_STR "Handover Options\n"
+
DEFUN(cfg_net_handover, cfg_net_handover_cmd,
"handover (0|1)",
- "Whether or not to use in-call handover")
+ HANDOVER_STR
+ "Don't perform in-call handover\n"
+ "Perform in-call handover\n")
{
int enable = atoi(argv[0]);
@@ -1055,8 +1097,14 @@ DEFUN(cfg_net_handover, cfg_net_handover_cmd,
return CMD_SUCCESS;
}
+#define HO_WIN_STR HANDOVER_STR "Measurement Window\n"
+#define HO_WIN_RXLEV_STR HO_WIN_STR "Received Level Averaging\n"
+#define HO_WIN_RXQUAL_STR HO_WIN_STR "Received Quality Averaging\n"
+#define HO_PBUDGET_STR HANDOVER_STR "Power Budget\n"
+
DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd,
"handover window rxlev averaging <1-10>",
+ HO_WIN_RXLEV_STR
"How many RxLev measurements are used for averaging")
{
gsmnet->handover.win_rxlev_avg = atoi(argv[0]);
@@ -1065,6 +1113,7 @@ DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd,
DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd,
"handover window rxqual averaging <1-10>",
+ HO_WIN_RXQUAL_STR
"How many RxQual measurements are used for averaging")
{
gsmnet->handover.win_rxqual_avg = atoi(argv[0]);
@@ -1073,6 +1122,7 @@ DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd,
DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd,
"handover window rxlev neighbor averaging <1-10>",
+ HO_WIN_RXLEV_STR
"How many RxQual measurements are used for averaging")
{
gsmnet->handover.win_rxlev_avg_neigh = atoi(argv[0]);
@@ -1081,6 +1131,7 @@ DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd,
DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd,
"handover power budget interval <1-99>",
+ HO_PBUDGET_STR
"How often to check if we have a better cell (SACCH frames)")
{
gsmnet->handover.pwr_interval = atoi(argv[0]);
@@ -1089,6 +1140,7 @@ DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd,
DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd,
"handover power budget hysteresis <0-999>",
+ HO_PBUDGET_STR
"How many dB does a neighbor to be stronger to become a HO candidate")
{
gsmnet->handover.pwr_hysteresis = atoi(argv[0]);
@@ -1097,6 +1149,7 @@ DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd,
DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd,
"handover maximum distance <0-9999>",
+ HANDOVER_STR
"How big is the maximum timing advance before HO is forced")
{
gsmnet->handover.max_distance = atoi(argv[0]);
@@ -1107,6 +1160,7 @@ DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd,
DEFUN(cfg_net_T##number, \
cfg_net_T##number##_cmd, \
"timer t" #number " <0-65535>", \
+ "Configure GSM Timers\n" \
doc) \
{ \
int value = atoi(argv[0]); \
@@ -1138,7 +1192,8 @@ DECLARE_TIMER(3141, "Currently not used.")
DEFUN(cfg_bts,
cfg_bts_cmd,
"bts BTS_NR",
- "Select a BTS to configure\n")
+ "Select a BTS to configure\n"
+ "BTS Number\n")
{
int bts_nr = atoi(argv[0]);
struct gsm_bts *bts;
@@ -1301,9 +1356,13 @@ DEFUN(cfg_bts_unit_id,
return CMD_SUCCESS;
}
+#define OML_STR "Organization & Maintenance Link\n"
+#define IPA_STR "ip.access Specific Options\n"
+
DEFUN(cfg_bts_stream_id,
cfg_bts_stream_id_cmd,
"oml ip.access stream_id <0-255>",
+ OML_STR IPA_STR
"Set the ip.access Stream ID of the OML link of this BTS\n")
{
struct gsm_bts *bts = vty->index;
@@ -1319,10 +1378,12 @@ DEFUN(cfg_bts_stream_id,
return CMD_SUCCESS;
}
+#define OML_E1_STR OML_STR "E1 Line\n"
DEFUN(cfg_bts_oml_e1,
cfg_bts_oml_e1_cmd,
"oml e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)",
+ OML_E1_STR
"E1 interface to be used for OML\n")
{
struct gsm_bts *bts = vty->index;
@@ -1336,6 +1397,7 @@ DEFUN(cfg_bts_oml_e1,
DEFUN(cfg_bts_oml_e1_tei,
cfg_bts_oml_e1_tei_cmd,
"oml e1 tei <0-63>",
+ OML_E1_STR
"Set the TEI to be used for OML")
{
struct gsm_bts *bts = vty->index;
@@ -1347,7 +1409,9 @@ DEFUN(cfg_bts_oml_e1_tei,
DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd,
"channel allocator (ascending|descending)",
- "Should the channel allocator allocate in reverse TRX order?")
+ "Channnel Allocator\n" "Channel Allocator\n"
+ "Allocate Timeslots and Transceivers in ascending order\n"
+ "Allocate Timeslots and Transceivers in descending order\n")
{
struct gsm_bts *bts = vty->index;
@@ -1359,9 +1423,12 @@ DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd,
return CMD_SUCCESS;
}
+#define RACH_STR "Random Access Control Channel\n"
+
DEFUN(cfg_bts_rach_tx_integer,
cfg_bts_rach_tx_integer_cmd,
"rach tx integer <0-15>",
+ RACH_STR
"Set the raw tx integer value in RACH Control parameters IE")
{
struct gsm_bts *bts = vty->index;
@@ -1372,6 +1439,7 @@ DEFUN(cfg_bts_rach_tx_integer,
DEFUN(cfg_bts_rach_max_trans,
cfg_bts_rach_max_trans_cmd,
"rach max transmission (1|2|4|7)",
+ RACH_STR
"Set the maximum number of RACH burst transmissions")
{
struct gsm_bts *bts = vty->index;
@@ -1379,6 +1447,30 @@ DEFUN(cfg_bts_rach_max_trans,
return CMD_SUCCESS;
}
+#define NM_STR "Network Management\n"
+
+DEFUN(cfg_bts_rach_nm_b_thresh,
+ cfg_bts_rach_nm_b_thresh_cmd,
+ "rach nm busy threshold <0-255>",
+ RACH_STR NM_STR
+ "Set the NM Busy Threshold in dB")
+{
+ struct gsm_bts *bts = vty->index;
+ bts->rach_b_thresh = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_rach_nm_ldavg,
+ cfg_bts_rach_nm_ldavg_cmd,
+ "rach nm load average <0-65535>",
+ RACH_STR NM_STR
+ "Set the NM Loadaverage Slots value")
+{
+ struct gsm_bts *bts = vty->index;
+ bts->rach_ldavg_slots = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd,
"cell barred (0|1)",
"Should this cell be barred from access?")
@@ -1448,13 +1540,17 @@ DEFUN(cfg_bts_per_loc_upd, cfg_bts_per_loc_upd_cmd,
return CMD_SUCCESS;
}
+#define GPRS_TEXT "GPRS Packet Network\n"
+
DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd,
- "gprs cell bvci <0-65535>",
+ "gprs cell bvci <2-65535>",
+ GPRS_TEXT
+ "GPRS Cell Settings\n"
"GPRS BSSGP VC Identifier")
{
struct gsm_bts *bts = vty->index;
- if (!bts->gprs.enabled) {
+ if (bts->gprs.mode == BTS_GPRS_NONE) {
vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1466,11 +1562,12 @@ DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd,
DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd,
"gprs nsei <0-65535>",
+ GPRS_TEXT
"GPRS NS Entity Identifier")
{
struct gsm_bts *bts = vty->index;
- if (!bts->gprs.enabled) {
+ if (bts->gprs.mode == BTS_GPRS_NONE) {
vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1480,15 +1577,19 @@ DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd,
return CMD_SUCCESS;
}
+#define NSVC_TEXT "Network Service Virtual Connection (NS-VC)\n" \
+ "NSVC Logical Number\n"
DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd,
"gprs nsvc <0-1> nsvci <0-65535>",
+ GPRS_TEXT NSVC_TEXT
+ "NS Virtual Connection Identifier\n"
"GPRS NS VC Identifier")
{
struct gsm_bts *bts = vty->index;
int idx = atoi(argv[0]);
- if (!bts->gprs.enabled) {
+ if (bts->gprs.mode == BTS_GPRS_NONE) {
vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1500,12 +1601,13 @@ DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd,
DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd,
"gprs nsvc <0-1> local udp port <0-65535>",
+ GPRS_TEXT NSVC_TEXT
"GPRS NS Local UDP Port")
{
struct gsm_bts *bts = vty->index;
int idx = atoi(argv[0]);
- if (!bts->gprs.enabled) {
+ if (bts->gprs.mode == BTS_GPRS_NONE) {
vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1517,12 +1619,13 @@ DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd,
DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd,
"gprs nsvc <0-1> remote udp port <0-65535>",
+ GPRS_TEXT NSVC_TEXT
"GPRS NS Remote UDP Port")
{
struct gsm_bts *bts = vty->index;
int idx = atoi(argv[0]);
- if (!bts->gprs.enabled) {
+ if (bts->gprs.mode == BTS_GPRS_NONE) {
vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1534,13 +1637,14 @@ DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd,
DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd,
"gprs nsvc <0-1> remote ip A.B.C.D",
+ GPRS_TEXT NSVC_TEXT
"GPRS NS Remote IP Address")
{
struct gsm_bts *bts = vty->index;
int idx = atoi(argv[0]);
struct in_addr ia;
- if (!bts->gprs.enabled) {
+ if (bts->gprs.mode == BTS_GPRS_NONE) {
vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1551,13 +1655,63 @@ DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_bts_gprs_ns_timer, cfg_bts_gprs_ns_timer_cmd,
+ "gprs ns timer " NS_TIMERS " <0-255>",
+ GPRS_TEXT "Network Service\n"
+ "Network Service Timer\n"
+ NS_TIMERS_HELP "Timer Value\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
+ int val = atoi(argv[1]);
+
+ if (bts->gprs.mode == BTS_GPRS_NONE) {
+ vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.nse.timer))
+ return CMD_WARNING;
+
+ bts->gprs.nse.timer[idx] = val;
+
+ return CMD_SUCCESS;
+}
+
+#define BSSGP_TIMERS "(blocking-timer|blocking-retries|unblocking-retries|reset-timer|reset-retries|suspend-timer|suspend-retries|resume-timer|resume-retries|capability-update-timer|capability-update-retries)"
+#define BSSGP_TIMERS_HELP ""
+
+DEFUN(cfg_bts_gprs_cell_timer, cfg_bts_gprs_cell_timer_cmd,
+ "gprs cell timer " BSSGP_TIMERS " <0-255>",
+ GPRS_TEXT "Cell / BSSGP\n"
+ "Cell/BSSGP Timer\n"
+ BSSGP_TIMERS_HELP "Timer Value\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int idx = get_string_value(gprs_bssgp_cfg_strs, argv[0]);
+ int val = atoi(argv[1]);
+
+ if (bts->gprs.mode == BTS_GPRS_NONE) {
+ vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.cell.timer))
+ return CMD_WARNING;
+
+ bts->gprs.cell.timer[idx] = val;
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd,
"gprs routing area <0-255>",
+ GPRS_TEXT
"GPRS Routing Area Code")
{
struct gsm_bts *bts = vty->index;
- if (!bts->gprs.enabled) {
+ if (bts->gprs.mode == BTS_GPRS_NONE) {
vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1567,22 +1721,28 @@ DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_bts_gprs_enabled, cfg_bts_gprs_enabled_cmd,
- "gprs enabled <0-1>",
- "GPRS Enabled on this BTS")
+DEFUN(cfg_bts_gprs_mode, cfg_bts_gprs_mode_cmd,
+ "gprs mode (none|gprs|egprs)",
+ GPRS_TEXT
+ "GPRS Mode for this BTS\n"
+ "GPRS Disabled on this BTS\n"
+ "GPRS Enabled on this BTS\n"
+ "EGPRS (EDGE) Enabled on this BTS\n")
{
struct gsm_bts *bts = vty->index;
- bts->gprs.enabled = atoi(argv[0]);
+ bts->gprs.mode = bts_gprs_mode_parse(argv[0]);
return CMD_SUCCESS;
}
+#define TRX_TEXT "Radio Transceiver\n"
/* per TRX configuration */
DEFUN(cfg_trx,
cfg_trx_cmd,
"trx TRX_NR",
+ TRX_TEXT
"Select a TRX to configure")
{
int trx_nr = atoi(argv[0]);
@@ -1756,6 +1916,8 @@ DEFUN(cfg_ts_e1_subslot,
return CMD_SUCCESS;
}
+extern int bsc_vty_init_extra(struct gsm_network *net);
+
int bsc_vty_init(struct gsm_network *net)
{
gsmnet = net;
@@ -1763,18 +1925,18 @@ int bsc_vty_init(struct gsm_network *net)
cmd_init(1);
vty_init();
- install_element(VIEW_NODE, &show_net_cmd);
- install_element(VIEW_NODE, &show_bts_cmd);
- install_element(VIEW_NODE, &show_trx_cmd);
- install_element(VIEW_NODE, &show_ts_cmd);
- install_element(VIEW_NODE, &show_lchan_cmd);
+ install_element_ve(&show_net_cmd);
+ install_element_ve(&show_bts_cmd);
+ install_element_ve(&show_trx_cmd);
+ install_element_ve(&show_ts_cmd);
+ install_element_ve(&show_lchan_cmd);
+ install_element_ve(&show_lchan_summary_cmd);
- install_element(VIEW_NODE, &show_e1drv_cmd);
- install_element(VIEW_NODE, &show_e1line_cmd);
- install_element(VIEW_NODE, &show_e1ts_cmd);
+ install_element_ve(&show_e1drv_cmd);
+ install_element_ve(&show_e1line_cmd);
+ install_element_ve(&show_e1ts_cmd);
- install_element(VIEW_NODE, &show_paging_cmd);
- install_element(VIEW_NODE, &show_stats_cmd);
+ install_element_ve(&show_paging_cmd);
openbsc_vty_add_cmds();
@@ -1826,15 +1988,19 @@ int bsc_vty_init(struct gsm_network *net)
install_element(BTS_NODE, &cfg_bts_challoc_cmd);
install_element(BTS_NODE, &cfg_bts_rach_tx_integer_cmd);
install_element(BTS_NODE, &cfg_bts_rach_max_trans_cmd);
+ install_element(BTS_NODE, &cfg_bts_rach_nm_b_thresh_cmd);
+ install_element(BTS_NODE, &cfg_bts_rach_nm_ldavg_cmd);
install_element(BTS_NODE, &cfg_bts_cell_barred_cmd);
install_element(BTS_NODE, &cfg_bts_rach_ec_allowed_cmd);
install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd);
install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd);
install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd);
install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_enabled_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_mode_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd);
diff --git a/openbsc/src/vty_interface_cmds.c b/openbsc/src/vty_interface_cmds.c
index d4945840e..dd5e108ab 100644
--- a/openbsc/src/vty_interface_cmds.c
+++ b/openbsc/src/vty_interface_cmds.c
@@ -30,6 +30,8 @@
#include <stdlib.h>
+#define LOGGING_STR "Configure log message to this terminal\n"
+
static void _vty_output(struct log_target *tgt, const char *line)
{
struct vty *vty = tgt->tgt_vty.vty;
@@ -55,6 +57,7 @@ struct log_target *log_target_create_vty(struct vty *vty)
DEFUN(enable_logging,
enable_logging_cmd,
"logging enable",
+ LOGGING_STR
"Enables logging to this vty\n")
{
struct telnet_connection *conn;
@@ -76,6 +79,7 @@ DEFUN(enable_logging,
DEFUN(logging_fltr_imsi,
logging_fltr_imsi_cmd,
"logging filter imsi IMSI",
+ LOGGING_STR
"Print all messages related to a IMSI\n")
{
struct telnet_connection *conn;
@@ -93,6 +97,7 @@ DEFUN(logging_fltr_imsi,
DEFUN(logging_fltr_all,
logging_fltr_all_cmd,
"logging filter all <0-1>",
+ LOGGING_STR
"Print all messages to the console\n")
{
struct telnet_connection *conn;
@@ -110,6 +115,7 @@ DEFUN(logging_fltr_all,
DEFUN(logging_use_clr,
logging_use_clr_cmd,
"logging color <0-1>",
+ LOGGING_STR
"Use color for printing messages\n")
{
struct telnet_connection *conn;
@@ -127,6 +133,7 @@ DEFUN(logging_use_clr,
DEFUN(logging_prnt_timestamp,
logging_prnt_timestamp_cmd,
"logging timestamp <0-1>",
+ LOGGING_STR
"Print the timestamp of each message\n")
{
struct telnet_connection *conn;
@@ -142,12 +149,48 @@ DEFUN(logging_prnt_timestamp,
}
/* FIXME: those have to be kept in sync with the log levels and categories */
-#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref)"
+#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref|gprs|ns|bssgp|all)"
+#define CATEGORIES_HELP \
+ "A-bis Radio Link Layer (RLL)\n" \
+ "Layer3 Call Control (CC)\n" \
+ "Layer3 Mobility Management (MM)\n" \
+ "Layer3 Radio Resource (RR)\n" \
+ "A-bis Radio Signalling Link (RSL)\n" \
+ "A-bis Network Management / O&M (NM/OML)\n" \
+ "Layer3 Short Messagaging Service (SMS)\n" \
+ "Paging Subsystem\n" \
+ "MNCC API for Call Control application\n" \
+ "A-bis Input Subsystem\n" \
+ "A-bis Input Driver for Signalling\n" \
+ "A-bis Input Driver for B-Channel (voice data)\n" \
+ "A-bis B-Channel / Sub-channel Multiplexer\n" \
+ "Radio Measurements\n" \
+ "SCCP\n" \
+ "Mobile Switching Center\n" \
+ "Media Gateway Control Protocol\n" \
+ "Hand-over\n" \
+ "Database Layer\n" \
+ "Reference Counting\n" \
+ "GPRS Core\n" \
+ "GPRS Network Service (NS)\n" \
+ "GPRS BSS Gateway Protocol (BSSGP)\n" \
+ "Global setting for all subsytems\n"
+
#define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)"
+#define LEVELS_HELP \
+ "Log simply everything\n" \
+ "Log debug messages and higher levels\n" \
+ "Log informational messages and higher levels\n" \
+ "Log noticable messages and higher levels\n" \
+ "Log error messages and higher levels\n" \
+ "Log only fatal messages\n"
DEFUN(logging_level,
logging_level_cmd,
"logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS,
- "Set the log level for a specified category\n")
+ LOGGING_STR
+ "Set the log level for a specified category\n"
+ CATEGORIES_HELP
+ LEVELS_HELP)
{
struct telnet_connection *conn;
int category = log_parse_category(argv[0]);
@@ -159,13 +202,19 @@ DEFUN(logging_level,
return CMD_WARNING;
}
- if (category < 0) {
- vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE);
+ if (level < 0) {
+ vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE);
return CMD_WARNING;
}
- if (level < 0) {
- vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE);
+ /* Check for special case where we want to set global log level */
+ if (!strcmp(argv[0], "all")) {
+ log_set_log_level(conn->dbg, level);
+ return CMD_SUCCESS;
+ }
+
+ if (category < 0) {
+ vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
@@ -178,6 +227,7 @@ DEFUN(logging_level,
DEFUN(logging_set_category_mask,
logging_set_category_mask_cmd,
"logging set log mask MASK",
+ LOGGING_STR
"Decide which categories to output.\n")
{
struct telnet_connection *conn;
@@ -192,10 +242,11 @@ DEFUN(logging_set_category_mask,
return CMD_SUCCESS;
}
-DEFUN(logging_set_log_level,
- logging_set_log_level_cmd,
- "logging set log level <0-8>",
- "Set the global log level. The value 0 implies no filtering.\n")
+DEFUN(diable_logging,
+ disable_logging_cmd,
+ "logging disable",
+ LOGGING_STR
+ "Disables logging to this vty\n")
{
struct telnet_connection *conn;
@@ -205,14 +256,42 @@ DEFUN(logging_set_log_level,
return CMD_WARNING;
}
- log_set_log_level(conn->dbg, atoi(argv[0]));
+ log_del_target(conn->dbg);
+ talloc_free(conn->dbg);
+ conn->dbg = NULL;
return CMD_SUCCESS;
}
-DEFUN(diable_logging,
- disable_logging_cmd,
- "logging disable",
- "Disables logging to this vty\n")
+static void vty_print_logtarget(struct vty *vty, const struct log_info *info,
+ const struct log_target *tgt)
+{
+ unsigned int i;
+
+ vty_out(vty, " Global Loglevel: %s%s",
+ log_level_str(tgt->loglevel), VTY_NEWLINE);
+ vty_out(vty, " Use color: %s, Print Timestamp: %s%s",
+ tgt->use_color ? "On" : "Off",
+ tgt->print_timestamp ? "On" : "Off", VTY_NEWLINE);
+
+ vty_out(vty, " Log Level specific information:%s", VTY_NEWLINE);
+
+ for (i = 0; i < info->num_cat; i++) {
+ const struct log_category *cat = &tgt->categories[i];
+ vty_out(vty, " %-10s %-10s %-8s %s%s",
+ info->cat[i].name+1, log_level_str(cat->loglevel),
+ cat->enabled ? "Enabled" : "Disabled",
+ info->cat[i].description,
+ VTY_NEWLINE);
+ }
+}
+
+#define SHOW_LOG_STR "Show current logging configuration\n"
+
+DEFUN(show_logging_vty,
+ show_logging_vty_cmd,
+ "show logging vty",
+ SHOW_STR SHOW_LOG_STR
+ "Show current logging configuration for this vty\n")
{
struct telnet_connection *conn;
@@ -221,23 +300,37 @@ DEFUN(diable_logging,
vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
return CMD_WARNING;
}
+ vty_print_logtarget(vty, &log_info, conn->dbg);
- log_del_target(conn->dbg);
- talloc_free(conn->dbg);
- conn->dbg = NULL;
return CMD_SUCCESS;
}
-void openbsc_vty_add_cmds()
+void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net)
{
- install_element(VIEW_NODE, &enable_logging_cmd);
- install_element(VIEW_NODE, &disable_logging_cmd);
- install_element(VIEW_NODE, &logging_fltr_imsi_cmd);
- install_element(VIEW_NODE, &logging_fltr_all_cmd);
- install_element(VIEW_NODE, &logging_use_clr_cmd);
- install_element(VIEW_NODE, &logging_prnt_timestamp_cmd);
- install_element(VIEW_NODE, &logging_set_category_mask_cmd);
- install_element(VIEW_NODE, &logging_level_cmd);
- install_element(VIEW_NODE, &logging_set_log_level_cmd);
+ vty_out(vty, "Channel Requests : %lu total, %lu no channel%s",
+ counter_get(net->stats.chreq.total),
+ counter_get(net->stats.chreq.no_channel), VTY_NEWLINE);
+ vty_out(vty, "Channel Failures : %lu rf_failures, %lu rll failures%s",
+ counter_get(net->stats.chan.rf_fail),
+ counter_get(net->stats.chan.rll_err), VTY_NEWLINE);
+ vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s",
+ counter_get(net->stats.paging.attempted),
+ counter_get(net->stats.paging.completed),
+ counter_get(net->stats.paging.expired), VTY_NEWLINE);
+ vty_out(vty, "BTS failures : %lu OML, %lu RSL%s",
+ counter_get(net->stats.bts.oml_fail),
+ counter_get(net->stats.bts.rsl_fail), VTY_NEWLINE);
+}
+void openbsc_vty_add_cmds()
+{
+ install_element_ve(&enable_logging_cmd);
+ install_element_ve(&disable_logging_cmd);
+ install_element_ve(&logging_fltr_imsi_cmd);
+ install_element_ve(&logging_fltr_all_cmd);
+ install_element_ve(&logging_use_clr_cmd);
+ install_element_ve(&logging_prnt_timestamp_cmd);
+ install_element_ve(&logging_set_category_mask_cmd);
+ install_element_ve(&logging_level_cmd);
+ install_element_ve(&show_logging_vty_cmd);
}
diff --git a/openbsc/src/vty_interface_layer3.c b/openbsc/src/vty_interface_layer3.c
index b824c3db6..1b2adbbc7 100644
--- a/openbsc/src/vty_interface_layer3.c
+++ b/openbsc/src/vty_interface_layer3.c
@@ -41,6 +41,7 @@
#include <osmocore/talloc.h>
#include <openbsc/signal.h>
#include <openbsc/debug.h>
+#include <openbsc/vty.h>
static struct gsm_network *gsmnet;
@@ -57,7 +58,7 @@ static int dummy_config_write(struct vty *v)
static struct buffer *argv_to_buffer(int argc, const char *argv[], int base)
{
- struct buffer *b = buffer_new(1024);
+ struct buffer *b = buffer_new(NULL, 1024);
int i;
if (!b)
@@ -502,21 +503,57 @@ static int scall_cbfn(unsigned int subsys, unsigned int signal,
return 0;
}
+DEFUN(show_stats,
+ show_stats_cmd,
+ "show statistics",
+ SHOW_STR "Display network statistics\n")
+{
+ struct gsm_network *net = gsmnet;
+
+ openbsc_vty_print_statistics(vty, net);
+ vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s",
+ counter_get(net->stats.loc_upd_type.attach),
+ counter_get(net->stats.loc_upd_type.normal),
+ counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE);
+ vty_out(vty, "IMSI Detach Indications : %lu%s",
+ counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE);
+ vty_out(vty, "Location Update Response: %lu accept, %lu reject%s",
+ counter_get(net->stats.loc_upd_resp.accept),
+ counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE);
+ vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, "
+ "%lu completed, %lu failed%s",
+ counter_get(net->stats.handover.attempted),
+ counter_get(net->stats.handover.no_channel),
+ counter_get(net->stats.handover.timeout),
+ counter_get(net->stats.handover.completed),
+ counter_get(net->stats.handover.failed), VTY_NEWLINE);
+ vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s",
+ counter_get(net->stats.sms.submitted),
+ counter_get(net->stats.sms.no_receiver), VTY_NEWLINE);
+ vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s",
+ counter_get(net->stats.sms.delivered),
+ counter_get(net->stats.sms.rp_err_mem),
+ counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+
int bsc_vty_init_extra(struct gsm_network *net)
{
gsmnet = net;
register_signal_handler(SS_SCALL, scall_cbfn, NULL);
- install_element(VIEW_NODE, &show_subscr_cmd);
- install_element(VIEW_NODE, &show_subscr_cache_cmd);
+ install_element_ve(&show_subscr_cmd);
+ install_element_ve(&show_subscr_cache_cmd);
- install_element(VIEW_NODE, &sms_send_pend_cmd);
+ install_element_ve(&sms_send_pend_cmd);
- install_element(VIEW_NODE, &subscriber_send_sms_cmd);
- install_element(VIEW_NODE, &subscriber_silent_sms_cmd);
- install_element(VIEW_NODE, &subscriber_silent_call_start_cmd);
- install_element(VIEW_NODE, &subscriber_silent_call_stop_cmd);
+ install_element_ve(&subscriber_send_sms_cmd);
+ install_element_ve(&subscriber_silent_sms_cmd);
+ install_element_ve(&subscriber_silent_call_start_cmd);
+ install_element_ve(&subscriber_silent_call_stop_cmd);
+ install_element_ve(&show_stats_cmd);
install_element(CONFIG_NODE, &cfg_subscr_cmd);
install_node(&subscr_node, dummy_config_write);
diff --git a/openbsc/tests/sccp/sccp_test.c b/openbsc/tests/sccp/sccp_test.c
index 0c2adc83f..31eb47f9f 100644
--- a/openbsc/tests/sccp/sccp_test.c
+++ b/openbsc/tests/sccp/sccp_test.c
@@ -264,6 +264,10 @@ static const u_int8_t it_test[] = {
0x10, 0x01, 0x07,
0x94, 0x01, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00 };
+static const u_int8_t proto_err[] = {
+0x0f, 0x0c, 0x04, 0x00, 0x00,
+};
+
static const struct sccp_parse_header_result parse_result[] = {
{
.msg_type = SCCP_MSG_TYPE_IT,
@@ -287,6 +291,21 @@ static const struct sccp_parse_header_result parse_result[] = {
.input = it_test,
.input_len = sizeof(it_test),
},
+ {
+ .msg_type = SCCP_MSG_TYPE_ERR,
+ .wanted_len = 0,
+ .src_ssn = -1,
+ .dst_ssn = -1,
+ .has_src_ref = 0,
+ .has_dst_ref = 1,
+ .dst_ref = {
+ .octet1 = 0x0c,
+ .octet2 = 0x04,
+ .octet3 = 0x00,
+ },
+ .input = proto_err,
+ .input_len = sizeof(proto_err),
+ },
};
@@ -777,7 +796,7 @@ static void test_sccp_parsing(void)
memset(&result, 0, sizeof(result));
if (sccp_parse_header(msg, &result) != 0) {
- fprintf(stderr, "Failed to parse test: %d\n", current_test);
+ fprintf(stderr, "Failed to sccp parse test: %d\n", current_test);
} else {
if (parse_result[current_test].wanted_len != result.data_len) {
fprintf(stderr, "Unexpected data length.\n");
diff --git a/wireshark/abis_oml.patch b/wireshark/abis_oml.patch
index 9f06b4d82..40132110e 100644
--- a/wireshark/abis_oml.patch
+++ b/wireshark/abis_oml.patch
@@ -1,8 +1,21 @@
-Index: wireshark/epan/dissectors/Makefile.common
-===================================================================
---- wireshark.orig/epan/dissectors/Makefile.common
-+++ wireshark/epan/dissectors/Makefile.common
-@@ -474,6 +474,7 @@
+From 5857518be87641fdab45e593bc9fd5ef5595e619 Mon Sep 17 00:00:00 2001
+From: Holger Hans Peter Freyther <zecke@selfish.org>
+Date: Mon, 19 Apr 2010 13:23:51 +0800
+Subject: [PATCH 1/2] Add the Abis OML patch.
+
+---
+ epan/dissectors/Makefile.common | 1 +
+ epan/dissectors/packet-gsm_abis_oml.c | 1382 +++++++++++++++++++++++++++++++++
+ epan/dissectors/packet-gsm_abis_oml.h | 787 +++++++++++++++++++
+ 3 files changed, 2170 insertions(+), 0 deletions(-)
+ create mode 100644 epan/dissectors/packet-gsm_abis_oml.c
+ create mode 100644 epan/dissectors/packet-gsm_abis_oml.h
+
+diff --git a/epan/dissectors/Makefile.common b/epan/dissectors/Makefile.common
+index dbc3726..98dcdc3 100644
+--- a/epan/dissectors/Makefile.common
++++ b/epan/dissectors/Makefile.common
+@@ -481,6 +481,7 @@ DISSECTOR_SRC = \
packet-gsm_a_gm.c \
packet-gsm_a_rp.c \
packet-gsm_a_rr.c \
@@ -12,7 +25,7 @@ Index: wireshark/epan/dissectors/Makefile.common
packet-gsm_bssmap_le.c \
diff --git a/epan/dissectors/packet-gsm_abis_oml.c b/epan/dissectors/packet-gsm_abis_oml.c
new file mode 100644
-index 0000000..2de9dca
+index 0000000..fa46ab5
--- /dev/null
+++ b/epan/dissectors/packet-gsm_abis_oml.c
@@ -0,0 +1,1382 @@
@@ -1398,11 +1411,12 @@ index 0000000..2de9dca
+ abis_oml_handle = create_dissector_handle(dissect_abis_oml, proto_abis_oml);
+ dissector_add("lapd.gsm.sapi", LAPD_GSM_SAPI_OM_PROC, abis_oml_handle);
+}
-Index: wireshark/epan/dissectors/packet-gsm_abis_oml.h
-===================================================================
+diff --git a/epan/dissectors/packet-gsm_abis_oml.h b/epan/dissectors/packet-gsm_abis_oml.h
+new file mode 100644
+index 0000000..d523e96
--- /dev/null
-+++ wireshark/epan/dissectors/packet-gsm_abis_oml.h
-@@ -0,0 +1,786 @@
++++ b/epan/dissectors/packet-gsm_abis_oml.h
+@@ -0,0 +1,787 @@
+/* GSM Network Management messages on the A-bis interface
+ * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
+
@@ -2190,3 +2204,6 @@ Index: wireshark/epan/dissectors/packet-gsm_abis_oml.h
+};
+
+#endif /* _NM_H */
+--
+1.7.0.1
+
diff --git a/wireshark/rsl-ipaccess.patch b/wireshark/rsl-ipaccess.patch
index 36c09c57b..29220b87b 100644
--- a/wireshark/rsl-ipaccess.patch
+++ b/wireshark/rsl-ipaccess.patch
@@ -1,16 +1,25 @@
-Index: wireshark/epan/dissectors/packet-rsl.c
-===================================================================
---- wireshark.orig/epan/dissectors/packet-rsl.c 2009-10-21 23:03:41.000000000 +0200
-+++ wireshark/epan/dissectors/packet-rsl.c 2009-10-22 10:02:51.000000000 +0200
+From 8f35d623641dbba90e6186604c11e892bf515ecc Mon Sep 17 00:00:00 2001
+From: Holger Hans Peter Freyther <zecke@selfish.org>
+Date: Mon, 19 Apr 2010 13:32:58 +0800
+Subject: [PATCH 2/2] RSL patch
+
+---
+ epan/dissectors/packet-rsl.c | 522 +++++++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 515 insertions(+), 7 deletions(-)
+
+diff --git a/epan/dissectors/packet-rsl.c b/epan/dissectors/packet-rsl.c
+index b10a671..a455cf3 100644
+--- a/epan/dissectors/packet-rsl.c
++++ b/epan/dissectors/packet-rsl.c
@@ -2,6 +2,7 @@
* Routines for Radio Signalling Link (RSL) dissection.
*
* Copyright 2007, Anders Broman <anders.broman@ericsson.com>
+ * Copyright 2009, Harald Welte <laforge@gnumonks.org>
*
- * $Id: packet-rsl.c 29944 2009-09-16 13:39:37Z morriss $
+ * $Id$
*
-@@ -44,6 +45,8 @@
+@@ -42,6 +43,8 @@
#include <epan/lapd_sapi.h>
#include "packet-gsm_a_common.h"
@@ -19,7 +28,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
/* Initialize the protocol and registered fields */
static int proto_rsl = -1;
-@@ -117,6 +120,24 @@
+@@ -115,6 +118,24 @@ static int hf_rsl_emlpp_prio = -1;
static int hf_rsl_rtd = -1;
static int hf_rsl_delay_ind = -1;
static int hf_rsl_tfo = -1;
@@ -44,7 +53,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
/* Initialize the subtree pointers */
static int ett_rsl = -1;
-@@ -174,6 +195,15 @@
+@@ -172,6 +193,15 @@ static int ett_ie_cause = -1;
static int ett_ie_meas_res_no = -1;
static int ett_ie_message_id = -1;
static int ett_ie_sys_info_type = -1;
@@ -60,7 +69,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
proto_tree *top_tree;
dissector_handle_t gsm_a_ccch_handle;
-@@ -209,8 +239,11 @@
+@@ -207,8 +237,11 @@ static const value_string rsl_msg_disc_vals[] = {
{ 0x06, "Common Channel Management messages" },
{ 0x08, "TRX Management messages" },
{ 0x16, "Location Services messages" },
@@ -72,7 +81,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
/*
* 9.2 MESSAGE TYPE
*/
-@@ -277,6 +310,49 @@
+@@ -275,6 +308,49 @@ static const value_string rsl_msg_disc_vals[] = {
/* 0 1 - - - - - - Location Services messages: */
#define RSL_MSG_LOC_INF 65 /* 8.7.1 */
@@ -90,16 +99,16 @@ Index: wireshark/epan/dissectors/packet-rsl.c
+#define RSL_MSG_TYPE_IPAC_PDCH_DEACT_ACK 0x4c
+#define RSL_MSG_TYPE_IPAC_PDCH_DEACT_NACK 0x4d
+
-+#define RSL_MSG_TYPE_IPAC_BIND 0x70
-+#define RSL_MSG_TYPE_IPAC_BIND_ACK 0x71
-+#define RSL_MSG_TYPE_IPAC_BIND_NACK 0x72
-+#define RSL_MSG_TYPE_IPAC_CONNECT 0x73
-+#define RSL_MSG_TYPE_IPAC_CONNECT_ACK 0x74
-+#define RSL_MSG_TYPE_IPAC_CONNECT_NACK 0x75
-+#define RSL_MSG_TYPE_IPAC_DISC_IND 0x76
-+#define RSL_MSG_TYPE_IPAC_DISC 0x77
-+#define RSL_MSG_TYPE_IPAC_DISC_ACK 0x78
-+#define RSL_MSG_TYPE_IPAC_DISC_NACK 0x79
++#define RSL_MSG_TYPE_IPAC_CRCX 0x70
++#define RSL_MSG_TYPE_IPAC_CRCX_ACK 0x71
++#define RSL_MSG_TYPE_IPAC_CRCX_NACK 0x72
++#define RSL_MSG_TYPE_IPAC_MDCX 0x73
++#define RSL_MSG_TYPE_IPAC_MDCX_ACK 0x74
++#define RSL_MSG_TYPE_IPAC_MDCX_NACK 0x75
++#define RSL_MSG_TYPE_IPAC_DLCX_IND 0x76
++#define RSL_MSG_TYPE_IPAC_DLCX 0x77
++#define RSL_MSG_TYPE_IPAC_DLCX_ACK 0x78
++#define RSL_MSG_TYPE_IPAC_DLCX_NACK 0x79
+
+#define RSL_IE_IPAC_SRTP_CONFIG 0xe0
+#define RSL_IE_IPAC_PROXY_UDP 0xe1
@@ -122,7 +131,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
static const value_string rsl_msg_type_vals[] = {
/* 0 0 0 0 - - - - Radio Link Layer Management messages: */
-@@ -339,6 +415,26 @@
+@@ -337,6 +413,26 @@ static const value_string rsl_msg_type_vals[] = {
{ 0x3f, "TFO MODification REQuest" }, /* 8.4.31 */
/* 0 1 - - - - - - Location Services messages: */
{ 0x41, "Location Information" }, /* 8.7.1 */
@@ -149,7 +158,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
{ 0, NULL }
};
-@@ -372,10 +468,10 @@ static const value_string rsl_msg_type_vals[] = {
+@@ -370,10 +466,10 @@ static const value_string rsl_msg_type_vals[] = {
#define RSL_IE_MESSAGE_ID 28
#define RSL_IE_SYS_INFO_TYPE 30
@@ -164,7 +173,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
#define RSL_IE_FULL_IMM_ASS_INF 35
#define RSL_IE_SMSCB_INF 36
#define RSL_IE_FULL_MS_TIMING_OFFSET 37
-@@ -478,6 +574,24 @@
+@@ -476,6 +572,24 @@ static const value_string rsl_ie_type_vals[] = {
Not used
*/
@@ -189,7 +198,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
{ 0, NULL }
};
-@@ -514,6 +628,96 @@
+@@ -512,6 +626,96 @@ static const value_string rsl_ch_no_Cbits_vals[] = {
{ 0, NULL }
};
@@ -286,7 +295,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
/* 9.3.1 Channel number 9.3.1 M TV 2 */
static int
dissect_rsl_ie_ch_no(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gboolean is_mandatory)
-@@ -2044,7 +2248,6 @@
+@@ -2042,7 +2246,6 @@ dissect_rsl_ie_err_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int
proto_item_set_len(ti, length+2);
proto_tree_add_item(ie_tree, hf_rsl_ie_length, tvb, offset, 1, FALSE);
@@ -294,7 +303,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
/* Received Message */
offset = dissct_rsl_msg(tvb, pinfo, ie_tree, offset);
-@@ -2909,12 +3112,183 @@
+@@ -2907,12 +3110,184 @@ dissect_rsl_ie_tfo_transp_cont(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree
}
static int
@@ -310,16 +319,16 @@ Index: wireshark/epan/dissectors/packet-rsl.c
+
+#if 0
+ switch (msg_type) {
-+ case RSL_MSG_TYPE_IPAC_BIND:
-+ case RSL_MSG_TYPE_IPAC_BIND_ACK:
-+ case RSL_MSG_TYPE_IPAC_BIND_NACK:
-+ case RSL_MSG_TYPE_IPAC_CONNECT:
-+ case RSL_MSG_TYPE_IPAC_CONNECT_ACK:
-+ case RSL_MSG_TYPE_IPAC_CONNECT_NACK:
-+ case RSL_MSG_TYPE_IPAC_DISC_IND:
-+ case RSL_MSG_TYPE_IPAC_DISC:
-+ case RSL_MSG_TYPE_IPAC_DISC_ACK:
-+ case RSL_MSG_TYPE_IPAC_DISC_NACK:
++ case RSL_MSG_TYPE_IPAC_CRCX:
++ case RSL_MSG_TYPE_IPAC_CRCX_ACK:
++ case RSL_MSG_TYPE_IPAC_CRCX_NACK:
++ case RSL_MSG_TYPE_IPAC_MDCX:
++ case RSL_MSG_TYPE_IPAC_MDCX_ACK:
++ case RSL_MSG_TYPE_IPAC_MDCX_NACK:
++ case RSL_MSG_TYPE_IPAC_DLCX_IND:
++ case RSL_MSG_TYPE_IPAC_DLCX:
++ case RSL_MSG_TYPE_IPAC_DLCX_ACK:
++ case RSL_MSG_TYPE_IPAC_DLCX_NACK:
+ case RSL_MSG_TYPE_IPAC_PDCH_ACT:
+ case RSL_MSG_TYPE_IPAC_PDCH_ACT_ACK:
+ case RSL_MSG_TYPE_IPAC_PDCH_ACT_NACK:
@@ -449,7 +458,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
+ }
+
+ switch (msg_type) {
-+ case RSL_MSG_TYPE_IPAC_BIND_ACK:
++ case RSL_MSG_TYPE_IPAC_CRCX_ACK:
+ /* Notify the RTP and RTCP dissectors about a new RTP stream */
+ src_addr.type = AT_IPv4;
+ src_addr.len = 4;
@@ -480,7 +489,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
offset++;
switch (msg_type){
-@@ -3482,6 +3856,18 @@
+@@ -3480,6 +3855,18 @@ dissct_rsl_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
/* LLP APDU 9.3.58 M LV 2-N */
offset = dissect_rsl_ie_llp_apdu(tvb, pinfo, tree, offset, TRUE);
break;
@@ -499,7 +508,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
default:
break;
}
-@@ -3489,6 +3875,40 @@
+@@ -3487,6 +3874,40 @@ dissct_rsl_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
return offset;
}
@@ -540,7 +549,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
static void
dissect_rsl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
-@@ -3516,7 +3936,6 @@
+@@ -3514,7 +3935,6 @@ dissect_rsl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
/* 9.1 Message discriminator */
proto_tree_add_item(rsl_tree, hf_rsl_msg_dsc, tvb, offset, 1, FALSE);
proto_tree_add_item(rsl_tree, hf_rsl_T_bit, tvb, offset, 1, FALSE);
@@ -548,7 +557,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
offset = dissct_rsl_msg(tvb, pinfo, rsl_tree, offset);
-@@ -3886,6 +4305,86 @@
+@@ -3884,6 +4304,86 @@ void proto_register_rsl(void)
FT_UINT8, BASE_DEC, VALS(rsl_emlpp_prio_vals), 0x03,
NULL, HFILL }
},
@@ -635,7 +644,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
};
static gint *ett[] = {
&ett_rsl,
-@@ -3943,6 +4442,14 @@
+@@ -3941,6 +4441,14 @@ void proto_register_rsl(void)
&ett_ie_meas_res_no,
&ett_ie_message_id,
&ett_ie_sys_info_type,
@@ -650,3 +659,6 @@ Index: wireshark/epan/dissectors/packet-rsl.c
};
/* Register the protocol name and description */
+--
+1.7.0.1
+