aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc
diff options
context:
space:
mode:
authorHarald Welte <laforge@netfilter.org>2009-08-09 19:53:12 +0200
committerHarald Welte <laforge@netfilter.org>2009-08-09 19:53:12 +0200
commit369cc27f3b7da119b41527de14c447f44b4bfaef (patch)
tree7791f62da62918e03cfd2f17a966d30376c1c231 /openbsc
parent5013b2ad42c1a63e2f168d3d8a6021e4763446f0 (diff)
parent3073a9f24190f005ac97f906a8654f33ee35dfc5 (diff)
Merge commit 'origin/master'
Conflicts: openbsc/src/vty_interface.c
Diffstat (limited to 'openbsc')
-rw-r--r--openbsc/include/openbsc/Makefile.am3
-rw-r--r--openbsc/include/openbsc/abis_nm.h5
-rw-r--r--openbsc/include/openbsc/abis_rsl.h2
-rw-r--r--openbsc/include/openbsc/bsc_rll.h19
-rw-r--r--openbsc/include/openbsc/chan_alloc.h3
-rw-r--r--openbsc/include/openbsc/db.h1
-rw-r--r--openbsc/include/openbsc/gsm_04_08.h13
-rw-r--r--openbsc/include/openbsc/gsm_04_11.h120
-rw-r--r--openbsc/include/openbsc/gsm_data.h55
-rw-r--r--openbsc/include/openbsc/gsm_subscriber.h3
-rw-r--r--openbsc/include/openbsc/gsm_utils.h2
-rw-r--r--openbsc/include/openbsc/signal.h16
-rw-r--r--openbsc/include/openbsc/transaction.h50
-rw-r--r--openbsc/src/Makefile.am2
-rw-r--r--openbsc/src/abis_nm.c31
-rw-r--r--openbsc/src/abis_rsl.c134
-rw-r--r--openbsc/src/bsc_hack.c5
-rw-r--r--openbsc/src/bsc_rll.c118
-rw-r--r--openbsc/src/chan_alloc.c17
-rw-r--r--openbsc/src/db.c185
-rw-r--r--openbsc/src/gsm_04_08.c62
-rw-r--r--openbsc/src/gsm_04_11.c771
-rw-r--r--openbsc/src/gsm_data.c2
-rw-r--r--openbsc/src/gsm_subscriber.c20
-rw-r--r--openbsc/src/gsm_utils.c76
-rw-r--r--openbsc/src/ipaccess-config.c73
-rw-r--r--openbsc/src/ipaccess-find.c33
-rw-r--r--openbsc/src/paging.c6
-rw-r--r--openbsc/src/transaction.c9
-rw-r--r--openbsc/src/vty_interface.c118
30 files changed, 1547 insertions, 407 deletions
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index adef573e5..c1f3f139d 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -2,4 +2,5 @@ noinst_HEADERS = abis_nm.h abis_rsl.h debug.h db.h gsm_04_08.h gsm_data.h \
gsm_subscriber.h linuxlist.h msgb.h select.h tlv.h gsm_04_11.h \
timer.h misdn.h chan_alloc.h telnet_interface.h paging.h \
subchan_demux.h trau_frame.h e1_input.h trau_mux.h signal.h \
- gsm_utils.h ipaccess.h rs232.h openbscdefines.h rtp_proxy.h
+ gsm_utils.h ipaccess.h rs232.h openbscdefines.h rtp_proxy.h \
+ bsc_rll.h
diff --git a/openbsc/include/openbsc/abis_nm.h b/openbsc/include/openbsc/abis_nm.h
index 3dc553144..d8cd9fa78 100644
--- a/openbsc/include/openbsc/abis_nm.h
+++ b/openbsc/include/openbsc/abis_nm.h
@@ -681,6 +681,11 @@ int abis_nm_software_activate(struct gsm_bts *bts, const char *fname,
int abis_nm_conn_mdrop_link(struct gsm_bts *bts, u_int8_t e1_port0, u_int8_t ts0,
u_int8_t e1_port1, u_int8_t ts1);
+int abis_nm_perform_test(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 test_nr, u_int8_t auton_report,
+ u_int8_t *phys_config, u_int16_t phys_config_len);
+
/* Siemens / BS-11 specific */
int abis_nm_bs11_reset_resource(struct gsm_bts *bts);
int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin);
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index 201c77fbc..a8734d1bf 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -476,6 +476,8 @@ int rsl_paging_cmd_subscr(struct gsm_bts *bts, u_int8_t chan_needed,
int rsl_imm_assign_cmd(struct gsm_bts *bts, u_int8_t len, u_int8_t *val);
int rsl_data_request(struct msgb *msg, u_int8_t link_id);
+int rsl_establish_request(struct gsm_lchan *lchan, u_int8_t link_id);
+int rsl_relase_request(struct gsm_lchan *lchan, u_int8_t link_id);
/* ip.access specfic RSL extensions */
int rsl_ipacc_bind(struct gsm_lchan *lchan);
diff --git a/openbsc/include/openbsc/bsc_rll.h b/openbsc/include/openbsc/bsc_rll.h
new file mode 100644
index 000000000..b2898d1b0
--- /dev/null
+++ b/openbsc/include/openbsc/bsc_rll.h
@@ -0,0 +1,19 @@
+#ifndef _BSC_RLL_H
+#define _BSC_RLL_H
+
+#include <openbsc/gsm_data.h>
+
+enum bsc_rllr_ind {
+ BSC_RLLR_IND_EST_CONF,
+ BSC_RLLR_IND_REL_IND,
+ BSC_RLLR_IND_ERR_IND,
+ BSC_RLLR_IND_TIMEOUT,
+};
+
+int rll_establish(struct gsm_lchan *lchan, u_int8_t link_id,
+ void (*cb)(struct gsm_lchan *, u_int8_t, void *,
+ enum bsc_rllr_ind),
+ void *data);
+void rll_indication(struct gsm_lchan *lchan, u_int8_t link_id, u_int8_t type);
+
+#endif /* _BSC_RLL_H */
diff --git a/openbsc/include/openbsc/chan_alloc.h b/openbsc/include/openbsc/chan_alloc.h
index d6d367c84..38855d1ee 100644
--- a/openbsc/include/openbsc/chan_alloc.h
+++ b/openbsc/include/openbsc/chan_alloc.h
@@ -37,6 +37,9 @@ void ts_free(struct gsm_bts_trx_ts *ts);
/* Find an allocated channel */
struct gsm_lchan *lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr);
+/* Find an allocated channel for a specified subscriber */
+struct gsm_lchan *lchan_for_subscr(struct gsm_subscriber *subscr);
+
/* Allocate a logical channel (SDCCH, TCH, ...) */
struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type);
diff --git a/openbsc/include/openbsc/db.h b/openbsc/include/openbsc/db.h
index c9b72659e..eef42f6fb 100644
--- a/openbsc/include/openbsc/db.h
+++ b/openbsc/include/openbsc/db.h
@@ -43,5 +43,6 @@ int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char *imei);
/* SMS store-and-forward */
int db_sms_store(struct gsm_sms *sms);
struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id);
+struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr);
int db_sms_mark_sent(struct gsm_sms *sms);
#endif /* _DB_H */
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index c93540b14..0d83cfb08 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -112,6 +112,15 @@ struct gsm48_rach_control {
u_int8_t t3;
} __attribute__ ((packed));
+/* Section 10.5.2.4 Cell Selection Parameters */
+struct gsm48_cell_sel_par {
+ u_int8_t ms_txpwr_max_ccch:5, /* GSM 05.08 MS-TXPWR-MAX-CCCH */
+ cell_resel_hyst:3; /* GSM 05.08 CELL-RESELECT-HYSTERESIS */
+ u_int8_t rxlev_acc_min:6, /* GSM 05.08 RXLEV-ACCESS-MIN */
+ neci:1,
+ acs:1;
+} __attribute__ ((packed));
+
/* Section 10.5.2.11 Control Channel Description , Figure 10.5.33 */
struct gsm48_control_channel_descr {
u_int8_t ccch_conf :3,
@@ -157,7 +166,7 @@ struct gsm48_system_information_type_3 {
struct gsm48_loc_area_id lai;
struct gsm48_control_channel_descr control_channel_desc;
u_int8_t cell_options;
- u_int8_t cell_selection[2];
+ struct gsm48_cell_sel_par cell_sel_par;
struct gsm48_rach_control rach_control;
u_int8_t s3_reset_octets[4];
} __attribute__ ((packed));
@@ -166,7 +175,7 @@ struct gsm48_system_information_type_3 {
struct gsm48_system_information_type_4 {
struct gsm48_system_information_type_header header;
struct gsm48_loc_area_id lai;
- u_int8_t cell_selection[2];
+ struct gsm48_cell_sel_par cell_sel_par;
struct gsm48_rach_control rach_control;
/* optional CBCH conditional CBCH... followed by
mandantory SI 4 Reset Octets
diff --git a/openbsc/include/openbsc/gsm_04_11.h b/openbsc/include/openbsc/gsm_04_11.h
index 12c607f54..09740e0f3 100644
--- a/openbsc/include/openbsc/gsm_04_11.h
+++ b/openbsc/include/openbsc/gsm_04_11.h
@@ -3,6 +3,21 @@
/* GSM TS 04.11 definitions */
+/* Chapter 5.2.3: SMC-CS states at the network side */
+enum gsm411_cp_state {
+ GSM411_CPS_IDLE = 0,
+ GSM411_CPS_MM_CONN_PENDING = 1, /* only MT ! */
+ GSM411_CPS_WAIT_CP_ACK = 2,
+ GSM411_CPS_MM_ESTABLISHED = 3,
+};
+
+/* Chapter 6.2.2: SMR states at the network side */
+enum gsm411_rp_state {
+ GSM411_RPS_IDLE = 0,
+ GSM411_RPS_WAIT_FOR_RP_ACK = 1,
+ GSM411_RPS_WAIT_TO_TX_RP_ACK = 3,
+};
+
/* Chapter 8.1.2 (refers to GSM 04.07 Chapter 11.2.3.1.1 */
#define GSM411_PDISC_SMS 0x09
@@ -16,20 +31,68 @@ enum gsm411_cp_ie {
GSM411_CP_IE_CAUSE = 0x02, /* 8.1.4.2. */
};
+/* Section 8.1.4.2 / Table 8.2 */
+enum gsm411_cp_cause {
+ GSM411_CP_CAUSE_NET_FAIL = 17,
+ GSM411_CP_CAUSE_CONGESTION = 22,
+ GSM411_CP_CAUSE_INV_TRANS_ID = 81,
+ GSM411_CP_CAUSE_SEMANT_INC_MSG = 95,
+ GSM411_CP_CAUSE_INV_MAND_INF = 96,
+ GSM411_CP_CAUSE_MSGTYPE_NOTEXIST= 97,
+ GSM411_CP_CAUSE_MSG_INCOMP_STATE= 98,
+ GSM411_CP_CAUSE_IE_NOTEXIST = 99,
+ GSM411_CP_CAUSE_PROTOCOL_ERR = 111,
+};
+
/* Chapter 8.2.2 */
#define GSM411_MT_RP_DATA_MO 0x00
#define GSM411_MT_RP_DATA_MT 0x01
#define GSM411_MT_RP_ACK_MO 0x02
#define GSM411_MT_RP_ACK_MT 0x03
#define GSM411_MT_RP_ERROR_MO 0x04
-#define GSM411_MT_RP_ERROR_MT 0x04
-#define GSM411_MT_RP_SMMA_MO 0x05
+#define GSM411_MT_RP_ERROR_MT 0x05
+#define GSM411_MT_RP_SMMA_MO 0x06
enum gsm411_rp_ie {
GSM411_IE_RP_USER_DATA = 0x41, /* 8.2.5.3 */
GSM411_IE_RP_CAUSE = 0x42, /* 8.2.5.4 */
};
+/* Chapter 8.2.5.4 Table 8.4 */
+enum gsm411_rp_cause {
+ /* valid only for MO */
+ GSM411_RP_CAUSE_MO_NUM_UNASSIGNED = 1,
+ GSM411_RP_CAUSE_MO_OP_DET_BARR = 8,
+ GSM411_RP_CAUSE_MO_CALL_BARRED = 10,
+ GSM411_RP_CAUSE_MO_SMS_REJECTED = 21,
+ GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER = 27,
+ GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR = 28,
+ GSM411_RP_CAUSE_MO_FACILITY_REJ = 29,
+ GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR = 30,
+ GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER = 38,
+ GSM411_RP_CAUSE_MO_TEMP_FAIL = 41,
+ GSM411_RP_CAUSE_MO_CONGESTION = 42,
+ GSM411_RP_CAUSE_MO_RES_UNAVAIL = 47,
+ GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR = 50,
+ GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL = 69,
+ GSM411_RP_CAUSE_MO_INTERWORKING = 127,
+ /* valid only for MT */
+ GSM411_RP_CAUSE_MT_MEM_EXCEEDED = 22,
+ /* valid for both directions */
+ GSM411_RP_CAUSE_INV_TRANS_REF = 81,
+ GSM411_RP_CAUSE_SEMANT_INC_MSG = 95,
+ GSM411_RP_CAUSE_INV_MAND_INF = 96,
+ GSM411_RP_CAUSE_MSGTYPE_NOTEXIST = 97,
+ GSM411_RP_CAUSE_MSG_INCOMP_STATE = 98,
+ GSM411_RP_CAUSE_IE_NOTEXIST = 99,
+ GSM411_RP_CAUSE_PROTOCOL_ERR = 111,
+};
+
+/* Chapter 10: Timers */
+#define GSM411_TMR_TR1M 40 /* 35 < x < 45 seconds */
+#define GSM411_TMR_TRAM 30 /* 25 < x < 35 seconds */
+#define GSM411_TMR_TR2M 15 /* 12 < x < 20 seconds */
+
/* Chapter 8.2.1 */
struct gsm411_rp_hdr {
u_int8_t len;
@@ -46,29 +109,6 @@ enum sms_alphabet {
DCS_8BIT_DATA,
};
-/* SMS submit PDU */
-struct sms_submit {
- u_int8_t *smsc;
- u_int8_t mti:2;
- u_int8_t vpf:2;
- u_int8_t msg_ref;
- u_int8_t pid;
- u_int8_t dcs;
- u_int8_t *vp;
- u_int8_t ud_len;
- u_int8_t *user_data;
-
- /* interpreted */
- u_int8_t mms:1;
- u_int8_t sri:1;
- u_int8_t udhi:1;
- u_int8_t rp:1;
- enum sms_alphabet alphabet;
- char dest_addr[20+1]; /* DA LV is 12 bytes max, i.e. 10 bytes BCD == 20 bytes string */
- unsigned long validity_mins;
- char decoded[256];
-};
-
/* GSM 03.40 / Chapter 9.2.3.1: TP-Message-Type-Indicator */
#define GSM340_SMS_DELIVER_SC2MS 0x00
#define GSM340_SMS_DELIVER_REP_MS2SC 0x00
@@ -141,23 +181,22 @@ struct sms_submit {
#define GSM338_DCS_1111_CLASS2_SIM 2
#define GSM338_DCS_1111_CLASS3_TE 3 /* See TS 07.05 */
-
/* SMS deliver PDU */
struct sms_deliver {
+ u_int8_t mti:2; /* message type indicator */
+ u_int8_t mms:1; /* more messages to send */
+ u_int8_t rp:1; /* reply path */
+ u_int8_t udhi:1; /* user data header indicator */
+ u_int8_t sri:1; /* status report indication */
+ u_int8_t *orig_addr; /* originating address */
+ u_int8_t pid; /* protocol identifier */
+ u_int8_t dcs; /* data coding scheme */
+ /* service centre time stamp */
+ u_int8_t ud_len; /* user data length */
+ u_int8_t *user_data; /* user data */
+
+ u_int8_t msg_ref; /* message reference */
u_int8_t *smsc;
- u_int8_t mti:2;
- u_int8_t rd:1;
- u_int8_t vpf:2;
- u_int8_t srr:1;
- u_int8_t udhi:1;
- u_int8_t rp:1;
- u_int8_t msg_ref;
- u_int8_t *orig_addr;
- u_int8_t pid;
- u_int8_t dcs;
- u_int8_t vp;
- u_int8_t ud_len;
- u_int8_t *user_data;
};
struct msgb;
@@ -166,7 +205,4 @@ int gsm0411_rcv_sms(struct msgb *msg);
int gsm0411_send_sms(struct gsm_lchan *lchan, struct sms_deliver *sms);
-struct msgb *gsm411_msgb_alloc(void);
-int gsm0411_sendmsg(struct msgb *msg);
-
#endif
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 7c8cf7cac..663c8698b 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -80,7 +80,7 @@ typedef int gsm_cbfn(unsigned int hooknum,
* Use the channel. As side effect the lchannel recycle timer
* will be started.
*/
-#define LCHAN_RELEASE_TIMEOUT 10, 0
+#define LCHAN_RELEASE_TIMEOUT 20, 0
#define use_lchan(lchan) \
do { lchan->use_count++; \
DEBUGP(DCC, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \
@@ -106,44 +106,6 @@ struct gsm_subscriber;
struct gsm_mncc;
struct rtp_socket;
-/* One transaction */
-struct gsm_trans {
- /* Entry in list of all transactions */
- struct llist_head entry;
-
- /* The protocol within which we live */
- u_int8_t protocol;
-
- /* The current transaction ID */
- u_int8_t transaction_id;
-
- /* To whom we belong, unique identifier of remote MM entity */
- struct gsm_subscriber *subscr;
-
- /* The LCHAN that we're currently using to transmit messages */
- struct gsm_lchan *lchan;
-
- /* reference from MNCC or other application */
- u_int32_t callref;
-
- union {
- struct {
-
- /* current call state */
- int state;
-
- /* current timer and message queue */
- int Tcurrent; /* current CC timer */
- int T308_second; /* used to send release again */
- struct timer_list timer;
- struct gsm_mncc msg; /* stores setup/disconnect/release message */
- } cc;
- struct {
- } sms;
- };
-};
-
-
/* Network Management State */
struct gsm_nm_state {
u_int8_t operational;
@@ -398,7 +360,18 @@ struct gsm_sms {
struct gsm_subscriber *sender;
struct gsm_subscriber *receiver;
- unsigned char header[SMS_HDR_SIZE];
+ unsigned long validity_minutes;
+ u_int8_t reply_path_req;
+ u_int8_t status_rep_req;
+ u_int8_t ud_hdr_ind;
+ u_int8_t protocol_id;
+ u_int8_t data_coding_scheme;
+ u_int8_t msg_ref;
+ char dest_addr[20+1]; /* DA LV is 12 bytes max, i.e. 10 bytes
+ * BCD == 20 bytes string */
+ u_int8_t user_data_len;
+ u_int8_t user_data[SMS_TEXT_SIZE];
+
char text[SMS_TEXT_SIZE];
};
@@ -425,7 +398,7 @@ enum gsm_e1_event {
void set_ts_e1link(struct gsm_bts_trx_ts *ts, u_int8_t e1_nr,
u_int8_t e1_ts, u_int8_t e1_ts_ss);
-enum gsm_bts_type parse_btstype(char *arg);
+enum gsm_bts_type parse_btstype(const char *arg);
const char *btstype2str(enum gsm_bts_type type);
struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
struct gsm_bts *start_bts);
diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h
index 00eeb3287..3efb20c03 100644
--- a/openbsc/include/openbsc/gsm_subscriber.h
+++ b/openbsc/include/openbsc/gsm_subscriber.h
@@ -50,6 +50,7 @@ enum gsm_subscriber_field {
GSM_SUBSCRIBER_IMSI,
GSM_SUBSCRIBER_TMSI,
GSM_SUBSCRIBER_EXTENSION,
+ GSM_SUBSCRIBER_ID,
};
enum gsm_subscriber_update_reason {
@@ -66,6 +67,8 @@ struct gsm_subscriber *subscr_get_by_imsi(struct gsm_network *net,
const char *imsi);
struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net,
const char *ext);
+struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net,
+ unsigned long long id);
int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason);
void subscr_put_channel(struct gsm_lchan *lchan);
void subscr_get_channel(struct gsm_subscriber *subscr,
diff --git a/openbsc/include/openbsc/gsm_utils.h b/openbsc/include/openbsc/gsm_utils.h
index c46837185..7cd0578ad 100644
--- a/openbsc/include/openbsc/gsm_utils.h
+++ b/openbsc/include/openbsc/gsm_utils.h
@@ -30,4 +30,6 @@
int gsm_7bit_decode(char *decoded, const u_int8_t *user_data, u_int8_t length);
int gsm_7bit_encode(u_int8_t *result, const char *data);
+int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm);
+int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl);
#endif
diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h
index 2ce812d10..1af849684 100644
--- a/openbsc/include/openbsc/signal.h
+++ b/openbsc/include/openbsc/signal.h
@@ -38,6 +38,7 @@ enum signal_subsystems {
SS_ABISIP,
SS_NM,
SS_LCHAN,
+ SS_SUBSCR,
};
/* SS_PAGING signals */
@@ -45,6 +46,14 @@ enum signal_paging {
S_PAGING_COMPLETED,
};
+/* SS_SMS signals */
+enum signal_sms {
+ S_SMS_SUBMITTED, /* A SMS has been successfully submitted to us */
+ S_SMS_DELIVERED, /* A SMS has been successfully delivered to a MS */
+ S_SMS_SMMA, /* A MS tells us it has more space available */
+ S_SMS_MEM_EXCEEDED, /* A MS tells us it has no more space available */
+};
+
/* SS_ABISIP signals */
enum signal_abisip {
S_ABISIP_BIND_ACK,
@@ -57,6 +66,7 @@ enum signal_nm {
S_NM_FAIL_REP, /* GSM 12.21 failure event report */
S_NM_NACK, /* GSM 12.21 various NM_MT_*_NACK happened */
S_NM_IPACC_NACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_NACK happened */
+ S_NM_TEST_REP, /* GSM 12.21 Test Report */
};
/* SS_LCHAN signals */
@@ -69,6 +79,12 @@ enum signal_lchan {
S_LCHAN_UNEXPECTED_RELEASE,
};
+/* SS_SUBSCR signals */
+enum signal_subscr {
+ S_SUBSCR_ATTACHED,
+ S_SUBSCR_DETACHED,
+};
+
typedef int signal_cbfn(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data);
diff --git a/openbsc/include/openbsc/transaction.h b/openbsc/include/openbsc/transaction.h
index 1450dbca5..5678c83e7 100644
--- a/openbsc/include/openbsc/transaction.h
+++ b/openbsc/include/openbsc/transaction.h
@@ -3,8 +3,56 @@
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_subscriber.h>
+#include <openbsc/linuxlist.h>
+#include <openbsc/gsm_04_11.h>
-struct gsm_trans *trans_find_by_id(struct gsm_lchan *lchan, u_int8_t trans_id);
+/* One transaction */
+struct gsm_trans {
+ /* Entry in list of all transactions */
+ struct llist_head entry;
+
+ /* The protocol within which we live */
+ u_int8_t protocol;
+
+ /* The current transaction ID */
+ u_int8_t transaction_id;
+
+ /* To whom we belong, unique identifier of remote MM entity */
+ struct gsm_subscriber *subscr;
+
+ /* The LCHAN that we're currently using to transmit messages */
+ struct gsm_lchan *lchan;
+
+ /* reference from MNCC or other application */
+ u_int32_t callref;
+
+ union {
+ struct {
+
+ /* current call state */
+ int state;
+
+ /* current timer and message queue */
+ int Tcurrent; /* current CC timer */
+ int T308_second; /* used to send release again */
+ struct timer_list timer;
+ struct gsm_mncc msg; /* stores setup/disconnect/release message */
+ } cc;
+ struct {
+ int is_mt; /* is this a MO (0) or MT (1) transfer */
+ enum gsm411_cp_state cp_state;
+ enum gsm411_rp_state rp_state;
+
+ struct timer_list timer;
+ struct gsm_sms *sms;
+ } sms;
+ };
+};
+
+
+
+struct gsm_trans *trans_find_by_id(struct gsm_subscriber *subscr,
+ u_int8_t proto, u_int8_t trans_id);
struct gsm_trans *trans_find_by_callref(struct gsm_network *net,
u_int32_t callref);
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index ed63c8e31..82415af94 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -10,7 +10,7 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c mncc.c \
gsm_04_11.c telnet_interface.c subchan_demux.c \
trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c \
- transaction.c rtp_proxy.c
+ transaction.c rtp_proxy.c bsc_rll.c
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index 0eb8657db..31f1df009 100644
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -801,6 +801,10 @@ static int abis_nm_rcvmsg_report(struct msgb *mb)
rx_fail_evt_rep(mb);
dispatch_signal(SS_NM, S_NM_FAIL_REP, mb);
break;
+ case NM_MT_TEST_REP:
+ DEBUGPC(DNM, "Test Report\n");
+ dispatch_signal(SS_NM, S_NM_TEST_REP, mb);
+ break;
default:
DEBUGPC(DNM, "reporting NM MT 0x%02x\n", mt);
break;
@@ -1790,6 +1794,33 @@ int abis_nm_conn_mdrop_link(struct gsm_bts *bts, u_int8_t e1_port0, u_int8_t ts0
return abis_nm_sendmsg(bts, msg);
}
+/* Chapter 8.7.1 */
+int abis_nm_perform_test(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 test_nr, u_int8_t auton_report,
+ u_int8_t *phys_config, u_int16_t phys_config_len)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ int len = 4; /* 2 TV attributes */
+
+ DEBUGP(DNM, "PEFORM TEST\n");
+
+ if (phys_config_len)
+ len += 3 + phys_config_len;
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, len, NM_MT_PERF_TEST,
+ obj_class, bts_nr, trx_nr, ts_nr);
+ msgb_tv_put(msg, NM_ATT_TEST_NO, test_nr);
+ msgb_tv_put(msg, NM_ATT_AUTON_REPORT, auton_report);
+ if (phys_config_len)
+ msgb_tl16v_put(msg, NM_ATT_PHYS_CONF, phys_config_len,
+ phys_config);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
int abis_nm_event_reports(struct gsm_bts *bts, int on)
{
if (on == 0)
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 1a2c58b16..8f6037f83 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -30,8 +30,10 @@
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_04_08.h>
+#include <openbsc/gsm_utils.h>
#include <openbsc/abis_rsl.h>
#include <openbsc/chan_alloc.h>
+#include <openbsc/bsc_rll.h>
#include <openbsc/debug.h>
#include <openbsc/tlv.h>
#include <openbsc/paging.h>
@@ -430,78 +432,6 @@ int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db)
return abis_rsl_sendmsg(msg);
}
-/* determine power control level for given dBm value, as indicated
- * by the tables in chapter 4.1.1 of GSM TS 05.05 */
-static int ms_pwr_ctl_lvl(struct gsm_bts *bts, unsigned int dbm)
-{
- switch (bts->band) {
- case GSM_BAND_400:
- case GSM_BAND_900:
- case GSM_BAND_850:
- if (dbm >= 39)
- return 0;
- else if (dbm < 5)
- return 19;
- else
- return 2 + ((39 - dbm) / 2);
- break;
- case GSM_BAND_1800:
- if (dbm >= 36)
- return 29;
- else if (dbm >= 34)
- return 30;
- else if (dbm >= 32)
- return 31;
- else
- return (30 - dbm) / 2;
- break;
- case GSM_BAND_1900:
- if (dbm >= 33)
- return 30;
- else if (dbm >= 32)
- return 31;
- else
- return (30 - dbm) / 2;
- break;
- }
- return -EINVAL;
-}
-
-static int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl)
-{
- lvl &= 0x1f;
-
- switch (band) {
- case GSM_BAND_400:
- case GSM_BAND_900:
- case GSM_BAND_850:
- if (lvl < 2)
- return 39;
- else if (lvl < 20)
- return 39 - ((lvl - 2) * 2) ;
- else
- return 5;
- break;
- case GSM_BAND_1800:
- if (lvl < 16)
- return 30 - (lvl * 2);
- else if (lvl < 29)
- return 0;
- else
- return 36 - ((lvl - 29) * 2);
- break;
- case GSM_BAND_1900:
- if (lvl < 16)
- return 30 - (lvl * 2);
- else if (lvl < 30)
- return -EINVAL;
- else
- return 33 - (lvl - 30);
- break;
- }
- return -EINVAL;
-}
-
int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm)
{
struct abis_rsl_dchan_hdr *dh;
@@ -509,7 +439,7 @@ int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm)
u_int8_t chan_nr = lchan2chan_nr(lchan);
int ctl_lvl;
- ctl_lvl = ms_pwr_ctl_lvl(lchan->ts->trx->bts, dbm);
+ ctl_lvl = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, dbm);
if (ctl_lvl < 0)
return ctl_lvl;
@@ -840,6 +770,45 @@ int rsl_data_request(struct msgb *msg, u_int8_t link_id)
return abis_rsl_sendmsg(msg);
}
+/* Send "ESTABLISH REQUEST" message with given L3 Info payload */
+/* Chapter 8.3.1 */
+int rsl_establish_request(struct gsm_lchan *lchan, u_int8_t link_id)
+{
+ struct msgb *msg = rsl_msgb_alloc();
+ struct abis_rsl_rll_hdr *rh;
+
+ rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
+ init_llm_hdr(rh, RSL_MT_EST_REQ);
+ //rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
+ rh->chan_nr = lchan2chan_nr(lchan);
+ rh->link_id = link_id;
+
+ msg->trx = lchan->ts->trx;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+/* Chapter 8.3.7 Request the release of multiframe mode of RLL connection.
+ This is what higher layers should call. The BTS then responds with
+ RELEASE CONFIRM, which we in turn use to trigger RSL CHANNEL RELEASE,
+ which in turn is acknowledged by RSL CHANNEL RELEASE ACK, which calls
+ lchan_free() */
+int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id)
+{
+ struct msgb *msg = rsl_msgb_alloc();
+ struct abis_rsl_rll_hdr *rh;
+
+ rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
+ init_llm_hdr(rh, RSL_MT_REL_REQ);
+ //rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
+ rh->chan_nr = lchan2chan_nr(lchan);
+ rh->link_id = link_id;
+
+ msg->trx = lchan->ts->trx;
+
+ return abis_rsl_sendmsg(msg);
+}
+
/* Chapter 8.4.2: Channel Activate Acknowledge */
static int rsl_rx_chan_act_ack(struct msgb *msg)
{
@@ -868,6 +837,7 @@ static int rsl_rx_chan_act_nack(struct msgb *msg)
print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
TLVP_LEN(&tp, RSL_IE_CAUSE));
+ lchan_free(msg->lchan);
return 0;
}
@@ -1090,7 +1060,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
arfcn = lchan->ts->trx->arfcn;
subch = lchan->nr;
- lchan->ms_power = ms_pwr_ctl_lvl(bts, 20 /* dBm == 100mW */);
+ lchan->ms_power = ms_pwr_ctl_lvl(bts->band, 20 /* dBm == 100mW */);
lchan->bs_power = 0x0f; /* 30dB reduction */
lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
rsl_chan_activate_lchan(lchan, 0x00, rqd_ta);
@@ -1191,6 +1161,8 @@ static int rsl_rx_rll_err_ind(struct msgb *msg)
u_int8_t *rlm_cause = rllh->data;
DEBUGPC(DRLL, "ERROR INDICATION cause=0x%02x\n", rlm_cause[1]);
+
+ rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND);
if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED)
return rsl_chan_release(msg->lchan);
@@ -1233,11 +1205,25 @@ static int abis_rsl_rx_rll(struct msgb *msg)
return gsm0408_rcvmsg(msg);
}
break;
+ case RSL_MT_EST_CONF:
+ DEBUGPC(DRLL, "ESTABLISH CONFIRM\n");
+ rll_indication(msg->lchan, rllh->link_id,
+ BSC_RLLR_IND_EST_CONF);
+ break;
case RSL_MT_REL_IND:
+ /* BTS informs us of having received DISC from MS */
DEBUGPC(DRLL, "RELEASE INDICATION\n");
+ rll_indication(msg->lchan, rllh->link_id,
+ BSC_RLLR_IND_REL_IND);
+ /* we can now releae the channel on the BTS/Abis side */
+ rsl_chan_release(msg->lchan);
break;
case RSL_MT_REL_CONF:
+ /* BTS informs us of having received UA from MS,
+ * in response to DISC that we've sent earlier */
DEBUGPC(DRLL, "RELEASE CONFIRMATION\n");
+ /* we can now releae the channel on the BTS/Abis side */
+ rsl_chan_release(msg->lchan);
break;
case RSL_MT_ERROR_IND:
rc = rsl_rx_rll_err_ind(msg);
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index 9e0a26096..0f0f4c8c7 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -37,6 +37,7 @@
#include <openbsc/db.h>
#include <openbsc/timer.h>
#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_utils.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/select.h>
#include <openbsc/abis_rsl.h>
@@ -979,6 +980,10 @@ static void patch_tables(struct gsm_bts *bts)
/* patch TSC */
si4[15] &= ~0xe0;
si4[15] |= (bts->tsc & 7) << 5;
+
+ /* patch MS max power for CCH */
+ type_4->cell_sel_par.ms_txpwr_max_ccch =
+ ms_pwr_ctl_lvl(bts->band, 20 /* dBm == 100mW */);
}
diff --git a/openbsc/src/bsc_rll.c b/openbsc/src/bsc_rll.c
new file mode 100644
index 000000000..780a84e39
--- /dev/null
+++ b/openbsc/src/bsc_rll.c
@@ -0,0 +1,118 @@
+/* GSM BSC Radio Link Layer API
+ * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
+
+/* (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 <errno.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/talloc.h>
+#include <openbsc/timer.h>
+#include <openbsc/linuxlist.h>
+#include <openbsc/bsc_rll.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/chan_alloc.h>
+#include <openbsc/abis_rsl.h>
+
+struct bsc_rll_req {
+ struct llist_head list;
+ struct timer_list timer;
+
+ struct gsm_lchan *lchan;
+ u_int8_t link_id;
+
+ void (*cb)(struct gsm_lchan *lchan, u_int8_t link_id,
+ void *data, enum bsc_rllr_ind);
+ void *data;
+};
+
+/* we only compare C1, C2 and SAPI */
+#define LINKID_MASK 0xC7
+
+static LLIST_HEAD(bsc_rll_reqs);
+
+static void complete_rllr(struct bsc_rll_req *rllr, enum bsc_rllr_ind type)
+{
+ llist_del(&rllr->list);
+ put_lchan(rllr->lchan);
+ rllr->cb(rllr->lchan, rllr->link_id, rllr->data, type);
+ talloc_free(rllr);
+}
+
+static void timer_cb(void *_rllr)
+{
+ struct bsc_rll_req *rllr = _rllr;
+
+ complete_rllr(rllr, BSC_RLLR_IND_TIMEOUT);
+}
+
+/* establish a RLL connection with given SAPI / priority */
+int rll_establish(struct gsm_lchan *lchan, u_int8_t sapi,
+ void (*cb)(struct gsm_lchan *, u_int8_t, void *,
+ enum bsc_rllr_ind),
+ void *data)
+{
+ struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req);
+ u_int8_t link_id;
+ if (!rllr)
+ return -ENOMEM;
+
+ link_id = sapi;
+
+ /* If we are a TCH and not in signalling mode, we need to
+ * indicate that the new RLL connection is to be made on the SACCH */
+ if ((lchan->type == GSM_LCHAN_TCH_F ||
+ lchan->type == GSM_LCHAN_TCH_H) &&
+ lchan->rsl_cmode != RSL_CMOD_SPD_SIGN)
+ link_id |= 0x40;
+
+ use_lchan(lchan);
+ rllr->lchan = lchan;
+ rllr->link_id = link_id;
+ rllr->cb = cb;
+ rllr->data = data;
+
+ llist_add(&rllr->list, &bsc_rll_reqs);
+
+ rllr->timer.cb = &timer_cb;
+ rllr->timer.data = rllr;
+
+ bsc_schedule_timer(&rllr->timer, 10, 0);
+
+ /* send the RSL RLL ESTablish REQuest */
+ return rsl_establish_request(rllr->lchan, rllr->link_id);
+}
+
+/* Called from RSL code in case we have received an indication regarding
+ * any RLL link */
+void rll_indication(struct gsm_lchan *lchan, u_int8_t link_id, u_int8_t type)
+{
+ struct bsc_rll_req *rllr, *rllr2;
+
+ llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) {
+ if (rllr->lchan == lchan &&
+ (rllr->link_id & LINKID_MASK) == (link_id & LINKID_MASK)) {
+ bsc_del_timer(&rllr->timer);
+ complete_rllr(rllr, type);
+ return;
+ }
+ }
+}
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index e3d6ae67e..860355a02 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -239,7 +239,7 @@ int lchan_auto_release(struct gsm_lchan *lchan)
}
DEBUGP(DRLL, "Recycling the channel with: %d (%x)\n", lchan->nr, lchan->nr);
- rsl_chan_release(lchan);
+ rsl_release_request(lchan, 0);
return 1;
}
@@ -269,3 +269,18 @@ struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr)
return NULL;
}
+
+struct gsm_lchan *lchan_for_subscr(struct gsm_subscriber *subscr)
+{
+ struct gsm_bts *bts;
+ struct gsm_network *net = subscr->net;
+ struct gsm_lchan *lchan;
+
+ llist_for_each_entry(bts, &net->bts_list, list) {
+ lchan = lchan_find(bts, subscr);
+ if (lchan)
+ return lchan;
+ }
+
+ return 0;
+}
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index fc2950a36..51f94b654 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -22,6 +22,7 @@
#include <openbsc/gsm_data.h>
#include <openbsc/db.h>
+#include <openbsc/talloc.h>
#include <libgen.h>
#include <stdio.h>
@@ -43,7 +44,7 @@ static char *create_stmts[] = {
"INSERT OR IGNORE INTO Meta "
"(key, value) "
"VALUES "
- "('revision', '1')",
+ "('revision', '2')",
"CREATE TABLE IF NOT EXISTS Subscriber ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"created TIMESTAMP NOT NULL, "
@@ -74,13 +75,24 @@ static char *create_stmts[] = {
"UNIQUE (subscriber_id, equipment_id) "
")",
"CREATE TABLE IF NOT EXISTS SMS ("
+ /* metadata, not part of sms */
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"created TIMESTAMP NOT NULL, "
"sent TIMESTAMP, "
- "sender_id NUMERIC NOT NULL, "
- "receiver_id NUMERIC NOT NULL, "
- "header NUMERIC, "
- "text TEXT NOT NULL "
+ "sender_id INTEGER NOT NULL, "
+ "receiver_id INTEGER NOT NULL, "
+ /* data directly copied/derived from SMS */
+ "valid_until TIMESTAMP, "
+ "reply_path_req INTEGER NOT NULL, "
+ "status_rep_req INTEGER NOT NULL, "
+ "protocol_id INTEGER NOT NULL, "
+ "data_coding_scheme INTEGER NOT NULL, "
+ "ud_hdr_ind INTEGER NOT NULL, "
+ "dest_addr TEXT, "
+ "user_data BLOB, " /* TP-UD */
+ /* additional data, interpreted from SMS */
+ "header BLOB, " /* UD Header */
+ "text TEXT " /* decoded UD after UDH */
")",
"CREATE TABLE IF NOT EXISTS VLR ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
@@ -97,6 +109,30 @@ void db_error_func(dbi_conn conn, void* data) {
printf("DBI: %s\n", msg);
}
+static int check_db_revision(void)
+{
+ dbi_result result;
+ const char *rev;
+
+ result = dbi_conn_query(conn,
+ "SELECT value FROM Meta WHERE key='revision'");
+ if (!result)
+ return -EINVAL;
+
+ if (!dbi_result_next_row(result)) {
+ dbi_result_free(result);
+ return -EINVAL;
+ }
+ rev = dbi_result_get_string(result, "value");
+ if (!rev || atoi(rev) != 2) {
+ dbi_result_free(result);
+ return -EINVAL;
+ }
+
+ dbi_result_free(result);
+ return 0;
+}
+
int db_init(const char *name) {
dbi_initialize(NULL);
conn = dbi_conn_new("sqlite3");
@@ -121,16 +157,25 @@ int db_init(const char *name) {
dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname));
dbi_conn_set_option(conn, "dbname", basename(db_basename));
- if (dbi_conn_connect(conn) < 0) {
- free(db_dirname);
- free(db_basename);
- db_dirname = db_basename = NULL;
- return 1;
+ if (dbi_conn_connect(conn) < 0)
+ goto out_err;
+
+ if (check_db_revision() < 0) {
+ fprintf(stderr, "Database schema revision invalid, "
+ "please update your database schema\n");
+ goto out_err;
}
return 0;
+
+out_err:
+ free(db_dirname);
+ free(db_basename);
+ db_dirname = db_basename = NULL;
+ return -1;
}
+
int db_prepare() {
dbi_result result;
int i;
@@ -235,6 +280,13 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
);
free(quoted);
break;
+ case GSM_SUBSCRIBER_ID:
+ dbi_conn_quote_string_copy(conn, id, &quoted);
+ result = dbi_conn_queryf(conn,
+ "SELECT * FROM Subscriber "
+ "WHERE id = %s ", quoted);
+ free(quoted);
+ break;
default:
printf("DB: Unknown query selector for Subscriber.\n");
return NULL;
@@ -445,17 +497,33 @@ int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IM
int db_sms_store(struct gsm_sms *sms)
{
dbi_result result;
- char *q_text;
+ char *q_text, *q_daddr;
+ unsigned char *q_udata;
+ char *validity_timestamp = "2222-2-2";
+
+ /* FIXME: generate validity timestamp based on validity_minutes */
dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text);
+ dbi_conn_quote_string_copy(conn, (char *)sms->dest_addr, &q_daddr);
+ dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len,
+ &q_udata);
+ /* FIXME: correct validity period */
result = dbi_conn_queryf(conn,
"INSERT INTO SMS "
- "(created,sender_id,receiver_id,header,text) VALUES "
- "(datetime('now'),%llu,%llu,%s,%s)",
+ "(created, sender_id, receiver_id, valid_until, "
+ "reply_path_req, status_rep_req, protocol_id, "
+ "data_coding_scheme, ud_hdr_ind, dest_addr, "
+ "user_data, text) VALUES "
+ "(datetime('now'), %llu, %llu, %u, "
+ "%u, %u, %u, %u, %u, %s, %s, %s)",
sms->sender->id,
- sms->receiver ? sms->receiver->id : 0,
- NULL, q_text);
+ sms->receiver ? sms->receiver->id : 0, validity_timestamp,
+ sms->reply_path_req, sms->status_rep_req, sms->protocol_id,
+ sms->data_coding_scheme, sms->ud_hdr_ind,
+ q_daddr, q_udata, q_text);
free(q_text);
+ free(q_daddr);
+ free(q_udata);
if (!result)
return -EIO;
@@ -464,28 +532,101 @@ int db_sms_store(struct gsm_sms *sms)
return 0;
}
+static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result)
+{
+ struct gsm_sms *sms = sms_alloc();
+ long long unsigned int sender_id, receiver_id;
+ const char *text, *daddr;
+ const unsigned char *user_data;
+
+ if (!sms)
+ return NULL;
+
+ sms->id = dbi_result_get_ulonglong(result, "id");
+
+ sender_id = dbi_result_get_ulonglong(result, "sender_id");
+ sms->sender = subscr_get_by_id(net, sender_id);
+
+ receiver_id = dbi_result_get_ulonglong(result, "receiver_id");
+ sms->receiver = subscr_get_by_id(net, receiver_id);
+
+ /* FIXME: validity */
+ /* FIXME: those should all be get_uchar, but sqlite3 is braindead */
+ sms->reply_path_req = dbi_result_get_uint(result, "reply_path_req");
+ sms->status_rep_req = dbi_result_get_uint(result, "status_rep_req");
+ sms->ud_hdr_ind = dbi_result_get_uint(result, "ud_hdr_ind");
+ sms->protocol_id = dbi_result_get_uint(result, "protocol_id");
+ sms->data_coding_scheme = dbi_result_get_uint(result,
+ "data_coding_scheme");
+ /* sms->msg_ref is temporary and not stored in DB */
+
+ daddr = dbi_result_get_string(result, "dest_addr");
+ if (daddr) {
+ strncpy(sms->dest_addr, daddr, sizeof(sms->dest_addr));
+ sms->dest_addr[sizeof(sms->dest_addr)-1] = '\0';
+ }
+
+ sms->user_data_len = dbi_result_get_field_length(result, "user_data");
+ user_data = dbi_result_get_binary(result, "user_data");
+ if (sms->user_data_len > sizeof(sms->user_data))
+ sms->user_data_len = sizeof(sms->user_data);
+ memcpy(sms->user_data, user_data, sms->user_data_len);
+
+ text = dbi_result_get_string(result, "text");
+ if (text) {
+ strncpy(sms->text, text, sizeof(sms->text));
+ sms->text[sizeof(sms->text)-1] = '\0';
+ }
+ return sms;
+}
+
/* retrieve the next unsent SMS with ID >= min_id */
struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
{
dbi_result result;
- struct gsm_sms *sms = malloc(sizeof(*sms));
+ struct gsm_sms *sms;
- if (!sms) {
- free(sms);
+ result = dbi_conn_queryf(conn,
+ "SELECT * FROM SMS "
+ "WHERE id >= %llu AND sent is NULL ORDER BY id",
+ min_id);
+ if (!result)
+ return NULL;
+
+ if (!dbi_result_next_row(result)) {
+ dbi_result_free(result);
return NULL;
}
+ sms = sms_from_result(net, result);
+
+ dbi_result_free(result);
+
+ return sms;
+}
+
+/* retrieve the next unsent SMS with ID >= min_id */
+struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr)
+{
+ dbi_result result;
+ struct gsm_sms *sms;
+
result = dbi_conn_queryf(conn,
"SELECT * FROM SMS "
- "WHERE id >= %llu ORDER BY id", min_id);
- if (!result) {
- free(sms);
+ "WHERE receiver_id = %llu AND sent is NULL ORDER BY id",
+ subscr->id);
+ if (!result)
+ return NULL;
+
+ if (!dbi_result_next_row(result)) {
+ dbi_result_free(result);
return NULL;
}
- /* FIXME: fill gsm_sms from database */
+ sms = sms_from_result(subscr->net, result);
dbi_result_free(result);
+
return sms;
}
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 078e95f3b..967e4ce58 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -245,7 +245,7 @@ static void parse_meas_rep(struct gsm_meas_rep *rep, const u_int8_t *data,
rep->flags |= MEAS_REP_F_BA1;
if (data[0] & 0x40)
rep->flags |= MEAS_REP_F_DTX;
- if (data[1] & 0x40)
+ if ((data[1] & 0x40) == 0x00)
rep->flags |= MEAS_REP_F_VALID;
rep->rxlev_full = data[0] & 0x3f;
@@ -364,11 +364,18 @@ static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg)
u_int32_t tmsi;
if (authorize_subscriber(lchan->loc_operation, lchan->subscr)) {
+ int rc;
+
db_subscriber_alloc_tmsi(lchan->subscr);
- subscr_update(lchan->subscr, msg->trx->bts, GSM_SUBSCRIBER_UPDATE_ATTACHED);
tmsi = strtoul(lchan->subscr->tmsi, NULL, 10);
release_loc_updating_req(lchan);
- return gsm0408_loc_upd_acc(msg->lchan, tmsi);
+ rc = gsm0408_loc_upd_acc(msg->lchan, tmsi);
+ /* call subscr_update after putting the loc_upd_acc
+ * in the transmit queue, since S_SUBSCR_ATTACHED might
+ * trigger further action like SMS delivery */
+ subscr_update(lchan->subscr, msg->trx->bts,
+ GSM_SUBSCRIBER_UPDATE_ATTACHED);
+ return rc;
}
return 0;
@@ -1053,6 +1060,7 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi)
ret = gsm48_sendmsg(msg, NULL);
+ /* send MM INFO with network name */
ret = gsm48_tx_mm_info(lchan);
return ret;
@@ -1610,7 +1618,7 @@ static int gsm48_rr_rx_pag_resp(struct msgb *msg)
return -EINVAL;
}
DEBUGP(DRR, "<- Channel was requested by %s\n",
- subscr->name ? subscr->name : subscr->imsi);
+ subscr->name && strlen(subscr->name) ? subscr->name : subscr->imsi);
subscr->equipment.classmark2_len = *classmark2_lv;
memcpy(subscr->equipment.classmark2, classmark2_lv+1, *classmark2_lv);
@@ -1783,6 +1791,7 @@ int gsm48_send_rr_release(struct gsm_lchan *lchan)
/* Send actual release request to MS */
gsm48_sendmsg(msg, NULL);
+ /* FIXME: Start Timer T3109 */
/* Deactivate the SACCH on the BTS side */
return rsl_deact_sacch(lchan);
@@ -3374,13 +3383,11 @@ static struct downstate {
int mncc_send(struct gsm_network *net, int msg_type, void *arg)
{
- int i, j, k, l, rc = 0;
+ int i, rc = 0;
struct gsm_trans *trans = NULL, *transt;
struct gsm_subscriber *subscr;
- struct gsm_lchan *lchan = NULL, *lchant;
+ struct gsm_lchan *lchan = NULL;
struct gsm_bts *bts = NULL;
- struct gsm_bts_trx *trx;
- struct gsm_bts_trx_ts *ts;
struct gsm_mncc *data = arg, rel;
/* handle special messages */
@@ -3463,23 +3470,7 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
return -ENOMEM;
}
/* Find lchan */
- for (i = 0; i < net->num_bts; i++) {
- bts = gsm_bts_num(net, i);
- for (j = 0; j < bts->num_trx; j++) {
- trx = gsm_bts_trx_num(bts, j);
- for (k = 0; k < TRX_NR_TS; k++) {
- ts = &trx->ts[k];
- for (l = 0; l < TS_MAX_LCHAN; l++) {
- lchant = &ts->lchan[l];
- if (lchant->subscr == subscr) {
- lchan = lchant;
- break;
- }
- }
- }
- }
- }
-
+ lchan = lchan_for_subscr(subscr);
/* If subscriber has no lchan */
if (!lchan) {
/* find transaction with this subscriber already paging */
@@ -3498,22 +3489,9 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
}
/* store setup informations until paging was successfull */
memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc));
- /* start paging subscriber on all BTS with her location */
- subscr->net = net;
- bts = NULL;
- do {
- bts = gsm_bts_by_lac(net, subscr->lac, bts);
- if (!bts)
- break;
- DEBUGP(DCC, "(bts %d trx - ts - ti -- sub %s) "
- "Received '%s' from MNCC with "
- "unallocated channel, paging.\n",
- bts->nr, data->called.number,
- get_mncc_name(msg_type));
- /* Trigger paging */
- paging_request(net, subscr, RSL_CHANNEED_TCH_F,
- setup_trig_pag_evt, subscr);
- } while (1);
+ /* Trigger paging */
+ paging_request(net, subscr, RSL_CHANNEED_TCH_F,
+ setup_trig_pag_evt, subscr);
return 0;
}
/* Assign lchan */
@@ -3632,7 +3610,7 @@ static int gsm0408_rcv_cc(struct msgb *msg)
}
/* Find transaction */
- trans = trans_find_by_id(lchan, transaction_id);
+ trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_CC, transaction_id);
DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) "
"Received '%s' from MS in state %d (%s)\n",
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 9218783ff..9a5a08cde 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -28,6 +28,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <time.h>
#include <netinet/in.h>
#include <openbsc/msgb.h>
@@ -42,45 +43,103 @@
#include <openbsc/signal.h>
#include <openbsc/db.h>
#include <openbsc/talloc.h>
+#include <openbsc/transaction.h>
+#include <openbsc/paging.h>
+#include <openbsc/bsc_rll.h>
#define GSM411_ALLOC_SIZE 1024
#define GSM411_ALLOC_HEADROOM 128
-static void *tall_sms_ctx;
+#define UM_SAPI_SMS 3 /* See GSM 04.05/04.06 */
+
static void *tall_gsms_ctx;
+static u_int32_t new_callref = 0x40000001;
+
+struct gsm_sms *sms_alloc(void)
+{
+ return talloc_zero(tall_gsms_ctx, struct gsm_sms);
+}
+
+void sms_free(struct gsm_sms *sms)
+{
+ /* drop references to subscriber structure */
+ if (sms->sender)
+ subscr_put(sms->sender);
+ if (sms->receiver)
+ subscr_put(sms->receiver);
+
+ talloc_free(sms);
+}
+
struct msgb *gsm411_msgb_alloc(void)
{
return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM,
"GSM 04.11");
}
-int gsm0411_sendmsg(struct msgb *msg)
+static int gsm411_sendmsg(struct msgb *msg)
{
if (msg->lchan)
msg->trx = msg->lchan->ts->trx;
msg->l3h = msg->data;
- return rsl_data_request(msg, 0);
+ DEBUGP(DSMS, "GSM4.11 TX %s\n", hexdump(msg->data, msg->len));
+
+ return rsl_data_request(msg, UM_SAPI_SMS);
}
+/* Prefix msg with a 04.08/04.11 CP header */
+static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
+ u_int8_t msg_type)
+{
+ struct gsm48_hdr *gh;
-#if 0
-static u_int8_t gsm0411_tpdu_from_sms(u_int8_t *tpdu, struct sms_deliver *sms)
+ gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
+ /* Outgoing needs the highest bit set */
+ gh->proto_discr = trans->protocol | (trans->transaction_id<<4);
+ gh->msg_type = msg_type;
+
+ /* assign the outgoing lchan */
+ msg->lchan = trans->lchan;
+
+ /* mobile originating */
+ switch (gh->msg_type) {
+ case GSM411_MT_CP_DATA:
+ /* 5.2.3.1.2: enter MO-wait for CP-ack */
+ /* 5.2.3.2.3: enter MT-wait for CP-ACK */
+ trans->sms.cp_state = GSM411_CPS_WAIT_CP_ACK;
+ break;
+ }
+
+ return gsm411_sendmsg(msg);
+}
+
+/* Prefix msg with a RP-DATA header and send as CP-DATA */
+static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
+ u_int8_t rp_msg_type, u_int8_t rp_msg_ref)
{
+ struct gsm411_rp_hdr *rp;
+
+ /* GSM 04.11 RP-DATA header */
+ rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
+ rp->len = msg->len;
+ rp->msg_type = rp_msg_type;
+ rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
+
+ return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA);
}
-#endif
-static unsigned long gsm340_validity_period(struct sms_submit *sms)
+static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
{
u_int8_t vp;
unsigned long minutes;
- switch (sms->vpf) {
+ switch (sms_vpf) {
case GSM340_TP_VPF_RELATIVE:
/* Chapter 9.2.3.12.1 */
- vp = *(sms->vp);
+ vp = *(sms_vp);
if (vp <= 143)
minutes = vp + 1 * 5;
else if (vp <= 167)
@@ -139,57 +198,146 @@ enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs)
return alpha;
}
-static int gsm340_rx_sms_submit(struct msgb *msg, struct sms_submit *sms,
- struct gsm_sms *gsms)
+static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms)
{
if (db_sms_store(gsms) != 0) {
DEBUGP(DSMS, "Failed to store SMS in Database\n");
- talloc_free(sms);
- talloc_free(gsms);
- return -EIO;
+ return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
}
+ /* dispatch a signal to tell higher level about it */
+ dispatch_signal(SS_SMS, S_SMS_SUBMITTED, gsms);
return 0;
}
-/* process an incoming TPDU (called from RP-DATA) */
+/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
+static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len,
+ struct gsm_subscriber *subscr)
+{
+ int len_in_bytes;
+
+ oa[1] = 0xb9; /* networks-specific number, private numbering plan */
+
+ len_in_bytes = encode_bcd_number(oa, oa_len, 1, subscr->extension);
+
+ /* GSM 03.40 tells us the length is in 'useful semi-octets' */
+ oa[0] = strlen(subscr->extension) & 0xff;
+
+ return len_in_bytes;
+}
+
+static u_int8_t bcdify(u_int8_t value)
+{
+ u_int8_t ret;
+
+ ret = value / 10;
+ ret |= (value % 10) << 4;
+
+ return ret;
+}
+
+/* Generate 03.40 TP-SCTS */
+static void gsm340_gen_scts(u_int8_t *scts, time_t time)
+{
+ struct tm *tm = localtime(&time);
+
+ *scts++ = bcdify(tm->tm_year % 100);
+ *scts++ = bcdify(tm->tm_mon);
+ *scts++ = bcdify(tm->tm_mday);
+ *scts++ = bcdify(tm->tm_hour);
+ *scts++ = bcdify(tm->tm_min);
+ *scts++ = bcdify(tm->tm_sec);
+ *scts++ = 0; /* FIXME: timezone */
+}
+
+/* generate a msgb containing a TPDU derived from struct gsm_sms,
+ * returns total size of TPDU */
+static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
+{
+ u_int8_t *smsp;
+ u_int8_t oa[12]; /* max len per 03.40 */
+ u_int8_t oa_len = 0;
+ unsigned int old_msg_len = msg->len;
+
+ /* generate first octet with masked bits */
+ smsp = msgb_put(msg, 1);
+ /* TP-MTI (message type indicator) */
+ *smsp = GSM340_SMS_DELIVER_SC2MS;
+ /* TP-MMS (more messages to send) */
+ if (0 /* FIXME */)
+ *smsp |= 0x04;
+ /* TP-SRI(deliver)/SRR(submit) */
+ if (sms->status_rep_req)
+ *smsp |= 0x20;
+ /* TP-UDHI (indicating TP-UD contains a header) */
+ if (sms->ud_hdr_ind)
+ *smsp |= 0x40;
+#if 0
+ /* TP-RP (indicating that a reply path exists) */
+ if (sms->
+ *smsp |= 0x80;
+#endif
+
+ /* generate originator address */
+ oa_len = gsm340_gen_oa(oa, sizeof(oa), sms->sender);
+ smsp = msgb_put(msg, oa_len);
+ memcpy(smsp, oa, oa_len);
+
+ /* generate TP-PID */
+ smsp = msgb_put(msg, 1);
+ *smsp = sms->protocol_id;
+
+ /* generate TP-DCS */
+ smsp = msgb_put(msg, 1);
+ *smsp = sms->data_coding_scheme;
+
+ /* generate TP-SCTS */
+ smsp = msgb_put(msg, 7);
+ gsm340_gen_scts(smsp, time(NULL));
+
+ /* generate TP-UDL */
+ smsp = msgb_put(msg, 1);
+ *smsp = sms->user_data_len;
+
+ /* generate TP-UD */
+ smsp = msgb_put(msg, sms->user_data_len);
+ memcpy(smsp, sms->user_data, sms->user_data_len);
+
+ return msg->len - old_msg_len;
+}
+
+/* process an incoming TPDU (called from RP-DATA)
+ * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */
static int gsm340_rx_tpdu(struct msgb *msg)
{
struct gsm_bts *bts = msg->lchan->ts->trx->bts;
u_int8_t *smsp = msgb_sms(msg);
- struct sms_submit *sms;
struct gsm_sms *gsms;
+ u_int8_t sms_mti, sms_mms, sms_vpf, sms_alphabet, sms_rp;
+ u_int8_t *sms_vp;
u_int8_t da_len_bytes;
u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
int rc = 0;
- sms = talloc(tall_sms_ctx, struct sms_submit);
- if (!sms)
- return -ENOMEM;
- memset(sms, 0, sizeof(*sms));
-
- gsms = talloc(tall_gsms_ctx, struct gsm_sms);
- if (!gsms) {
- talloc_free(sms);
- return -ENOMEM;
- }
- memset(gsms, 0, sizeof(*gsms));
+ gsms = sms_alloc();
+ if (!gsms)
+ return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
/* invert those fields where 0 means active/present */
- sms->mti = *smsp & 0x03;
- sms->mms = !!(*smsp & 0x04);
- sms->vpf = (*smsp & 0x18) >> 3;
- sms->sri = !!(*smsp & 0x20);
- sms->udhi= !!(*smsp & 0x40);
- sms->rp = !!(*smsp & 0x80);
+ sms_mti = *smsp & 0x03;
+ sms_mms = !!(*smsp & 0x04);
+ sms_vpf = (*smsp & 0x18) >> 3;
+ gsms->status_rep_req = (*smsp & 0x20);
+ gsms->ud_hdr_ind = (*smsp & 0x40);
+ sms_rp = (*smsp & 0x80);
smsp++;
- sms->msg_ref = *smsp++;
+ gsms->msg_ref = *smsp++;
/* length in bytes of the destination address */
da_len_bytes = 2 + *smsp/2 + *smsp%2;
if (da_len_bytes > 12) {
DEBUGP(DSMS, "Destination Address > 12 bytes ?!?\n");
- rc = -EIO;
+ rc = GSM411_RP_CAUSE_SEMANT_INC_MSG;
goto out;
}
memset(address_lv, 0, sizeof(address_lv));
@@ -197,145 +345,117 @@ static int gsm340_rx_tpdu(struct msgb *msg)
/* mangle first byte to reflect length in bytes, not digits */
address_lv[0] = da_len_bytes - 1;
/* convert to real number */
- decode_bcd_number(sms->dest_addr, sizeof(sms->dest_addr), address_lv, 1);
-
+ decode_bcd_number(gsms->dest_addr, sizeof(gsms->dest_addr), address_lv, 1);
smsp += da_len_bytes;
- sms->pid = *smsp++;
+ gsms->protocol_id = *smsp++;
+ gsms->data_coding_scheme = *smsp++;
- sms->dcs = *smsp++;
- sms->alphabet = gsm338_get_sms_alphabet(sms->dcs);
+ sms_alphabet = gsm338_get_sms_alphabet(gsms->data_coding_scheme);
- switch (sms->vpf) {
+ switch (sms_vpf) {
case GSM340_TP_VPF_RELATIVE:
- sms->vp = smsp++;
+ sms_vp = smsp++;
break;
case GSM340_TP_VPF_ABSOLUTE:
case GSM340_TP_VPF_ENHANCED:
- sms->vp = smsp;
+ sms_vp = smsp;
smsp += 7;
break;
default:
DEBUGP(DSMS, "SMS Validity period not implemented: 0x%02x\n",
- sms->vpf);
+ sms_vpf);
+ return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
}
- sms->ud_len = *smsp++;
- if (sms->ud_len)
- sms->user_data = smsp;
- else
- sms->user_data = NULL;
+ gsms->user_data_len = *smsp++;
+ if (gsms->user_data_len) {
+ memcpy(gsms->user_data, smsp, gsms->user_data_len);
- if (sms->ud_len) {
- switch (sms->alphabet) {
+ switch (sms_alphabet) {
case DCS_7BIT_DEFAULT:
- gsm_7bit_decode(sms->decoded, smsp, sms->ud_len);
+ gsm_7bit_decode(gsms->text, smsp, gsms->user_data_len);
break;
case DCS_8BIT_DATA:
case DCS_UCS2:
case DCS_NONE:
- memcpy(sms->decoded, sms->user_data, sms->ud_len);
break;
}
}
DEBUGP(DSMS, "SMS:\nMTI: 0x%02x, VPF: 0x%02x, MR: 0x%02x "
"PID: 0x%02x, DCS: 0x%02x, DA: %s, UserDataLength: 0x%02x "
- "UserData: \"%s\"\n", sms->mti, sms->vpf, sms->msg_ref,
- sms->pid, sms->dcs, sms->dest_addr, sms->ud_len,
- sms->alphabet == DCS_7BIT_DEFAULT ? sms->decoded : hexdump(sms->user_data, sms->ud_len));
+ "UserData: \"%s\"\n", sms_mti, sms_vpf, gsms->msg_ref,
+ gsms->protocol_id, gsms->data_coding_scheme,
+ gsms->dest_addr, gsms->user_data_len,
+ sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text :
+ hexdump(gsms->user_data, gsms->user_data_len));
+
+ gsms->sender = subscr_get(msg->lchan->subscr);
- dispatch_signal(SS_SMS, 0, sms);
+ gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp);
- gsms->sender = msg->lchan->subscr;
- /* FIXME: sender refcount */
+ dispatch_signal(SS_SMS, 0, gsms);
/* determine gsms->receiver based on dialled number */
- gsms->receiver = subscr_get_by_extension(bts->network, sms->dest_addr);
+ gsms->receiver = subscr_get_by_extension(bts->network, gsms->dest_addr);
if (!gsms->receiver) {
rc = 1; /* cause 1: unknown subscriber */
goto out;
}
- if (sms->user_data)
- strncpy(gsms->text, sms->decoded, sizeof(gsms->text));
-
- switch (sms->mti) {
+ switch (sms_mti) {
case GSM340_SMS_SUBMIT_MS2SC:
/* MS is submitting a SMS */
- rc = gsm340_rx_sms_submit(msg, sms, gsms);
+ rc = gsm340_rx_sms_submit(msg, gsms);
break;
case GSM340_SMS_COMMAND_MS2SC:
case GSM340_SMS_DELIVER_REP_MS2SC:
- DEBUGP(DSMS, "Unimplemented MTI 0x%02x\n", sms->mti);
+ DEBUGP(DSMS, "Unimplemented MTI 0x%02x\n", sms_mti);
+ rc = GSM411_RP_CAUSE_IE_NOTEXIST;
break;
default:
- DEBUGP(DSMS, "Undefined MTI 0x%02x\n", sms->mti);
+ DEBUGP(DSMS, "Undefined MTI 0x%02x\n", sms_mti);
+ rc = GSM411_RP_CAUSE_IE_NOTEXIST;
break;
}
+ if (!rc && !gsms->receiver)
+ rc = GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
+
out:
- talloc_free(gsms);
- talloc_free(sms);
+ sms_free(gsms);
return rc;
}
-static int gsm411_send_rp_ack(struct gsm_lchan *lchan, u_int8_t trans_id,
- u_int8_t msg_ref)
+static int gsm411_send_rp_ack(struct gsm_trans *trans, u_int8_t msg_ref)
{
struct msgb *msg = gsm411_msgb_alloc();
- struct gsm48_hdr *gh;
- struct gsm411_rp_hdr *rp;
-
- msg->lchan = lchan;
-
- gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- // Outgoing needs the highest bit set
- gh->proto_discr = GSM48_PDISC_SMS | trans_id<<4 | 0x80;
- gh->msg_type = GSM411_MT_CP_DATA;
-
- rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp));
- rp->len = 2;
- rp->msg_type = GSM411_MT_RP_ACK_MT;
- rp->msg_ref = msg_ref;
DEBUGP(DSMS, "TX: SMS RP ACK\n");
- return gsm0411_sendmsg(msg);
+ return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ACK_MT, msg_ref);
}
-static int gsm411_send_rp_error(struct gsm_lchan *lchan, u_int8_t trans_id,
- u_int8_t msg_ref, u_int8_t cause)
+static int gsm411_send_rp_error(struct gsm_trans *trans,
+ u_int8_t msg_ref, u_int8_t cause)
{
struct msgb *msg = gsm411_msgb_alloc();
- struct gsm48_hdr *gh;
- struct gsm411_rp_hdr *rp;
-
- msg->lchan = lchan;
- gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- // Outgoing needs the highest bit set
- gh->proto_discr = GSM48_PDISC_SMS | trans_id<<4 | 0x80;
- gh->msg_type = GSM411_MT_CP_DATA;
-
- rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp));
- rp->msg_type = GSM411_MT_RP_ERROR_MT;
- rp->msg_ref = msg_ref;
msgb_tv_put(msg, 1, cause);
DEBUGP(DSMS, "TX: SMS RP ERROR (cause %02d)\n", cause);
- return gsm0411_sendmsg(msg);
+ return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ERROR_MT, msg_ref);
}
/* Receive a 04.11 TPDU inside RP-DATA / user data */
-static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm411_rp_hdr *rph,
+static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans,
+ struct gsm411_rp_hdr *rph,
u_int8_t src_len, u_int8_t *src,
u_int8_t dst_len, u_int8_t *dst,
u_int8_t tpdu_len, u_int8_t *tpdu)
{
- struct gsm48_hdr *gh = msgb_l3(msg);
- u_int8_t trans_id = gh->proto_discr >> 4;
int rc = 0;
if (src_len && src)
@@ -343,24 +463,26 @@ static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm411_rp_hdr *rph,
if (!dst_len || !dst || !tpdu_len || !tpdu) {
DEBUGP(DSMS, "RP-DATA (MO) without DST or TPDU ?!?\n");
+ gsm411_send_rp_error(trans, rph->msg_ref,
+ GSM411_RP_CAUSE_INV_MAND_INF);
return -EIO;
}
msg->smsh = tpdu;
DEBUGP(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len));
- //return gsm411_send_rp_error(msg->lchan, trans_id, rph->msg_ref, rc);
rc = gsm340_rx_tpdu(msg);
if (rc == 0)
- return gsm411_send_rp_ack(msg->lchan, trans_id, rph->msg_ref);
+ return gsm411_send_rp_ack(trans, rph->msg_ref);
else if (rc > 0)
- return gsm411_send_rp_error(msg->lchan, trans_id, rph->msg_ref, rc);
+ return gsm411_send_rp_error(trans, rph->msg_ref, rc);
else
return rc;
}
/* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */
-static int gsm411_rx_rp_data(struct msgb *msg, struct gsm411_rp_hdr *rph)
+static int gsm411_rx_rp_data(struct msgb *msg, struct gsm_trans *trans,
+ struct gsm411_rp_hdr *rph)
{
u_int8_t src_len, dst_len, rpud_len;
u_int8_t *src = NULL, *dst = NULL , *rp_ud = NULL;
@@ -378,12 +500,120 @@ static int gsm411_rx_rp_data(struct msgb *msg, struct gsm411_rp_hdr *rph)
if (rpud_len)
rp_ud = &rph->data[1+src_len+1+dst_len+1];
- DEBUGP(DSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n", src_len, dst_len, rpud_len);
- return gsm411_rx_rp_ud(msg, rph, src_len, src, dst_len, dst,
+ DEBUGP(DSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n",
+ src_len, dst_len, rpud_len);
+ return gsm411_rx_rp_ud(msg, trans, rph, src_len, src, dst_len, dst,
rpud_len, rp_ud);
}
-static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh)
+/* Receive a 04.11 RP-ACK message (response to RP-DATA from us) */
+static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
+ struct gsm411_rp_hdr *rph)
+{
+ struct gsm_sms *sms = trans->sms.sms;
+
+ /* Acnkowledgement to MT RP_DATA, i.e. the MS confirms it
+ * successfully received a SMS. We can now safely mark it as
+ * transmitted */
+
+ if (!trans->sms.is_mt) {
+ DEBUGP(DSMS, "RX RP-ACK on a MO transfer ?\n");
+ return gsm411_send_rp_error(trans, rph->msg_ref,
+ GSM411_RP_CAUSE_MSG_INCOMP_STATE);
+ }
+
+ if (!sms) {
+ DEBUGP(DSMS, "RX RP-ACK but no sms in transaction?!?\n");
+ return gsm411_send_rp_error(trans, rph->msg_ref,
+ GSM411_RP_CAUSE_PROTOCOL_ERR);
+ }
+
+ /* mark this SMS as sent in database */
+ db_sms_mark_sent(sms);
+
+ dispatch_signal(SS_SMS, S_SMS_DELIVERED, sms);
+
+ sms_free(sms);
+ trans->sms.sms = NULL;
+
+ /* do not free the transaction here, this is done by sending CP-ACK */
+
+ /* check for more messages for this subscriber */
+ sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr);
+ if (sms)
+ gsm411_send_sms_lchan(msg->lchan, sms);
+ else
+ rsl_release_request(msg->lchan, UM_SAPI_SMS);
+
+ return 0;
+}
+
+static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
+ struct gsm411_rp_hdr *rph)
+{
+ struct gsm_sms *sms = trans->sms.sms;
+ u_int8_t cause_len = rph->data[0];
+ u_int8_t cause = rph->data[1];
+
+ /* Error in response to MT RP_DATA, i.e. the MS did not
+ * successfully receive the SMS. We need to investigate
+ * the cause and take action depending on it */
+
+ DEBUGP(DSMS, "RX SMS RP-ERROR Cause=0x%02x\n", cause);
+
+ if (!trans->sms.is_mt) {
+ DEBUGP(DSMS, "RX RP-ERR on a MO transfer ?\n");
+ return gsm411_send_rp_error(trans, rph->msg_ref,
+ GSM411_RP_CAUSE_MSG_INCOMP_STATE);
+ }
+
+ if (!sms) {
+ DEBUGP(DSMS, "RX RP-ERR, but no sms in transaction?!?\n");
+ return gsm411_send_rp_error(trans, rph->msg_ref,
+ GSM411_RP_CAUSE_PROTOCOL_ERR);
+ }
+
+ if (cause == GSM411_RP_CAUSE_MT_MEM_EXCEEDED) {
+ /* MS has not enough memory to store the message. We need
+ * to store this in our database and wati for a SMMA message */
+ /* FIXME */
+ dispatch_signal(SS_SMS, S_SMS_MEM_EXCEEDED, trans->subscr);
+ }
+
+ sms_free(sms);
+ trans->sms.sms = NULL;
+
+ trans_free(trans);
+
+ return 0;
+}
+
+static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans,
+ struct gsm411_rp_hdr *rph)
+{
+ struct gsm_sms *sms;
+ int rc;
+
+ rc = gsm411_send_rp_ack(trans, rph->msg_ref);
+ trans->sms.rp_state = GSM411_RPS_IDLE;
+
+ /* MS tells us that it has memory for more SMS, we need
+ * to check if we have any pending messages for it and then
+ * transfer those */
+ dispatch_signal(SS_SMS, S_SMS_SMMA, trans->subscr);
+
+ /* check for more messages for this subscriber */
+ sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr);
+ if (sms)
+ gsm411_send_sms_lchan(msg->lchan, sms);
+ else
+ rsl_release_request(msg->lchan, UM_SAPI_SMS);
+
+ return rc;
+}
+
+static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh,
+ struct gsm_trans *trans)
{
struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data;
u_int8_t msg_type = rp_data->msg_type & 0x07;
@@ -391,65 +621,142 @@ static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh)
switch (msg_type) {
case GSM411_MT_RP_DATA_MO:
- DEBUGP(DSMS, "SMS RP-DATA (MO)\n");
- rc = gsm411_rx_rp_data(msg, rp_data);
+ DEBUGP(DSMS, "RX SMS RP-DATA (MO)\n");
+ /* start TR2N and enter 'wait to send RP-ACK state' */
+ trans->sms.rp_state = GSM411_RPS_WAIT_TO_TX_RP_ACK;
+ rc = gsm411_rx_rp_data(msg, trans, rp_data);
break;
case GSM411_MT_RP_ACK_MO:
- /* Acnkowledgement to MT RP_DATA */
- case GSM411_MT_RP_ERROR_MO:
- /* Error in response to MT RP_DATA */
+ DEBUGP(DSMS,"RX SMS RP-ACK (MO)\n");
+ rc = gsm411_rx_rp_ack(msg, trans, rp_data);
+ break;
case GSM411_MT_RP_SMMA_MO:
- /* MS tells us that it has memory for more SMS, we need
- * to check if we have any pending messages for it and then
- * transfer those */
- DEBUGP(DSMS, "Unimplemented RP type 0x%02x\n", msg_type);
+ DEBUGP(DSMS, "RX SMS RP-SMMA\n");
+ /* start TR2N and enter 'wait to send RP-ACK state' */
+ trans->sms.rp_state = GSM411_RPS_WAIT_TO_TX_RP_ACK;
+ rc = gsm411_rx_rp_smma(msg, trans, rp_data);
+ break;
+ case GSM411_MT_RP_ERROR_MO:
+ rc = gsm411_rx_rp_error(msg, trans, rp_data);
break;
default:
DEBUGP(DSMS, "Invalid RP type 0x%02x\n", msg_type);
+ rc = gsm411_send_rp_error(trans, rp_data->msg_ref,
+ GSM411_RP_CAUSE_MSGTYPE_NOTEXIST);
break;
}
return rc;
}
+/* send CP-ACK to given transaction */
+static int gsm411_tx_cp_ack(struct gsm_trans *trans)
+{
+ struct msgb *msg = gsm411_msgb_alloc();
+ int rc;
+
+ rc = gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ACK);
+
+ if (trans->sms.is_mt) {
+ /* If this is a MT SMS DELIVER, we can clear transaction here */
+ trans->sms.cp_state = GSM411_CPS_IDLE;
+ trans_free(trans);
+ }
+}
+
+static int gsm411_tx_cp_error(struct gsm_trans *trans, u_int8_t cause)
+{
+ struct msgb *msg = gsm411_msgb_alloc();
+ u_int8_t *causep;
+
+ causep = msgb_put(msg, 1);
+ *causep = cause;
+
+ return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ERROR);
+}
+
+/* Entry point for incoming GSM48_PDISC_SMS from abis_rsl.c */
int gsm0411_rcv_sms(struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
u_int8_t msg_type = gh->msg_type;
+ u_int8_t transaction_id = ((gh->proto_discr >> 4) ^ 0x8); /* flip */
+ struct gsm_lchan *lchan = msg->lchan;
+ struct gsm_trans *trans;
int rc = 0;
+ if (!lchan->subscr)
+ return -EIO;
+ /* FIXME: send some error message */
+
+ trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_SMS,
+ transaction_id);
+ if (!trans) {
+ DEBUGP(DSMS, "Unknown transaction ID %x, "
+ "creating new trans\n", transaction_id);
+ trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
+ transaction_id, new_callref++);
+ if (!trans) {
+ DEBUGP(DSMS, "No memory for trans\n");
+ /* FIXME: send some error message */
+ return -ENOMEM;
+ }
+ trans->sms.cp_state = GSM411_CPS_IDLE;
+ trans->sms.rp_state = GSM411_RPS_IDLE;
+ trans->sms.is_mt = 0;
+
+ trans->lchan = lchan;
+ use_lchan(lchan);
+ }
+
switch(msg_type) {
case GSM411_MT_CP_DATA:
- DEBUGP(DSMS, "SMS CP-DATA\n");
- rc = gsm411_rx_cp_data(msg, gh);
+ DEBUGP(DSMS, "RX SMS CP-DATA\n");
+ /* 5.2.3.1.3: MO state exists when SMC has received
+ * CP-DATA, including sending of the assoc. CP-ACK */
+ /* 5.2.3.2.4: MT state exists when SMC has received
+ * CP-DATA, including sending of the assoc. CP-ACK */
+ trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED;
+
+ rc = gsm411_rx_cp_data(msg, gh, trans);
+ /* Send CP-ACK or CP-ERORR in response */
+ if (rc < 0) {
+ rc = gsm411_tx_cp_error(trans, GSM411_CP_CAUSE_NET_FAIL);
+ } else
+ rc = gsm411_tx_cp_ack(trans);
break;
case GSM411_MT_CP_ACK:
- DEBUGP(DSMS, "SMS CP-ACK\n");
+ /* previous CP-DATA in this transaction was confirmed */
+ DEBUGP(DSMS, "RX SMS CP-ACK\n");
+ /* 5.2.3.1.3: MO state exists when SMC has received CP-ACK */
+ /* 5.2.3.2.4: MT state exists when SMC has received CP-ACK */
+ trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED;
+
+ if (!trans->sms.is_mt) {
+ /* FIXME: we have sont one CP-DATA, which was now
+ * acknowledged. Check if we want to transfer more,
+ * i.e. multi-part message */
+ trans->sms.cp_state = GSM411_CPS_IDLE;
+ trans_free(trans);
+ }
break;
case GSM411_MT_CP_ERROR:
- DEBUGP(DSMS, "SMS CP-ERROR, cause 0x%02x\n", gh->data[0]);
+ DEBUGP(DSMS, "RX SMS CP-ERROR, cause 0x%02x\n", gh->data[0]);
+ trans->sms.cp_state = GSM411_CPS_IDLE;
+ trans_free(trans);
break;
default:
- DEBUGP(DSMS, "Unimplemented CP msg_type: 0x%02x\n", msg_type);
+ DEBUGP(DSMS, "RX Unimplemented CP msg_type: 0x%02x\n", msg_type);
+ rc = gsm411_tx_cp_error(trans, GSM411_CP_CAUSE_MSGTYPE_NOTEXIST);
+ trans->sms.cp_state = GSM411_CPS_IDLE;
+ trans_free(trans);
break;
}
-
return rc;
}
-/* Test TPDU - 25c3 welcome */
#if 0
-static u_int8_t tpdu_test[] = {
- 0x04, 0x04, 0x81, 0x32, 0x24, 0x00, 0x00, 0x80, 0x21, 0x92, 0x90, 0x32,
- 0x24, 0x40, 0x4D, 0xB2, 0xDA, 0x70, 0xD6, 0x9A, 0x97, 0xE5, 0xF6, 0xF4,
- 0xB8, 0x0C, 0x0A, 0xBB, 0xDD, 0xEF, 0xBA, 0x7B, 0x5C, 0x6E, 0x97, 0xDD,
- 0x74, 0x1D, 0x08, 0xCA, 0x2E, 0x87, 0xE7, 0x65, 0x50, 0x98, 0x4E, 0x2F,
- 0xBB, 0xC9, 0x20, 0x3A, 0xBA, 0x0C, 0x3A, 0x4E, 0x9B, 0x20, 0x7A, 0x98,
- 0xBD, 0x06, 0x85, 0xE9, 0xA0, 0x58, 0x4C, 0x37, 0x83, 0x81, 0xD2, 0x6E,
- 0xD0, 0x34, 0x1C, 0x66, 0x83, 0x62, 0x21, 0x90, 0xAE, 0x95, 0x02
-};
-#else
/* Test TPDU - ALL YOUR */
static u_int8_t tpdu_test[] = {
0x04, 0x04, 0x81, 0x32, 0x24, 0x00, 0x00, 0x80, 0x21, 0x03, 0x41, 0x24,
@@ -459,55 +766,191 @@ static u_int8_t tpdu_test[] = {
};
#endif
-int gsm0411_send_sms(struct gsm_lchan *lchan, struct sms_deliver *sms)
+/* Take a SMS in gsm_sms structure and send it through an already
+ * existing lchan. We also assume that the caller ensured this lchan already
+ * has a SAPI3 RLL connection! */
+int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
{
struct msgb *msg = gsm411_msgb_alloc();
- struct gsm48_hdr *gh;
- struct gsm411_rp_hdr *rp;
- u_int8_t *data;
+ struct gsm_trans *trans;
+ u_int8_t *data, *rp_ud_len;
+ u_int8_t msg_ref = 42;
+ u_int8_t transaction_id;
+ int rc;
+
+ transaction_id = 4; /* FIXME: we always use 4 for now */
msg->lchan = lchan;
- gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- gh->proto_discr = GSM48_PDISC_SMS;
- gh->msg_type = GSM411_MT_CP_DATA;
+ DEBUGP(DSMS, "send_sms_lchan()\n");
- rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp));
- rp->len = sizeof(tpdu_test) + 10;
- rp->msg_type = GSM411_MT_RP_DATA_MT;
- rp->msg_ref = 42; /* FIXME: Choose randomly */
- /* Hardcode OA for now */
+ /* FIXME: allocate transaction with message reference */
+ trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
+ transaction_id, new_callref++);
+ if (!trans) {
+ DEBUGP(DSMS, "No memory for trans\n");
+ /* FIXME: send some error message */
+ return -ENOMEM;
+ }
+ trans->sms.cp_state = GSM411_CPS_IDLE;
+ trans->sms.rp_state = GSM411_RPS_IDLE;
+ trans->sms.is_mt = 1;
+ trans->sms.sms = sms;
+
+ trans->lchan = lchan;
+ use_lchan(lchan);
+
+ /* Hardcode SMSC Originating Address for now */
data = (u_int8_t *)msgb_put(msg, 8);
- data[0] = 0x07;
- data[1] = 0x91;
- data[2] = 0x44;
+ data[0] = 0x07; /* originator length == 7 */
+ data[1] = 0x91; /* type of number: international, ISDN */
+ data[2] = 0x44; /* 447785016005 */
data[3] = 0x77;
data[4] = 0x58;
data[5] = 0x10;
data[6] = 0x06;
data[7] = 0x50;
- data = (u_int8_t *)msgb_put(msg, 1);
- data[0] = 0;
-
- /* FIXME: Hardcoded for now */
- //smslen = gsm0411_tpdu_from_sms(tpdu, sms);
- /* RPDU length */
+ /* Hardcoded Destination Address */
data = (u_int8_t *)msgb_put(msg, 1);
- data[0] = sizeof(tpdu_test);
+ data[0] = 0; /* destination length == 0 */
+
+ /* obtain a pointer for the rp_ud_len, so we can fill it later */
+ rp_ud_len = (u_int8_t *)msgb_put(msg, 1);
- data = (u_int8_t *)msgb_put(msg, sizeof(tpdu_test));
+#if 1
+ /* generate the 03.40 TPDU */
+ rc = gsm340_gen_tpdu(msg, sms);
+ if (rc < 0) {
+ msgb_free(msg);
+ return rc;
+ }
- //memcpy(data, tpdu, smslen);
+ *rp_ud_len = rc;
+#else
+ data = msgb_put(msg, sizeof(tpdu_test));
memcpy(data, tpdu_test, sizeof(tpdu_test));
+ *rp_ud_len = sizeof(tpdu_test);
+#endif
+
+ DEBUGP(DSMS, "TX: SMS DELIVER\n");
+
+ return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref);
+ /* FIXME: enter 'wait for RP-ACK' state, start TR1N */
+}
+
+/* RLL SAPI3 establish callback. Now we have a RLL connection and
+ * can deliver the actual message */
+static void rll_ind_cb(struct gsm_lchan *lchan, u_int8_t link_id,
+ void *_sms, enum bsc_rllr_ind type)
+{
+ struct gsm_sms *sms = _sms;
+
+ DEBUGP(DSMS, "rll_ind_cb(lchan=%p, link_id=%u, sms=%p, type=%u\n",
+ lchan, link_id, sms, type);
+
+ switch (type) {
+ case BSC_RLLR_IND_EST_CONF:
+ gsm411_send_sms_lchan(lchan, sms);
+ break;
+ case BSC_RLLR_IND_REL_IND:
+ case BSC_RLLR_IND_ERR_IND:
+ case BSC_RLLR_IND_TIMEOUT:
+ sms_free(sms);
+ break;
+ }
+}
+
+/* paging callback. Here we get called if paging a subscriber has
+ * succeeded or failed. */
+static int paging_cb_send_sms(unsigned int hooknum, unsigned int event,
+ struct msgb *msg, void *_lchan, void *_sms)
+{
+ struct gsm_lchan *lchan = _lchan;
+ struct gsm_sms *sms = _sms;
+ int rc;
+
+ DEBUGP(DSMS, "paging_cb_send_sms(hooknum=%u, event=%u, msg=%p,"
+ "lchan=%p, sms=%p)\n", hooknum, event, msg, lchan, sms);
+
+ if (hooknum != GSM_HOOK_RR_PAGING)
+ return -EINVAL;
+
+ switch (event) {
+ case GSM_PAGING_SUCCEEDED:
+ /* Paging aborted without lchan ?!? */
+ if (!lchan) {
+ sms_free(sms);
+ rc = -EIO;
+ break;
+ }
+ /* Establish a SAPI3 RLL connection for SMS */
+ rc = rll_establish(lchan, UM_SAPI_SMS, rll_ind_cb, sms);
+ break;
+ case GSM_PAGING_EXPIRED:
+ sms_free(sms);
+ rc = -ETIMEDOUT;
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+/* high-level function to send a SMS to a given subscriber. The function
+ * will take care of paging the subscriber, establishing the RLL SAPI3
+ * connection, etc. */
+int gsm411_send_sms_subscr(struct gsm_subscriber *subscr,
+ struct gsm_sms *sms)
+{
+ struct gsm_lchan *lchan;
+
+ /* check if we already have an open lchan to the subscriber.
+ * if yes, send the SMS this way */
+ lchan = lchan_for_subscr(subscr);
+ if (lchan)
+ return rll_establish(lchan, UM_SAPI_SMS,
+ rll_ind_cb, sms);
- DEBUGP(DSMS, "TX: SMS SUBMIT\n");
+ /* if not, we have to start paging */
+ paging_request(subscr->net, subscr, RSL_CHANNEED_SDCCH,
+ paging_cb_send_sms, sms);
- return gsm0411_sendmsg(msg);
+ return 0;
+}
+
+static int subscr_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct gsm_subscriber *subscr;
+ struct gsm_lchan *lchan;
+ struct gsm_sms *sms;
+
+ switch (signal) {
+ case S_SUBSCR_ATTACHED:
+ /* A subscriber has attached. Check if there are
+ * any pending SMS for him to be delivered */
+ subscr = signal_data;
+ lchan = lchan_for_subscr(subscr);
+ if (!lchan)
+ break;
+ sms = db_sms_get_unsent_for_subscr(subscr);
+ if (!sms)
+ break;
+ /* Establish a SAPI3 RLL connection for SMS */
+ rll_establish(lchan, UM_SAPI_SMS, rll_ind_cb, sms);
+ break;
+ default:
+ break;
+ }
+ return 0;
}
static __attribute__((constructor)) void on_dso_load_sms(void)
{
- tall_sms_ctx = talloc_named_const(tall_bsc_ctx, 1, "sms_submit");
tall_gsms_ctx = talloc_named_const(tall_bsc_ctx, 1, "sms");
+
+ register_signal_handler(SS_SUBSCR, subscr_sig_cb, NULL);
}
diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
index 0ed08454a..370c41097 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -234,7 +234,7 @@ static const char *bts_types[] = {
[GSM_BTS_TYPE_NANOBTS_1800] = "nanobts1800",
};
-enum gsm_bts_type parse_btstype(char *arg)
+enum gsm_bts_type parse_btstype(const char *arg)
{
int i;
for (i = 0; i < ARRAY_SIZE(bts_types); i++) {
diff --git a/openbsc/src/gsm_subscriber.c b/openbsc/src/gsm_subscriber.c
index 748015693..d91298ee6 100644
--- a/openbsc/src/gsm_subscriber.c
+++ b/openbsc/src/gsm_subscriber.c
@@ -32,6 +32,7 @@
#include <openbsc/paging.h>
#include <openbsc/debug.h>
#include <openbsc/paging.h>
+#include <openbsc/signal.h>
#include <openbsc/db.h>
LLIST_HEAD(active_subscribers);
@@ -162,6 +163,22 @@ struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net,
return db_get_subscriber(net, GSM_SUBSCRIBER_EXTENSION, ext);
}
+struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net,
+ unsigned long long id)
+{
+ struct gsm_subscriber *subscr;
+ char buf[32];
+ sprintf(buf, "%llu", id);
+
+ llist_for_each_entry(subscr, &active_subscribers, entry) {
+ if (subscr->id == id)
+ return subscr_get(subscr);
+ }
+
+ return db_get_subscriber(net, GSM_SUBSCRIBER_ID, buf);
+}
+
+
int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason)
{
/* FIXME: Migrate pending requests from one BSC to another */
@@ -170,12 +187,13 @@ int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason)
s->net = bts->network;
/* Indicate "attached to LAC" */
s->lac = bts->location_area_code;
+ dispatch_signal(SS_SUBSCR, S_SUBSCR_ATTACHED, s);
break;
case GSM_SUBSCRIBER_UPDATE_DETACHED:
/* Only detach if we are currently in this area */
if (bts->location_area_code == s->lac)
s->lac = 0;
-
+ dispatch_signal(SS_SUBSCR, S_SUBSCR_DETACHED, s);
break;
default:
fprintf(stderr, "subscr_update with unknown reason: %d\n",
diff --git a/openbsc/src/gsm_utils.c b/openbsc/src/gsm_utils.c
index 0c25b28c6..185918ef9 100644
--- a/openbsc/src/gsm_utils.c
+++ b/openbsc/src/gsm_utils.c
@@ -21,9 +21,11 @@
*
*/
+#include <openbsc/gsm_data.h>
#include <openbsc/gsm_utils.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
/* GSM 03.38 6.2.1 Charachter packing */
int gsm_7bit_decode(char *text, const u_int8_t *user_data, u_int8_t length)
@@ -71,3 +73,77 @@ int gsm_7bit_encode(u_int8_t *result, const char *data)
return out_length;
}
+
+/* determine power control level for given dBm value, as indicated
+ * by the tables in chapter 4.1.1 of GSM TS 05.05 */
+int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm)
+{
+ switch (band) {
+ case GSM_BAND_400:
+ case GSM_BAND_900:
+ case GSM_BAND_850:
+ if (dbm >= 39)
+ return 0;
+ else if (dbm < 5)
+ return 19;
+ else
+ return 2 + ((39 - dbm) / 2);
+ break;
+ case GSM_BAND_1800:
+ if (dbm >= 36)
+ return 29;
+ else if (dbm >= 34)
+ return 30;
+ else if (dbm >= 32)
+ return 31;
+ else
+ return (30 - dbm) / 2;
+ break;
+ case GSM_BAND_1900:
+ if (dbm >= 33)
+ return 30;
+ else if (dbm >= 32)
+ return 31;
+ else
+ return (30 - dbm) / 2;
+ break;
+ }
+ return -EINVAL;
+}
+
+int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl)
+{
+ lvl &= 0x1f;
+
+ switch (band) {
+ case GSM_BAND_400:
+ case GSM_BAND_900:
+ case GSM_BAND_850:
+ if (lvl < 2)
+ return 39;
+ else if (lvl < 20)
+ return 39 - ((lvl - 2) * 2) ;
+ else
+ return 5;
+ break;
+ case GSM_BAND_1800:
+ if (lvl < 16)
+ return 30 - (lvl * 2);
+ else if (lvl < 29)
+ return 0;
+ else
+ return 36 - ((lvl - 29) * 2);
+ break;
+ case GSM_BAND_1900:
+ if (lvl < 16)
+ return 30 - (lvl * 2);
+ else if (lvl < 30)
+ return -EINVAL;
+ else
+ return 33 - (lvl - 30);
+ break;
+ }
+ return -EINVAL;
+}
+
+
diff --git a/openbsc/src/ipaccess-config.c b/openbsc/src/ipaccess-config.c
index 5ccbaa70c..a7fb9c43b 100644
--- a/openbsc/src/ipaccess-config.c
+++ b/openbsc/src/ipaccess-config.c
@@ -38,9 +38,11 @@
#include <openbsc/e1_input.h>
#include <openbsc/abis_nm.h>
#include <openbsc/signal.h>
+#include <openbsc/debug.h>
static struct gsm_network *gsmnet;
+static int net_listen;
static int restart;
static char *prim_oml_ip;
static char *unit_id;
@@ -66,12 +68,61 @@ static int ipacc_msg_nack(int mt)
return 0;
}
+struct ipacc_ferr_elem {
+ int16_t freq_err;
+ u_int8_t freq_qual;
+ u_int8_t arfcn;
+} __attribute__((packed));
+
+static int test_rep(void *_msg)
+{
+ struct msgb *msg = _msg;
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ u_int16_t test_rep_len, ferr_list_len;
+ struct ipacc_ferr_elem *ife;
+ int i;
+
+ DEBUGP(DNM, "TEST REPORT: ");
+
+ if (foh->data[0] != NM_ATT_TEST_NO ||
+ foh->data[2] != NM_ATT_TEST_REPORT)
+ return -EINVAL;
+
+ DEBUGPC(DNM, "test_no=0x%02x ", foh->data[1]);
+ /* data[2] == NM_ATT_TEST_REPORT */
+ /* data[3..4]: test_rep_len */
+ test_rep_len = ntohs(*(u_int16_t *) &foh->data[3]);
+ /* data[5]: ip.access test result */
+ DEBUGPC(DNM, "test_res=%u\n", foh->data[5]);
+
+ /* data[6]: ip.access nested IE. 3 == freq_err_list */
+ switch (foh->data[6]) {
+ case 3:
+ /* data[7..8]: length of ferr_list */
+ ferr_list_len = ntohs(*(u_int16_t *) &foh->data[7]);
+
+ /* data[9...]: frequency error list elements */
+ for (i = 0; i < ferr_list_len; i+= sizeof(*ife)) {
+ ife = (struct ipacc_ferr_elem *) (foh->data + 9 + i);
+ DEBUGP(DNM, "==> ARFCN %4u, Frequency Error %6hd\n",
+ ife->arfcn, ntohs(ife->freq_err));
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int nm_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
switch (signal) {
case S_NM_IPACC_NACK:
return ipacc_msg_nack((int)signal_data);
+ case S_NM_TEST_REP:
+ return test_rep(signal_data);
default:
break;
}
@@ -141,6 +192,9 @@ static void bootstrap_om(struct gsm_bts *bts)
printf("restarting BTS\n");
abis_nm_ipaccess_restart(bts);
}
+
+ if (net_listen) {
+ }
}
void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
@@ -170,6 +224,16 @@ 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)
{
+ if (evt == EVT_STATECHG_OPER &&
+ obj_class == NM_OC_RADIO_CARRIER &&
+ new_state->availability == 3 &&
+ net_listen) {
+ struct gsm_bts_trx *trx = obj;
+ u_int8_t phys_config[] = { 0x02, 0x0a, 0x00, 0x01, 0x02 };
+ abis_nm_perform_test(trx->bts, 2, 0, 0, 0xff,
+ NM_IPACC_TESTNO_FREQ_SYNC, 1,
+ phys_config, sizeof(phys_config));
+ }
return 0;
}
@@ -183,7 +247,8 @@ static void print_help(void)
printf(" -u --unit-id UNIT_ID\n");
printf(" -o --oml-ip ip\n");
printf(" -r --restart\n");
- printf(" -n flags/mask Set NVRAM attributes.\n");
+ printf(" -n flags/mask\tSet NVRAM attributes.\n");
+ printf(" -l --listen\tPerform Frequency Error test\n");
printf(" -h --help this text\n");
}
@@ -205,9 +270,10 @@ int main(int argc, char **argv)
{ "oml-ip", 1, 0, 'o' },
{ "restart", 0, 0, 'r' },
{ "help", 0, 0, 'h' },
+ { "listen", 0, 0, 'l' },
};
- c = getopt_long(argc, argv, "u:o:rn:h", long_options,
+ c = getopt_long(argc, argv, "u:o:rn:lh", long_options,
&option_index);
if (c == -1)
@@ -232,6 +298,9 @@ int main(int argc, char **argv)
ul = strtoul(slash+1, NULL, 16);
nv_mask = ul & 0xffff;
break;
+ case 'l':
+ net_listen = 1;
+ break;
case 'h':
print_usage();
print_help();
diff --git a/openbsc/src/ipaccess-find.c b/openbsc/src/ipaccess-find.c
index db78922b9..f469b6788 100644
--- a/openbsc/src/ipaccess-find.c
+++ b/openbsc/src/ipaccess-find.c
@@ -33,7 +33,7 @@ static const char *ipac_idtag_name(int tag)
return idtag_names[tag];
}
-static int udp_sock(char *local_ip)
+static int udp_sock(const char *ifname)
{
int fd, rc, bc = 1;
struct sockaddr_in sa;
@@ -42,10 +42,16 @@ static int udp_sock(char *local_ip)
if (fd < 0)
return fd;
+ if (ifname) {
+ rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
+ strlen(ifname));
+ if (rc < 0)
+ goto err;
+ }
+
sa.sin_family = AF_INET;
sa.sin_port = htons(3006);
sa.sin_addr.s_addr = INADDR_ANY;
- inet_aton(local_ip, &sa.sin_addr);
rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
if (rc < 0)
@@ -56,6 +62,12 @@ static int udp_sock(char *local_ip)
goto err;
#if 0
+ /* we cannot bind, since the response packets don't come from
+ * the broadcast address */
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(3006);
+ inet_aton("255.255.255.255", &sa.sin_addr);
+
rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
if (rc < 0)
goto err;
@@ -121,6 +133,13 @@ static int read_response(int fd)
if (len < 0)
return len;
+ /* 2 bytes length, 1 byte protocol (0xfe) */
+ if (buf[2] != 0xfe)
+ return 0;
+
+ if (buf[4] != IPAC_MSGT_ID_RESP)
+ return 0;
+
return parse_response(buf+6, len-6);
}
@@ -149,21 +168,21 @@ static void timer_cb(void *_data)
int main(int argc, char **argv)
{
struct bsc_fd bfd;
- char *local_ip;
+ char *ifname;
int rc;
printf("ipaccess-find (C) 2009 by Harald Welte\n");
printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
if (argc < 2) {
- fprintf(stderr, "please specify the _local_ IP address as argument\n");
- exit(2);
+ fprintf(stdout, "you might need to specify the outgoing\n"
+ " network interface, e.g. ``%s eth0''\n", argv[0]);
}
- local_ip = argv[1];
+ ifname = argv[1];
bfd.cb = bfd_cb;
bfd.when = BSC_FD_READ | BSC_FD_WRITE;
- bfd.fd = udp_sock(local_ip);
+ bfd.fd = udp_sock(ifname);
if (bfd.fd < 0) {
perror("Cannot create local socket for broadcast udp");
exit(1);
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index b63a717b0..dd2215005 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -224,7 +224,8 @@ static void _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
return;
}
- DEBUGP(DPAG, "Start paging on bts %d.\n", bts->nr);
+ DEBUGP(DPAG, "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);
req->bts = bts;
@@ -245,13 +246,14 @@ void paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
{
struct gsm_bts *bts = NULL;
+ /* start paging subscriber on all BTS within Location Area */
do {
bts = gsm_bts_by_lac(network, subscr->lac, bts);
if (!bts)
break;
/* Trigger paging */
- _paging_request(bts, subscr, RSL_CHANNEED_TCH_F, cbfn, data);
+ _paging_request(bts, subscr, type, cbfn, data);
} while (1);
}
diff --git a/openbsc/src/transaction.c b/openbsc/src/transaction.c
index f4cef28d2..9e7c7948f 100644
--- a/openbsc/src/transaction.c
+++ b/openbsc/src/transaction.c
@@ -31,13 +31,16 @@
static void *tall_trans_ctx;
-struct gsm_trans *trans_find_by_id(struct gsm_lchan *lchan, u_int8_t trans_id)
+struct gsm_trans *trans_find_by_id(struct gsm_subscriber *subscr,
+ u_int8_t proto, u_int8_t trans_id)
{
struct gsm_trans *trans;
- struct gsm_network *net = lchan->ts->trx->bts->network;
+ struct gsm_network *net = subscr->net;
llist_for_each_entry(trans, &net->trans_list, entry) {
- if (trans->lchan == lchan && trans->transaction_id == trans_id)
+ if (trans->subscr == subscr &&
+ trans->protocol == proto &&
+ trans->transaction_id == trans_id)
return trans;
}
return NULL;
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index 7213edfa8..50da4a053 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <vty/command.h>
+#include <vty/buffer.h>
#include <vty/vty.h>
#include <arpa/inet.h>
@@ -32,6 +33,7 @@
#include <openbsc/gsm_subscriber.h>
#include <openbsc/e1_input.h>
#include <openbsc/abis_nm.h>
+#include <openbsc/gsm_utils.h>
#include <openbsc/db.h>
#include <openbsc/talloc.h>
@@ -1034,6 +1036,118 @@ DEFUN(show_subscr,
return CMD_SUCCESS;
}
+DEFUN(sms_send_pend,
+ sms_send_pend_cmd,
+ "sms send pending MIN_ID",
+ "Send all pending SMS starting from MIN_ID")
+{
+ struct gsm_sms *sms;
+
+ sms = db_sms_get_unsent(gsmnet, atoi(argv[0]));
+ if (!sms)
+ return CMD_WARNING;
+
+ if (!sms->receiver) {
+ sms_free(sms);
+ return CMD_WARNING;
+ }
+
+ gsm411_send_sms_subscr(sms->receiver, sms);
+
+ return CMD_SUCCESS;
+}
+
+static struct buffer *argv_to_buffer(int argc, const char *argv[], int base)
+{
+ struct buffer *b = buffer_new(1024);
+ int i;
+
+ if (!b)
+ return NULL;
+
+ for (i = base; i < argc; i++) {
+ buffer_putstr(b, argv[i]);
+ buffer_putc(b, ' ');
+ }
+ buffer_putc(b, '\0');
+
+ return b;
+}
+
+static int _send_sms_buffer(struct gsm_subscriber *receiver,
+ struct buffer *b)
+{
+ struct gsm_sms *sms = sms_alloc();
+
+ if (!sms)
+ return CMD_WARNING;
+
+ if (!receiver->lac) {
+ /* subscriber currently not attached, store in database? */
+ subscr_put(sms->receiver);
+ return CMD_WARNING;
+ }
+
+ sms->receiver = receiver;
+ strncpy(sms->text, buffer_getstr(b), sizeof(sms->text)-1);
+
+ /* FIXME: don't use ID 1 static */
+ sms->sender = subscr_get_by_id(gsmnet, 1);
+ sms->reply_path_req = 0;
+ sms->status_rep_req = 0;
+ sms->ud_hdr_ind = 0;
+ sms->protocol_id = 0; /* implicit */
+ sms->data_coding_scheme = 0; /* default 7bit */
+ strncpy(sms->dest_addr, receiver->extension, sizeof(sms->dest_addr)-1);
+ /* Generate user_data */
+ sms->user_data_len = gsm_7bit_encode(sms->user_data, sms->text);
+
+ gsm411_send_sms_subscr(sms->receiver, sms);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(sms_send_ext,
+ sms_send_ext_cmd,
+ "sms send extension EXTEN .LINE",
+ "Send a message to a subscriber identified by EXTEN")
+{
+ struct gsm_subscriber *receiver;
+ struct buffer *b;
+ int rc;
+
+ receiver = subscr_get_by_extension(gsmnet, argv[0]);
+ if (!receiver)
+ return CMD_WARNING;
+
+ b = argv_to_buffer(argc, argv, 1);
+ rc = _send_sms_buffer(receiver, b);
+ buffer_free(b);
+
+ return rc;
+}
+
+DEFUN(sms_send_imsi,
+ sms_send_imsi_cmd,
+ "sms send imsi IMSI .LINE",
+ "Send a message to a subscriber identified by IMSI")
+{
+ struct gsm_subscriber *receiver;
+ struct buffer *b;
+ int rc;
+
+ receiver = subscr_get_by_imsi(gsmnet, argv[0]);
+ if (!receiver)
+ return CMD_WARNING;
+
+ b = argv_to_buffer(argc, argv, 1);
+ rc = _send_sms_buffer(receiver, b);
+ buffer_free(b);
+
+ return rc;
+}
+
+
DEFUN(cfg_subscr_name,
cfg_subscr_name_cmd,
"name NAME",
@@ -1103,6 +1217,10 @@ int bsc_vty_init(struct gsm_network *net)
install_element(VIEW_NODE, &show_subscr_cmd);
+ install_element(VIEW_NODE, &sms_send_pend_cmd);
+ install_element(VIEW_NODE, &sms_send_ext_cmd);
+ install_element(VIEW_NODE, &sms_send_imsi_cmd);
+
install_element(CONFIG_NODE, &cfg_net_cmd);
install_node(&net_node, config_write_net);
install_default(GSMNET_NODE);