diff options
Diffstat (limited to 'src/shared/libosmocore')
28 files changed, 1571 insertions, 79 deletions
diff --git a/src/shared/libosmocore/configure.ac b/src/shared/libosmocore/configure.ac index 52ab850f..7d8e6dda 100644 --- a/src/shared/libosmocore/configure.ac +++ b/src/shared/libosmocore/configure.ac @@ -19,6 +19,10 @@ AC_CONFIG_MACRO_DIR([m4]) dnl checks for header files AC_HEADER_STDC AC_CHECK_HEADERS(execinfo.h sys/select.h sys/socket.h syslog.h ctype.h) +# for src/conv.c +AC_FUNC_ALLOCA +AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""]) +AC_SUBST(LIBRARY_DL) AC_PATH_PROG(DOXYGEN,doxygen,false) AM_CONDITIONAL(HAVE_DOXYGEN, test $DOXYGEN != false) diff --git a/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h b/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h index ee988918..44e00a16 100644 --- a/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h +++ b/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h @@ -139,10 +139,10 @@ extern void rb_insert_color(struct rb_node *, struct rb_root *); extern void rb_erase(struct rb_node *, struct rb_root *); /* Find logical next and previous nodes in a tree */ -extern struct rb_node *rb_next(struct rb_node *); -extern struct rb_node *rb_prev(struct rb_node *); -extern struct rb_node *rb_first(struct rb_root *); -extern struct rb_node *rb_last(struct rb_root *); +extern struct rb_node *rb_next(const struct rb_node *); +extern struct rb_node *rb_prev(const struct rb_node *); +extern struct rb_node *rb_first(const struct rb_root *); +extern struct rb_node *rb_last(const struct rb_root *); /* Fast replacement of a single node without remove/rebalance/add/rebalance */ extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, diff --git a/src/shared/libosmocore/include/osmocom/core/logging.h b/src/shared/libosmocore/include/osmocom/core/logging.h index b207c1b4..043a8509 100644 --- a/src/shared/libosmocore/include/osmocom/core/logging.h +++ b/src/shared/libosmocore/include/osmocom/core/logging.h @@ -67,7 +67,8 @@ void logp(int subsys, char *file, int line, int cont, const char *format, ...) _ #define DLMUX -4 #define DLMI -5 #define DLMIB -6 -#define OSMO_NUM_DLIB 6 +#define DLSMS -7 +#define OSMO_NUM_DLIB 7 struct log_category { uint8_t loglevel; diff --git a/src/shared/libosmocore/include/osmocom/core/utils.h b/src/shared/libosmocore/include/osmocom/core/utils.h index 940c25f8..315757c9 100644 --- a/src/shared/libosmocore/include/osmocom/core/utils.h +++ b/src/shared/libosmocore/include/osmocom/core/utils.h @@ -34,7 +34,7 @@ int osmo_hexparse(const char *str, uint8_t *b, int max_len); char *osmo_ubit_dump(const uint8_t *bits, unsigned int len); char *osmo_hexdump(const unsigned char *buf, int len); -char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len); +char *osmo_hexdump_nospc(const unsigned char *buf, int len); #define osmo_static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1]; diff --git a/src/shared/libosmocore/include/osmocom/gsm/Makefile.am b/src/shared/libosmocore/include/osmocom/gsm/Makefile.am index 5971d0c5..fc1abfe8 100644 --- a/src/shared/libosmocore/include/osmocom/gsm/Makefile.am +++ b/src/shared/libosmocore/include/osmocom/gsm/Makefile.am @@ -1,6 +1,7 @@ osmogsm_HEADERS = a5.h comp128.h gsm0808.h gsm48_ie.h mncc.h rxlev_stat.h \ gsm0480.h gsm48.h gsm_utils.h rsl.h tlv.h abis_nm.h \ - sysinfo.h prim.h gsm0502.h lapd_core.h lapdm.h + sysinfo.h prim.h gsm0502.h lapd_core.h lapdm.h \ + gsm0411_utils.h gsm0411_smc.h gsm0411_smr.h SUBDIRS = protocol diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smc.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smc.h new file mode 100644 index 00000000..e1508a2d --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smc.h @@ -0,0 +1,62 @@ +#ifndef _GSM0411_SMC_H +#define _GSM0411_SMC_H + +#include <osmocom/gsm/protocol/gsm_04_11.h> + +#define GSM411_MMSMS_EST_REQ 0x310 +#define GSM411_MMSMS_EST_IND 0x312 +#define GSM411_MMSMS_EST_CNF 0x311 +#define GSM411_MMSMS_REL_REQ 0x320 +#define GSM411_MMSMS_REL_IND 0x322 +#define GSM411_MMSMS_DATA_REQ 0x330 +#define GSM411_MMSMS_DATA_IND 0x332 +#define GSM411_MMSMS_UNIT_DATA_REQ 0x340 +#define GSM411_MMSMS_UNIT_DATA_IND 0x342 +#define GSM411_MMSMS_ERR_IND 0x372 + +#define GSM411_MNSMS_ABORT_REQ 0x101 +#define GSM411_MNSMS_DATA_REQ 0x102 +#define GSM411_MNSMS_DATA_IND 0x103 +#define GSM411_MNSMS_EST_REQ 0x104 +#define GSM411_MNSMS_EST_IND 0x105 +#define GSM411_MNSMS_ERROR_IND 0x106 +#define GSM411_MNSMS_REL_REQ 0x107 + +struct gsm411_smc_inst { + int network; /* is this a MO (0) or MT (1) transfer */ + int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg); + int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg, int cp_msg_type); + + enum gsm411_cp_state cp_state; + struct osmo_timer_list cp_timer; + struct msgb *cp_msg; /* store pending message */ + int cp_rel; /* store pending release */ + int cp_retx; /* retry counter */ + int cp_max_retr; /* maximum retry */ + int cp_tc1; /* timer value TC1* */ + +}; + +extern const struct value_string gsm411_cp_cause_strs[]; + +/* init a new instance */ +void gsm411_smc_init(struct gsm411_smc_inst *inst, int network, + int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg), + int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg, int cp_msg_type)); + +/* clear instance */ +void gsm411_smc_clear(struct gsm411_smc_inst *inst); + +/* message from upper layer */ +int gsm411_smc_send(struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg); + +/* message from lower layer */ +int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg, int cp_msg_type); + +#endif /* _GSM0411_SMC_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smr.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smr.h new file mode 100644 index 00000000..5ea8584d --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smr.h @@ -0,0 +1,45 @@ +#ifndef _GSM0411_SMR_H +#define _GSM0411_SMR_H + +#include <osmocom/gsm/protocol/gsm_04_11.h> + +#define GSM411_SM_RL_DATA_REQ 0x401 +#define GSM411_SM_RL_DATA_IND 0x402 +#define GSM411_SM_RL_MEM_AVAIL_REQ 0x403 +#define GSM411_SM_RL_MEM_AVAIL_IND 0x404 +#define GSM411_SM_RL_REPORT_REQ 0x405 +#define GSM411_SM_RL_REPORT_IND 0x406 + +struct gsm411_smr_inst { + int network; /* is this a MO (0) or MT (1) transfer */ + int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg); + int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg); + + enum gsm411_rp_state rp_state; + struct osmo_timer_list rp_timer; +}; + +extern const struct value_string gsm411_rp_cause_strs[]; + +/* init a new instance */ +void gsm411_smr_init(struct gsm411_smr_inst *inst, int network, + int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg), + int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg)); + +/* clear instance */ +void gsm411_smr_clear(struct gsm411_smr_inst *inst); + +/* message from upper layer */ +int gsm411_smr_send(struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg); + +/* message from lower layer */ +int gsm411_smr_recv(struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg); + +#endif /* _GSM0411_SMR_H */ + diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0411_utils.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_utils.h new file mode 100644 index 00000000..a030f581 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_utils.h @@ -0,0 +1,30 @@ +#ifndef _GSM0411_UTILS_H +#define _GSM0411_UTILS_H + +struct msgb *gsm411_msgb_alloc(void); + +/* Generate 03.40 TP-SCTS */ +void gsm340_gen_scts(uint8_t *scts, time_t time); + +/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */ +time_t gsm340_scts(uint8_t *scts); + +/* decode validity period. return minutes */ +unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp); + +/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */ +enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs); + +/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */ +int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type, + uint8_t plan, const char *number); + +/* Prefix msg with a RP header */ +int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type, + uint8_t rp_msg_ref); + +/* Prefix msg with a 04.08/04.11 CP header */ +int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans, + uint8_t msg_type); + +#endif /* _GSM0411_UTILS_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/lapdm.h b/src/shared/libosmocore/include/osmocom/gsm/lapdm.h index cc9c63fe..52e8fc52 100644 --- a/src/shared/libosmocore/include/osmocom/gsm/lapdm.h +++ b/src/shared/libosmocore/include/osmocom/gsm/lapdm.h @@ -72,8 +72,8 @@ struct lapdm_msg_ctx { int lapdm_fmt; uint8_t chan_nr; uint8_t link_id; - uint8_t ta_ind; - uint8_t tx_power_ind; + uint8_t ta_ind; /* TA indicated by network */ + uint8_t tx_power_ind; /* MS power indicated by network */ }; /*! \brief LAPDm datalink like TS 04.06 / Section 3.5.2 */ @@ -113,6 +113,9 @@ struct lapdm_entity { /*! \brief pointer to \ref lapdm_channel of which we're part */ struct lapdm_channel *lapdm_ch; + + uint8_t ta; /* TA used and indicated to network */ + uint8_t tx_power; /* MS power used and indicated to network */ }; /*! \brief the two lapdm_entities that form a GSM logical channel (ACCH + DCCH) */ @@ -127,7 +130,7 @@ const char *get_rsl_name(int value); extern const char *lapdm_state_names[]; /* initialize a LAPDm entity */ -void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode); +void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode, int t200); void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode); /* deinitialize a LAPDm entity */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h index c6a2b193..f37152fe 100644 --- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h @@ -5,7 +5,7 @@ /* GSM TS 04.11 definitions */ -/* Chapter 5.2.3: SMC-CS states at the network side */ +/* Chapter 5.2.3: SMC-CS states at the user/network side */ enum gsm411_cp_state { GSM411_CPS_IDLE = 0, GSM411_CPS_MM_CONN_PENDING = 1, /* only MT ! */ @@ -13,11 +13,12 @@ enum gsm411_cp_state { GSM411_CPS_MM_ESTABLISHED = 3, }; -/* Chapter 6.2.2: SMR states at the network side */ +/* Chapter 6.2.2: SMR states at the user/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, + GSM411_RPS_WAIT_FOR_RETRANS_T = 4, }; /* Chapter 8.1.2 (refers to GSM 04.07 Chapter 11.2.3.1.1 */ @@ -95,7 +96,8 @@ enum gsm411_rp_cause { #define GSM411_TMR_TRAM 30, 0 /* 25 < x < 35 seconds */ #define GSM411_TMR_TR2M 15, 0 /* 12 < x < 20 seconds */ -#define GSM411_TMR_TC1A 30, 0 +#define GSM411_TMR_TC1A 30, 0 /* TR1M - 10 */ +#define GSM411_TMR_TC1A_SEC 30 /* TR1M - 10 */ /* Chapter 8.2.1 */ struct gsm411_rp_hdr { diff --git a/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h index 9a7c9e52..2de4f192 100644 --- a/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h +++ b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h @@ -48,6 +48,8 @@ struct telnet_connection { int telnet_init(void *tall_ctx, void *priv, int port); +void telnet_exit(void); + /*! }@ */ #endif /* TELNET_INTERFACE_H */ diff --git a/src/shared/libosmocore/include/osmocom/vty/vty.h b/src/shared/libosmocore/include/osmocom/vty/vty.h index ffe3c591..83506c5e 100644 --- a/src/shared/libosmocore/include/osmocom/vty/vty.h +++ b/src/shared/libosmocore/include/osmocom/vty/vty.h @@ -176,6 +176,7 @@ int vty_shell_serv (struct vty *); void vty_hello (struct vty *); void *vty_current_index(struct vty *); int vty_current_node(struct vty *vty); +enum node_type vty_go_parent(struct vty *vty); extern void *tall_vty_ctx; diff --git a/src/shared/libosmocore/src/Makefile.am b/src/shared/libosmocore/src/Makefile.am index 6c0398bc..25da356a 100644 --- a/src/shared/libosmocore/src/Makefile.am +++ b/src/shared/libosmocore/src/Makefile.am @@ -19,7 +19,7 @@ libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c bits.c \ if ENABLE_PLUGIN libosmocore_la_SOURCES += plugin.c -libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) -ldl +libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) $(LIBRARY_DL) else libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) endif diff --git a/src/shared/libosmocore/src/backtrace.c b/src/shared/libosmocore/src/backtrace.c index 189a3cec..023671c2 100644 --- a/src/shared/libosmocore/src/backtrace.c +++ b/src/shared/libosmocore/src/backtrace.c @@ -57,4 +57,8 @@ void osmo_generate_backtrace(void) free(strings); } +#else +void osmo_generate_backtrace(void) +{ +} #endif diff --git a/src/shared/libosmocore/src/conv.c b/src/shared/libosmocore/src/conv.c index f47d75cd..0416d272 100644 --- a/src/shared/libosmocore/src/conv.c +++ b/src/shared/libosmocore/src/conv.c @@ -29,8 +29,10 @@ /*! \file conv.c * \file Osmocom convolutional encoder and decoder */ - +#include "config.h" +#ifdef HAVE_ALLOCA_H #include <alloca.h> +#endif #include <stdint.h> #include <stdlib.h> #include <string.h> diff --git a/src/shared/libosmocore/src/gsm/Makefile.am b/src/shared/libosmocore/src/gsm/Makefile.am index f27dff2f..7a49dba9 100644 --- a/src/shared/libosmocore/src/gsm/Makefile.am +++ b/src/shared/libosmocore/src/gsm/Makefile.am @@ -10,6 +10,8 @@ lib_LTLIBRARIES = libosmogsm.la libosmogsm_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c gsm_utils.c \ rsl.c gsm48.c gsm48_ie.c gsm0808.c sysinfo.c \ gprs_cipher_core.c gsm0480.c abis_nm.c gsm0502.c \ + gsm0411_utils.c gsm0411_smc.c gsm0411_smr.c \ lapd_core.c lapdm.c + libosmogsm_la_LDFLAGS = -version-info $(LIBVERSION) libosmogsm_la_LIBADD = $(top_builddir)/src/libosmocore.la diff --git a/src/shared/libosmocore/src/gsm/gsm0411_smc.c b/src/shared/libosmocore/src/gsm/gsm0411_smc.c new file mode 100644 index 00000000..54e6129c --- /dev/null +++ b/src/shared/libosmocore/src/gsm/gsm0411_smc.c @@ -0,0 +1,539 @@ +/* Point-to-Point (PP) Short Message Service (SMS) + * Support on Mobile Radio Interface + * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ + +/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de> + * (C) 2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010 by On-Waves + * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/* Notes on msg: + * + * Messages from lower layer are freed by lower layer. + * + * Messages to upper layer are freed after upper layer call returns, so upper + * layer cannot use data after returning. Upper layer must not free the msg. + * + * This implies: Lower layer messages can be forwarded to upper layer. + * + * Upper layer messages are freed by lower layer, so they must not be freed + * after calling lower layer. + * + * + * Notes on release: + * + * Whenever the process returns to IDLE, the MM connection is released using + * MMSMS-REL-REQ. It is allowed to destroy this process while processing + * this message. + * + * There is expeption, if MMSMS-REL-IND is received from lower layer, the + * process returns to IDLE without sending MMSMS-REL-REQ. + * + */ + +#include <string.h> +#include <errno.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/timer.h> + +#include <osmocom/gsm/gsm0411_utils.h> +#include <osmocom/gsm/gsm0411_smc.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> + +static void cp_timer_expired(void *data); + +#define MAX_SMS_RETRY 2 + +/* init a new instance */ +void gsm411_smc_init(struct gsm411_smc_inst *inst, int network, + int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg), + int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg, int cp_msg_type)) +{ + memset(inst, 0, sizeof(*inst)); + inst->network = network; + inst->cp_max_retr = MAX_SMS_RETRY; + inst->cp_tc1 = GSM411_TMR_TC1A_SEC / (inst->cp_max_retr + 1); + inst->cp_state = GSM411_CPS_IDLE; + inst->mn_recv = mn_recv; + inst->mm_send = mm_send; + + LOGP(DLSMS, LOGL_INFO, "New SMC instance created\n"); +} + +/* clear instance */ +void gsm411_smc_clear(struct gsm411_smc_inst *inst) +{ + LOGP(DLSMS, LOGL_INFO, "Clear SMC instance\n"); + + osmo_timer_del(&inst->cp_timer); + + /* free stored msg */ + if (inst->cp_msg) { + LOGP(DLSMS, LOGL_INFO, "Dropping pending message\n"); + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } +} + +const char *smc_state_names[] = { + "IDLE", + "MM_CONN_PENDING", + "WAIT_CP_ACK", + "MM_ESTABLISHED", +}; + +const struct value_string gsm411_cp_cause_strs[] = { + { GSM411_CP_CAUSE_NET_FAIL, "Network Failure" }, + { GSM411_CP_CAUSE_CONGESTION, "Congestion" }, + { GSM411_CP_CAUSE_INV_TRANS_ID, "Invalid Transaction ID" }, + { GSM411_CP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" }, + { GSM411_CP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" }, + { GSM411_CP_CAUSE_MSGTYPE_NOTEXIST, "Message Type doesn't exist" }, + { GSM411_CP_CAUSE_MSG_INCOMP_STATE, + "Message incompatible with protocol state" }, + { GSM411_CP_CAUSE_IE_NOTEXIST, "IE does not exist" }, + { GSM411_CP_CAUSE_PROTOCOL_ERR, "Protocol Error" }, + { 0, 0 } +}; + +static void new_cp_state(struct gsm411_smc_inst *inst, + enum gsm411_cp_state state) +{ + LOGP(DLSMS, LOGL_INFO, "New CP state %s -> %s\n", + smc_state_names[inst->cp_state], smc_state_names[state]); + inst->cp_state = state; +} + +static int gsm411_tx_cp_error(struct gsm411_smc_inst *inst, uint8_t cause) +{ + struct msgb *nmsg = gsm411_msgb_alloc(); + uint8_t *causep; + + LOGP(DLSMS, LOGL_NOTICE, "TX CP-ERROR, cause %d (%s)\n", cause, + get_value_string(gsm411_cp_cause_strs, cause)); + + causep = msgb_put(nmsg, 1); + *causep = cause; + + return inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg, + GSM411_MT_CP_ERROR); +} + +/* etablish SMC connection */ +static int gsm411_mnsms_est_req(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + struct msgb *nmsg; + + if (inst->cp_msg) { + LOGP(DLSMS, LOGL_FATAL, "EST REQ, but we already have an " + "cp_msg. This should never happen, please fix!\n"); + msgb_free(inst->cp_msg); + } + + inst->cp_msg = msg; + new_cp_state(inst, GSM411_CPS_MM_CONN_PENDING); + /* clear stored release flag */ + inst->cp_rel = 0; + /* send MMSMS_EST_REQ */ + nmsg = gsm411_msgb_alloc(); + return inst->mm_send(inst, GSM411_MMSMS_EST_REQ, nmsg, 0); +} + +static int gsm411_mmsms_send_msg(struct gsm411_smc_inst *inst) +{ + struct msgb *nmsg; + + LOGP(DLSMS, LOGL_INFO, "Send CP data\n"); + /* reset retry counter */ + if (inst->cp_state != GSM411_CPS_WAIT_CP_ACK) + inst->cp_retx = 0; + /* 5.2.3.1.2: enter MO-wait for CP-ACK */ + /* 5.2.3.2.3: enter MT-wait for CP-ACK */ + new_cp_state(inst, GSM411_CPS_WAIT_CP_ACK); + inst->cp_timer.data = inst; + inst->cp_timer.cb = cp_timer_expired; + /* 5.3.2.1: Set Timer TC1A */ + osmo_timer_schedule(&inst->cp_timer, inst->cp_tc1, 0); + /* clone cp_msg */ + nmsg = gsm411_msgb_alloc(); + memcpy(msgb_put(nmsg, inst->cp_msg->len), inst->cp_msg->data, + inst->cp_msg->len); + /* send MMSMS_DATA_REQ with CP-DATA */ + return inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg, + GSM411_MT_CP_DATA); +} + +static int gsm411_mmsms_est_cnf(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + if (!inst->cp_msg) { + LOGP(DLSMS, LOGL_FATAL, "EST CNF, but we have no cp_msg. This " + "should never happen, please fix!\n"); + return -EINVAL; + } + + return gsm411_mmsms_send_msg(inst); +} + +/* SMC TC1* is expired */ +static void cp_timer_expired(void *data) +{ + struct gsm411_smc_inst *inst = data; + struct msgb *nmsg; + + if (inst->cp_retx == inst->cp_max_retr) { + + LOGP(DLSMS, LOGL_INFO, "TC1* timeout, no more retries.\n"); + /* 5.3.2.1: enter idle state */ + new_cp_state(inst, GSM411_CPS_IDLE); + /* indicate error */ + nmsg = gsm411_msgb_alloc(); + inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg); + msgb_free(nmsg); + /* free pending stored msg */ + if (inst->cp_msg) { + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } + /* release MM connection */ + nmsg = gsm411_msgb_alloc(); + inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); + return; + } + + LOGP(DLSMS, LOGL_INFO, "TC1* timeout, retrying...\n"); + inst->cp_retx++; + gsm411_mmsms_est_cnf(inst, NULL); +} + +static int gsm411_mmsms_cp_ack(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + /* free stored msg */ + if (inst->cp_msg) { + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } + + LOGP(DLSMS, LOGL_INFO, "Received CP-ACK\n"); + /* 5.3.2.1 enter MM Connection established */ + new_cp_state(inst, GSM411_CPS_MM_ESTABLISHED); + /* 5.3.2.1: Reset Timer TC1* */ + osmo_timer_del(&inst->cp_timer); + + /* pending release? */ + if (inst->cp_rel) { + struct msgb *nmsg; + + LOGP(DLSMS, LOGL_INFO, "We have pending release.\n"); + new_cp_state(inst, GSM411_CPS_IDLE); + /* release MM connection */ + nmsg = gsm411_msgb_alloc(); + return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); + } + + return 0; +} + +static int gsm411_mmsms_cp_data(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + struct msgb *nmsg; + int mt = GSM411_MNSMS_DATA_IND; + + LOGP(DLSMS, LOGL_INFO, "Received CP-DATA\n"); + /* 5.3.1 enter MM Connection established (if idle) */ + if (inst->cp_state == GSM411_CPS_IDLE) { + new_cp_state(inst, GSM411_CPS_MM_ESTABLISHED); + mt = GSM411_MNSMS_EST_IND; + /* clear stored release flag */ + inst->cp_rel = 0; + } + /* send MMSMS_DATA_REQ (CP ACK) */ + nmsg = gsm411_msgb_alloc(); + inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg, GSM411_MT_CP_ACK); + /* indicate data */ + inst->mn_recv(inst, mt, msg); + + return 0; +} + +/* send CP DATA */ +static int gsm411_mnsms_data_req(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + if (inst->cp_msg) { + LOGP(DLSMS, LOGL_FATAL, "DATA REQ, but we already have an " + "cp_msg. This should never happen, please fix!\n"); + msgb_free(inst->cp_msg); + } + + /* store and send */ + inst->cp_msg = msg; + return gsm411_mmsms_send_msg(inst); +} + +/* release SMC connection */ +static int gsm411_mnsms_rel_req(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + struct msgb *nmsg; + + msgb_free(msg); + + /* discard silently */ + if (inst->cp_state == GSM411_CPS_IDLE) + return 0; + + /* store release, until established or released */ + if (inst->cp_state != GSM411_CPS_MM_ESTABLISHED) { + LOGP(DLSMS, LOGL_NOTICE, "Cannot release yet.\n"); + inst->cp_rel = 1; + return 0; + } + + /* free stored msg */ + if (inst->cp_msg) { + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } + + new_cp_state(inst, GSM411_CPS_IDLE); + /* release MM connection */ + nmsg = gsm411_msgb_alloc(); + return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); +} + +static int gsm411_mmsms_cp_error(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + struct msgb *nmsg; + + /* free stored msg */ + if (inst->cp_msg) { + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } + + LOGP(DLSMS, LOGL_INFO, "Received CP-ERROR\n"); + /* 5.3.4 enter idle */ + new_cp_state(inst, GSM411_CPS_IDLE); + /* indicate error */ + inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, msg); + /* release MM connection */ + nmsg = gsm411_msgb_alloc(); + return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); +} + +static int gsm411_mmsms_rel_ind(struct gsm411_smc_inst *inst, struct msgb *msg) +{ + struct msgb *nmsg; + + /* free stored msg */ + if (inst->cp_msg) { + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } + + LOGP(DLSMS, LOGL_INFO, "MM layer is released\n"); + /* 5.3.4 enter idle */ + new_cp_state(inst, GSM411_CPS_IDLE); + /* indicate error */ + nmsg = gsm411_msgb_alloc(); + inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg); + msgb_free(nmsg); + + return 0; +} + +/* abort SMC connection */ +static int gsm411_mnsms_abort_req(struct gsm411_smc_inst *inst, + struct msgb *msg) +{ + struct msgb *nmsg; + + /* free stored msg */ + if (inst->cp_msg) { + msgb_free(inst->cp_msg); + inst->cp_msg = NULL; + } + + /* 5.3.4 go idle */ + new_cp_state(inst, GSM411_CPS_IDLE); + /* send MMSMS_DATA_REQ with CP-ERROR */ + inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, msg, GSM411_MT_CP_ERROR); + /* release MM connection */ + nmsg = gsm411_msgb_alloc(); + return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); +} + +/* statefull handling for MNSMS SAP messages */ +static struct smcdownstate { + uint32_t states; + int type; + const char *name; + int (*rout) (struct gsm411_smc_inst *inst, + struct msgb *msg); +} smcdownstatelist[] = { + /* establish request */ + {SBIT(GSM411_CPS_IDLE), + GSM411_MNSMS_EST_REQ, + "MNSMS-EST-REQ", gsm411_mnsms_est_req}, + + /* release request */ + {ALL_STATES, + GSM411_MNSMS_REL_REQ, + "MNSMS-REL-REQ", gsm411_mnsms_rel_req}, + + /* data request */ + {SBIT(GSM411_CPS_MM_ESTABLISHED), + GSM411_MNSMS_DATA_REQ, + "MNSMS-DATA-REQ", gsm411_mnsms_data_req}, + + /* abort request */ + {ALL_STATES - SBIT(GSM411_CPS_IDLE), + GSM411_MNSMS_ABORT_REQ, + "MNSMS-ABORT-REQ", gsm411_mnsms_abort_req}, +}; + +#define SMCDOWNSLLEN \ + (sizeof(smcdownstatelist) / sizeof(struct smcdownstate)) + +/* message from upper layer */ +int gsm411_smc_send(struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg) +{ + int i, rc; + + /* find function for current state and message */ + for (i = 0; i < SMCDOWNSLLEN; i++) { + if ((msg_type == smcdownstatelist[i].type) + && (SBIT(inst->cp_state) & smcdownstatelist[i].states)) + break; + } + if (i == SMCDOWNSLLEN) { + LOGP(DLSMS, LOGL_NOTICE, "Message %u unhandled at this state " + "%s.\n", msg_type, smc_state_names[inst->cp_state]); + msgb_free(msg); + return 0; + } + + LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n", + smcdownstatelist[i].name, smc_state_names[inst->cp_state]); + + rc = smcdownstatelist[i].rout(inst, msg); + + return rc; +} + +/* statefull handling for MMSMS SAP messages */ +static struct smcdatastate { + uint32_t states; + int type, cp_type; + const char *name; + int (*rout) (struct gsm411_smc_inst *inst, + struct msgb *msg); +} smcdatastatelist[] = { + /* establish confirm */ + {SBIT(GSM411_CPS_MM_CONN_PENDING), + GSM411_MMSMS_EST_CNF, 0, + "MMSMS-EST-CNF", gsm411_mmsms_est_cnf}, + + /* establish indication (CP DATA) */ + {SBIT(GSM411_CPS_IDLE), + GSM411_MMSMS_EST_IND, GSM411_MT_CP_DATA, + "MMSMS-EST-IND (CP DATA)", gsm411_mmsms_cp_data}, + + /* data indication (CP DATA) */ + {SBIT(GSM411_CPS_MM_ESTABLISHED), + GSM411_MMSMS_DATA_IND, GSM411_MT_CP_DATA, + "MMSMS-DATA-IND (CP DATA)", gsm411_mmsms_cp_data}, + + /* data indication (CP ACK) */ + {SBIT(GSM411_CPS_WAIT_CP_ACK), + GSM411_MMSMS_DATA_IND, GSM411_MT_CP_ACK, + "MMSMS-DATA-IND (CP ACK)", gsm411_mmsms_cp_ack}, + + /* data indication (CP ERROR) */ + {ALL_STATES, + GSM411_MMSMS_DATA_IND, GSM411_MT_CP_ERROR, + "MMSMS-DATA-IND (CP_ERROR)", gsm411_mmsms_cp_error}, + + /* release indication */ + {ALL_STATES - SBIT(GSM411_CPS_IDLE), + GSM411_MMSMS_REL_IND, 0, + "MMSMS-REL-IND", gsm411_mmsms_rel_ind}, + +}; + +#define SMCDATASLLEN \ + (sizeof(smcdatastatelist) / sizeof(struct smcdatastate)) + +/* message from lower layer + * WARNING: We must not free msg, since it will be performed by the + * lower layer. */ +int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg, int cp_msg_type) +{ + int i, rc; + + /* find function for current state and message */ + for (i = 0; i < SMCDATASLLEN; i++) { + /* state must machtch, MM message must match + * CP msg must match only in case of MMSMS_DATA_IND + */ + if ((msg_type == smcdatastatelist[i].type) + && (SBIT(inst->cp_state) & smcdatastatelist[i].states) + && (msg_type != GSM411_MMSMS_DATA_IND + || cp_msg_type == smcdatastatelist[i].cp_type)) + break; + } + if (i == SMCDATASLLEN) { + LOGP(DLSMS, LOGL_NOTICE, "Message 0x%x/%u unhandled at this " + "state %s.\n", msg_type, cp_msg_type, + smc_state_names[inst->cp_state]); + if (msg_type == GSM411_MMSMS_EST_IND + || msg_type == GSM411_MMSMS_DATA_IND) { + struct msgb *nmsg; + + LOGP(DLSMS, LOGL_NOTICE, "RX Unimplemented CP " + "msg_type: 0x%02x\n", msg_type); + /* 5.3.4 enter idle */ + new_cp_state(inst, GSM411_CPS_IDLE); + /* indicate error */ + gsm411_tx_cp_error(inst, + GSM411_CP_CAUSE_MSGTYPE_NOTEXIST); + /* send error indication to upper layer */ + nmsg = gsm411_msgb_alloc(); + inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg); + msgb_free(nmsg); + /* release MM connection */ + nmsg = gsm411_msgb_alloc(); + return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, + 0); + } + return 0; + } + + LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n", + smcdatastatelist[i].name, smc_state_names[inst->cp_state]); + + rc = smcdatastatelist[i].rout(inst, msg); + + return rc; +} diff --git a/src/shared/libosmocore/src/gsm/gsm0411_smr.c b/src/shared/libosmocore/src/gsm/gsm0411_smr.c new file mode 100644 index 00000000..d5ca9238 --- /dev/null +++ b/src/shared/libosmocore/src/gsm/gsm0411_smr.c @@ -0,0 +1,451 @@ +/* Point-to-Point (PP) Short Message Service (SMS) + * Support on Mobile Radio Interface + * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ + +/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de> + * (C) 2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010 by On-Waves + * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/* Notes on msg: + * + * Messages from lower layer are freed by lower layer. + * + * Messages to upper layer are freed after upper layer call returns, so upper + * layer cannot use data after returning. Upper layer must not free the msg. + * + * This implies: Lower layer messages can be forwarded to upper layer. + * + * Upper layer messages are freed by lower layer, so they must not be freed + * after calling lower layer. + * + * + * Notes on release: + * + * Sending Abort/Release (MNSMS-ABORT-REQ or MNSMS-REL-REQ) may cause the + * lower layer to become IDLE. Then it is allowed to destroy this instance, + * so sending this this MUST be the last thing that is done. + * + */ + + +#include <string.h> +#include <errno.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/timer.h> +#include <osmocom/gsm/tlv.h> + +#include <osmocom/gsm/gsm0411_utils.h> +#include <osmocom/gsm/gsm0411_smc.h> +#include <osmocom/gsm/gsm0411_smr.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> + +static void rp_timer_expired(void *data); + +/* init a new instance */ +void gsm411_smr_init(struct gsm411_smr_inst *inst, int network, + int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg), + int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg)) +{ + memset(inst, 0, sizeof(*inst)); + inst->network = network; + inst->rp_state = GSM411_RPS_IDLE; + inst->rl_recv = rl_recv; + inst->mn_send = mn_send; + inst->rp_timer.data = inst; + inst->rp_timer.cb = rp_timer_expired; + + LOGP(DLSMS, LOGL_INFO, "New SMR instance created\n"); +} + +/* clear instance */ +void gsm411_smr_clear(struct gsm411_smr_inst *inst) +{ + LOGP(DLSMS, LOGL_INFO, "Clear SMR instance\n"); + + osmo_timer_del(&inst->rp_timer); +} + +const char *smr_state_names[] = { + "IDLE", + "WAIT_FOR_RP_ACK", + "illegal state 2" + "WAIT_TO_TX_RP_ACK", + "WAIT_FOR_RETRANS_T", +}; + +const struct value_string gsm411_rp_cause_strs[] = { + { GSM411_RP_CAUSE_MO_NUM_UNASSIGNED, "(MO) Number not assigned" }, + { GSM411_RP_CAUSE_MO_OP_DET_BARR, "(MO) Operator determined barring" }, + { GSM411_RP_CAUSE_MO_CALL_BARRED, "(MO) Call barred" }, + { GSM411_RP_CAUSE_MO_SMS_REJECTED, "(MO) SMS rejected" }, + { GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER, "(MO) Destination out of order" }, + { GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR, "(MO) Unidentified subscriber" }, + { GSM411_RP_CAUSE_MO_FACILITY_REJ, "(MO) Facility reject" }, + { GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR, "(MO) Unknown subscriber" }, + { GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER, "(MO) Network out of order" }, + { GSM411_RP_CAUSE_MO_TEMP_FAIL, "(MO) Temporary failure" }, + { GSM411_RP_CAUSE_MO_CONGESTION, "(MO) Congestion" }, + { GSM411_RP_CAUSE_MO_RES_UNAVAIL, "(MO) Resource unavailable" }, + { GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR, "(MO) Requested facility not subscribed" }, + { GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL, "(MO) Requested facility not implemented" }, + { GSM411_RP_CAUSE_MO_INTERWORKING, "(MO) Interworking" }, + /* valid only for MT */ + { GSM411_RP_CAUSE_MT_MEM_EXCEEDED, "(MT) Memory Exceeded" }, + /* valid for both directions */ + { GSM411_RP_CAUSE_INV_TRANS_REF, "Invalid Transaction Reference" }, + { GSM411_RP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" }, + { GSM411_RP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" }, + { GSM411_RP_CAUSE_MSGTYPE_NOTEXIST, "Message Type non-existant" }, + { GSM411_RP_CAUSE_MSG_INCOMP_STATE, "Message incompatible with protocol state" }, + { GSM411_RP_CAUSE_IE_NOTEXIST, "Information Element not existing" }, + { GSM411_RP_CAUSE_PROTOCOL_ERR, "Protocol Error" }, + { 0, NULL } +}; + +static void new_rp_state(struct gsm411_smr_inst *inst, + enum gsm411_rp_state state) +{ + LOGP(DLSMS, LOGL_INFO, "New RP state %s -> %s\n", + smr_state_names[inst->rp_state], smr_state_names[state]); + inst->rp_state = state; + + /* stop timer when going idle */ + if (state == GSM411_RPS_IDLE) + osmo_timer_del(&inst->rp_timer); +} + +/* Prefix msg with a RP-DATA header and send as CP-DATA */ +static int gsm411_rp_sendmsg(struct gsm411_smr_inst *inst, struct msgb *msg, + uint8_t rp_msg_type, uint8_t rp_msg_ref, + int mnsms_msg_type) +{ + struct gsm411_rp_hdr *rp; + uint8_t len = msg->len; + + /* GSM 04.11 RP-DATA header */ + rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp)); + rp->len = len + 2; + rp->msg_type = rp_msg_type; + rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */ + + return inst->mn_send(inst, mnsms_msg_type, msg); +} + +static int gsm411_send_rp_error(struct gsm411_smr_inst *inst, + uint8_t msg_ref, uint8_t cause) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + msgb_tv_put(msg, 1, cause); + + LOGP(DLSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause, + get_value_string(gsm411_rp_cause_strs, cause)); + + return gsm411_rp_sendmsg(inst, msg, + (inst->network) ? GSM411_MT_RP_ERROR_MT : GSM411_MT_RP_ERROR_MO, + msg_ref, GSM411_MNSMS_DATA_REQ); +} + +static int gsm411_send_release(struct gsm411_smr_inst *inst) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + LOGP(DLSMS, LOGL_NOTICE, "TX: MNSMS-REL-REQ\n"); + + return inst->mn_send(inst, GSM411_MNSMS_REL_REQ, msg); +} + +static int gsm411_send_abort(struct gsm411_smr_inst *inst) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + msgb_tv_put(msg, 1, 111); //FIXME: better idea ? */ + LOGP(DLSMS, LOGL_NOTICE, "TX: MNSMS-ABORT-REQ\n"); + + return inst->mn_send(inst, GSM411_MNSMS_ABORT_REQ, msg); +} + +static int gsm411_send_report(struct gsm411_smr_inst *inst) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + LOGP(DLSMS, LOGL_NOTICE, "send empty SM_RL_REPORT_IND\n"); + + return inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); +} + +static int gsm411_rl_data_req(struct gsm411_smr_inst *inst, struct msgb *msg) +{ + LOGP(DLSMS, LOGL_DEBUG, "TX SMS RP-DATA\n"); + /* start TR1N and enter 'wait for RP-ACK state' */ + osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR1M); + new_rp_state(inst, GSM411_RPS_WAIT_FOR_RP_ACK); + + return inst->mn_send(inst, GSM411_MNSMS_EST_REQ, msg); +} + +static int gsm411_rl_report_req(struct gsm411_smr_inst *inst, struct msgb *msg) +{ + LOGP(DLSMS, LOGL_DEBUG, "TX SMS REPORT\n"); + new_rp_state(inst, GSM411_RPS_IDLE); + + inst->mn_send(inst, GSM411_MNSMS_DATA_REQ, msg); + gsm411_send_release(inst); + return 0; +} + +static int gsm411_mnsms_est_ind(struct gsm411_smr_inst *inst, struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr*)msg->l3h; + struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; + uint8_t msg_type = rp_data->msg_type & 0x07; + int rc; + + /* check direction */ + if (inst->network == (msg_type & 1)) { + LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); + gsm411_send_rp_error(inst, rp_data->msg_ref, + GSM411_RP_CAUSE_MSG_INCOMP_STATE); + new_rp_state(inst, GSM411_RPS_IDLE); + gsm411_send_release(inst); + return -EINVAL; + } + + switch (msg_type) { + case GSM411_MT_RP_DATA_MT: + case GSM411_MT_RP_DATA_MO: + LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-DATA\n"); + /* start TR2N and enter 'wait to send RP-ACK state' */ + osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR2M); + new_rp_state(inst, GSM411_RPS_WAIT_TO_TX_RP_ACK); + rc = inst->rl_recv(inst, GSM411_SM_RL_DATA_IND, msg); + break; + case GSM411_MT_RP_SMMA_MO: + LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-SMMA\n"); + /* start TR2N and enter 'wait to send RP-ACK state' */ + osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR2M); + new_rp_state(inst, GSM411_RPS_WAIT_TO_TX_RP_ACK); + rc = inst->rl_recv(inst, GSM411_SM_RL_DATA_IND, msg); + break; + default: + LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); + gsm411_send_rp_error(inst, rp_data->msg_ref, + GSM411_RP_CAUSE_MSGTYPE_NOTEXIST); + new_rp_state(inst, GSM411_RPS_IDLE); + rc = -EINVAL; + break; + } + + return rc; +} + +static int gsm411_mnsms_data_ind_tx(struct gsm411_smr_inst *inst, + struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr*)msg->l3h; + struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; + uint8_t msg_type = rp_data->msg_type & 0x07; + int rc; + + /* check direction */ + if (inst->network == (msg_type & 1)) { + LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); + gsm411_send_rp_error(inst, rp_data->msg_ref, + GSM411_RP_CAUSE_MSG_INCOMP_STATE); + new_rp_state(inst, GSM411_RPS_IDLE); + gsm411_send_release(inst); + return -EINVAL; + } + + switch (msg_type) { + case GSM411_MT_RP_ACK_MO: + case GSM411_MT_RP_ACK_MT: + LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-ACK\n"); + new_rp_state(inst, GSM411_RPS_IDLE); + inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); + gsm411_send_release(inst); + return 0; + case GSM411_MT_RP_ERROR_MO: + case GSM411_MT_RP_ERROR_MT: + LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-ERROR\n"); + new_rp_state(inst, GSM411_RPS_IDLE); + inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); + gsm411_send_release(inst); + return 0; + default: + LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); + gsm411_send_rp_error(inst, rp_data->msg_ref, + GSM411_RP_CAUSE_MSGTYPE_NOTEXIST); + new_rp_state(inst, GSM411_RPS_IDLE); + gsm411_send_release(inst); + return -EINVAL; + } + + return rc; +} + +static int gsm411_mnsms_error_ind_tx(struct gsm411_smr_inst *inst, + struct msgb *msg) +{ + LOGP(DLSMS, LOGL_DEBUG, "RX SMS MNSMS-ERROR-IND\n"); + new_rp_state(inst, GSM411_RPS_IDLE); + inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); + gsm411_send_release(inst); + return 0; +} + +static int gsm411_mnsms_error_ind_rx(struct gsm411_smr_inst *inst, + struct msgb *msg) +{ + LOGP(DLSMS, LOGL_DEBUG, "RX SMS MNSMS-ERROR-IND\n"); + new_rp_state(inst, GSM411_RPS_IDLE); + return inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); +} + +/* SMR TR1* is expired */ +static void rp_timer_expired(void *data) +{ + struct gsm411_smr_inst *inst = data; + + if (inst->rp_state == GSM411_RPS_WAIT_TO_TX_RP_ACK) + LOGP(DLSMS, LOGL_DEBUG, "TR2N\n"); + else + LOGP(DLSMS, LOGL_DEBUG, "TR1N\n"); + gsm411_send_report(inst); + gsm411_send_abort(inst); +} + +/* statefull handling for SM-RL SAP messages */ +static struct smrdownstate { + uint32_t states; + int type; + const char *name; + int (*rout) (struct gsm411_smr_inst *inst, + struct msgb *msg); +} smrdownstatelist[] = { + /* data request */ + {SBIT(GSM411_RPS_IDLE), + GSM411_SM_RL_DATA_REQ, + "SM-RL-DATA_REQ", gsm411_rl_data_req}, + + /* report request */ + {SBIT(GSM411_RPS_WAIT_TO_TX_RP_ACK), + GSM411_SM_RL_REPORT_REQ, + "SM-RL-REPORT_REQ", gsm411_rl_report_req}, +}; + +#define SMRDOWNSLLEN \ + (sizeof(smrdownstatelist) / sizeof(struct smrdownstate)) + +/* message from upper layer */ +int gsm411_smr_send(struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg) +{ + int i, rc; + + /* find function for current state and message */ + for (i = 0; i < SMRDOWNSLLEN; i++) { + if ((msg_type == smrdownstatelist[i].type) + && (SBIT(inst->rp_state) & smrdownstatelist[i].states)) + break; + } + if (i == SMRDOWNSLLEN) { + LOGP(DLSMS, LOGL_NOTICE, "Message %u unhandled at this state " + "%s.\n", msg_type, smr_state_names[inst->rp_state]); + msgb_free(msg); + return 0; + } + + LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n", + smrdownstatelist[i].name, smr_state_names[inst->rp_state]); + + rc = smrdownstatelist[i].rout(inst, msg); + + return rc; +} + +/* statefull handling for MMSMS SAP messages */ +static struct smrdatastate { + uint32_t states; + int type; + const char *name; + int (*rout) (struct gsm411_smr_inst *inst, + struct msgb *msg); +} smrdatastatelist[] = { + /* establish indication */ + {SBIT(GSM411_RPS_IDLE), + GSM411_MNSMS_EST_IND, + "MNSMS-EST-IND", gsm411_mnsms_est_ind}, + + /* data indication */ + {SBIT(GSM411_RPS_WAIT_FOR_RP_ACK), + GSM411_MNSMS_DATA_IND, + "MNSMS-DATA-IND", gsm411_mnsms_data_ind_tx}, + + /* error indication */ + {SBIT(GSM411_RPS_WAIT_FOR_RP_ACK), + GSM411_MNSMS_ERROR_IND, + "MNSMS-ERROR-IND", gsm411_mnsms_error_ind_tx}, + + /* error indication */ + {SBIT(GSM411_RPS_WAIT_TO_TX_RP_ACK), + GSM411_MNSMS_ERROR_IND, + "MNSMS-ERROR-IND", gsm411_mnsms_error_ind_rx}, + +}; + +#define SMRDATASLLEN \ + (sizeof(smrdatastatelist) / sizeof(struct smrdatastate)) + +/* message from lower layer + * WARNING: We must not free msg, since it will be performed by the + * lower layer. */ +int gsm411_smr_recv(struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg) +{ + int i, rc; + + /* find function for current state and message */ + for (i = 0; i < SMRDATASLLEN; i++) { + /* state must machtch, MM message must match + * CP msg must match only in case of MMSMS_DATA_IND + */ + if ((msg_type == smrdatastatelist[i].type) + && (SBIT(inst->rp_state) & smrdatastatelist[i].states)) + break; + } + if (i == SMRDATASLLEN) { + LOGP(DLSMS, LOGL_NOTICE, "Message %u unhandled at this state " + "%s.\n", msg_type, smr_state_names[inst->rp_state]); + return 0; + } + + LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n", + smrdatastatelist[i].name, smr_state_names[inst->rp_state]); + + rc = smrdatastatelist[i].rout(inst, msg); + + return rc; +} diff --git a/src/shared/libosmocore/src/gsm/gsm0411_utils.c b/src/shared/libosmocore/src/gsm/gsm0411_utils.c new file mode 100644 index 00000000..21938bf7 --- /dev/null +++ b/src/shared/libosmocore/src/gsm/gsm0411_utils.c @@ -0,0 +1,306 @@ +/* Point-to-Point (PP) Short Message Service (SMS) + * Support on Mobile Radio Interface + * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ + +/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de> + * (C) 2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010 by On-Waves + * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#include <time.h> +#include <string.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/logging.h> + +#include <osmocom/gsm/gsm48.h> +#include <osmocom/gsm/protocol/gsm_04_11.h> + +#define GSM411_ALLOC_SIZE 1024 +#define GSM411_ALLOC_HEADROOM 128 + +struct msgb *gsm411_msgb_alloc(void) +{ + return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM, + "GSM 04.11"); +} + +/* Turn int into semi-octet representation: 98 => 0x89 */ +static uint8_t bcdify(uint8_t value) +{ + uint8_t ret; + + ret = value / 10; + ret |= (value % 10) << 4; + + return ret; +} + +/* Turn semi-octet representation into int: 0x89 => 98 */ +static uint8_t unbcdify(uint8_t value) +{ + uint8_t ret; + + if ((value & 0x0F) > 9 || (value >> 4) > 9) + LOGP(DLSMS, LOGL_ERROR, + "unbcdify got too big nibble: 0x%02X\n", value); + + ret = (value&0x0F)*10; + ret += value>>4; + + return ret; +} + +/* Generate 03.40 TP-SCTS */ +void gsm340_gen_scts(uint8_t *scts, time_t time) +{ + struct tm *tm = gmtime(&time); + + *scts++ = bcdify(tm->tm_year % 100); + *scts++ = bcdify(tm->tm_mon + 1); + *scts++ = bcdify(tm->tm_mday); + *scts++ = bcdify(tm->tm_hour); + *scts++ = bcdify(tm->tm_min); + *scts++ = bcdify(tm->tm_sec); + *scts++ = bcdify(0); /* GMT */ +} + +/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */ +time_t gsm340_scts(uint8_t *scts) +{ + struct tm tm; + uint8_t yr = unbcdify(*scts++); + int ofs; + + memset(&tm, 0x00, sizeof(struct tm)); + + if (yr <= 80) + tm.tm_year = 100 + yr; + else + tm.tm_year = yr; + tm.tm_mon = unbcdify(*scts++) - 1; + tm.tm_mday = unbcdify(*scts++); + tm.tm_hour = unbcdify(*scts++); + tm.tm_min = unbcdify(*scts++); + tm.tm_sec = unbcdify(*scts++); + + /* according to gsm 03.40 time zone is + "expressed in quarters of an hour" */ + ofs = unbcdify(*scts++) * 15*60; + + return mktime(&tm) - ofs; +} + +/* Return the default validity period in minutes */ +static unsigned long gsm340_vp_default(void) +{ + unsigned long minutes; + /* Default validity: two days */ + minutes = 24 * 60 * 2; + return minutes; +} + +/* Decode validity period format 'relative' */ +static unsigned long gsm340_vp_relative(uint8_t *sms_vp) +{ + /* Chapter 9.2.3.12.1 */ + uint8_t vp; + unsigned long minutes; + + vp = *(sms_vp); + if (vp <= 143) + minutes = vp + 1 * 5; + else if (vp <= 167) + minutes = 12*60 + (vp-143) * 30; + else if (vp <= 196) + minutes = vp-166 * 60 * 24; + else + minutes = vp-192 * 60 * 24 * 7; + return minutes; +} + +/* Decode validity period format 'absolute' */ +static unsigned long gsm340_vp_absolute(uint8_t *sms_vp) +{ + /* Chapter 9.2.3.12.2 */ + time_t expires, now; + unsigned long minutes; + + expires = gsm340_scts(sms_vp); + now = time(NULL); + if (expires <= now) + minutes = 0; + else + minutes = (expires-now)/60; + return minutes; +} + +/* Decode validity period format 'relative in integer representation' */ +static unsigned long gsm340_vp_relative_integer(uint8_t *sms_vp) +{ + uint8_t vp; + unsigned long minutes; + vp = *(sms_vp); + if (vp == 0) { + LOGP(DLSMS, LOGL_ERROR, + "reserved relative_integer validity period\n"); + return gsm340_vp_default(); + } + minutes = vp/60; + return minutes; +} + +/* Decode validity period format 'relative in semi-octet representation' */ +static unsigned long gsm340_vp_relative_semioctet(uint8_t *sms_vp) +{ + unsigned long minutes; + minutes = unbcdify(*sms_vp++)*60; /* hours */ + minutes += unbcdify(*sms_vp++); /* minutes */ + minutes += unbcdify(*sms_vp++)/60; /* seconds */ + return minutes; +} + +/* decode validity period. return minutes */ +unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp) +{ + uint8_t fi; /* functionality indicator */ + + switch (sms_vpf) { + case GSM340_TP_VPF_RELATIVE: + return gsm340_vp_relative(sms_vp); + case GSM340_TP_VPF_ABSOLUTE: + return gsm340_vp_absolute(sms_vp); + case GSM340_TP_VPF_ENHANCED: + /* Chapter 9.2.3.12.3 */ + fi = *sms_vp++; + /* ignore additional fi */ + if (fi & (1<<7)) sms_vp++; + /* read validity period format */ + switch (fi & 0x7) { + case 0x0: + return gsm340_vp_default(); /* no vpf specified */ + case 0x1: + return gsm340_vp_relative(sms_vp); + case 0x2: + return gsm340_vp_relative_integer(sms_vp); + case 0x3: + return gsm340_vp_relative_semioctet(sms_vp); + default: + /* The GSM spec says that the SC should reject any + unsupported and/or undefined values. FIXME */ + LOGP(DLSMS, LOGL_ERROR, + "Reserved enhanced validity period format\n"); + return gsm340_vp_default(); + } + case GSM340_TP_VPF_NONE: + default: + return gsm340_vp_default(); + } +} + +/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */ +enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs) +{ + uint8_t cgbits = dcs >> 4; + enum sms_alphabet alpha = DCS_NONE; + + if ((cgbits & 0xc) == 0) { + if (cgbits & 2) { + LOGP(DLSMS, LOGL_NOTICE, + "Compressed SMS not supported yet\n"); + return 0xffffffff; + } + + switch ((dcs >> 2)&0x03) { + case 0: + alpha = DCS_7BIT_DEFAULT; + break; + case 1: + alpha = DCS_8BIT_DATA; + break; + case 2: + alpha = DCS_UCS2; + break; + } + } else if (cgbits == 0xc || cgbits == 0xd) + alpha = DCS_7BIT_DEFAULT; + else if (cgbits == 0xe) + alpha = DCS_UCS2; + else if (cgbits == 0xf) { + if (dcs & 4) + alpha = DCS_8BIT_DATA; + else + alpha = DCS_7BIT_DEFAULT; + } + + return alpha; +} + +/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */ +int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type, + uint8_t plan, const char *number) +{ + int len_in_bytes; + + /* prevent buffer overflows */ + if (strlen(number) > 20) + number = ""; + +// oa[1] = 0xb9; /* networks-specific number, private numbering plan */ + oa[1] = 0x80 | (type << 4) | plan; + + len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, number); + + /* GSM 03.40 tells us the length is in 'useful semi-octets' */ + oa[0] = strlen(number) & 0xff; + + return len_in_bytes; +} + +/* Prefix msg with a RP header */ +int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type, + uint8_t rp_msg_ref) +{ + struct gsm411_rp_hdr *rp; + uint8_t len = msg->len; + + /* GSM 04.11 RP-DATA header */ + rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp)); + rp->len = len + 2; + rp->msg_type = rp_msg_type; + rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */ + + return 0; +} + +/* Prefix msg with a 04.08/04.11 CP header */ +int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans, + uint8_t msg_type) +{ + struct gsm48_hdr *gh; + + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + /* Outgoing needs the highest bit set */ + gh->proto_discr = proto | (trans << 4); + gh->msg_type = msg_type; + + return 0; +} diff --git a/src/shared/libosmocore/src/gsm/lapd_core.c b/src/shared/libosmocore/src/gsm/lapd_core.c index dcc21506..54adbcaa 100644 --- a/src/shared/libosmocore/src/gsm/lapd_core.c +++ b/src/shared/libosmocore/src/gsm/lapd_core.c @@ -1920,10 +1920,11 @@ static int lapd_res_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) if (dl->send_buffer) msgb_free(dl->send_buffer); dl->send_out = 0; - if (msg && msg->len) { + if (msg && msg->len) /* Write data into the send buffer, to be sent first */ dl->send_buffer = msg; - } + else + dl->send_buffer = NULL; /* Discard partly received L3 message */ if (dl->rcv_buffer) { diff --git a/src/shared/libosmocore/src/gsm/lapdm.c b/src/shared/libosmocore/src/gsm/lapdm.c index 82f8b0c1..3d2f3d83 100644 --- a/src/shared/libosmocore/src/gsm/lapdm.c +++ b/src/shared/libosmocore/src/gsm/lapdm.c @@ -116,7 +116,7 @@ static int send_rslms_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx); static void lapdm_dl_init(struct lapdm_datalink *dl, - struct lapdm_entity *entity) + struct lapdm_entity *entity, int t200) { memset(dl, 0, sizeof(*dl)); dl->entity = entity; @@ -127,18 +127,19 @@ static void lapdm_dl_init(struct lapdm_datalink *dl, dl->dl.n200_est_rel = N200_EST_REL; dl->dl.n200 = N200; dl->dl.t203_sec = 0; dl->dl.t203_usec = 0; + dl->dl.t200_sec = t200; dl->dl.t200_usec = 0; } /*! \brief initialize a LAPDm entity and all datalinks inside * \param[in] le LAPDm entity * \param[in] mode \ref lapdm_mode (BTS/MS) */ -void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode) +void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode, int t200) { unsigned int i; for (i = 0; i < ARRAY_SIZE(le->datalink); i++) - lapdm_dl_init(&le->datalink[i], le); + lapdm_dl_init(&le->datalink[i], le, t200); lapdm_entity_set_mode(le, mode); } @@ -152,9 +153,9 @@ void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode) */ void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode) { - lapdm_entity_init(&lc->lapdm_acch, mode); + lapdm_entity_init(&lc->lapdm_acch, mode, 2); /* FIXME: this depends on chan type */ - lapdm_entity_init(&lc->lapdm_dcch, mode); + lapdm_entity_init(&lc->lapdm_dcch, mode, 1); } /*! \brief flush and release all resoures in LAPDm entity */ @@ -478,6 +479,15 @@ static int lapdm_send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg) if (lctx->more) msg->l2h[2] |= LAPDm_MORE; + /* add ACCH header with last indicated tx-power and TA */ + if ((mctx->link_id & 0x40)) { + struct lapdm_entity *le = mdl->entity; + + msg->l2h = msgb_push(msg, 2); + msg->l2h[0] = le->tx_power; + msg->l2h[1] = le->ta; + } + return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id, 23); } @@ -510,8 +520,10 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, if (mctx.link_id & 0x40) { /* It was received from network on SACCH */ - /* If sent by BTS, lapdm_fmt must be B4 */ - if (le->mode == LAPDM_MODE_MS) { + /* If UI on SACCH sent by BTS, lapdm_fmt must be B4 */ + if (le->mode == LAPDM_MODE_MS + && LAPDm_CTRL_is_U(msg->l2h[3]) + && LAPDm_CTRL_U_BITS(msg->l2h[3]) == 0) { mctx.lapdm_fmt = LAPDm_FMT_B4; n201 = N201_B4; LOGP(DLLAPD, LOGL_INFO, "fmt=B4\n"); @@ -530,7 +542,7 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, } else { mctx.lapdm_fmt = LAPDm_FMT_B; LOGP(DLLAPD, LOGL_INFO, "fmt=B\n"); - n201 = 20; // FIXME: select correct size by chan. + n201 = N201_AB_SDCCH; sapi = (msg->l2h[0] >> 2) & 7; } } @@ -742,7 +754,7 @@ static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl) uint8_t sapi = rllh->link_id & 7; struct tlv_parsed tv; uint8_t length; - int n201 = 20; //FIXME + uint8_t n201 = (rllh->link_id & 0x40) ? N201_AB_SACCH : N201_AB_SDCCH; struct osmo_dlsap_prim dp; /* Set LAPDm context for established connection */ @@ -770,9 +782,9 @@ static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl) } /* check if the layer3 message length exceeds N201 */ - if (length + 3 > 21) { /* FIXME: do we know the channel N201? */ + if (length > n201) { LOGP(DLLAPD, LOGL_ERROR, "frame too large: %d > N201(%d) " - "(discarding)\n", length + 3, 21); + "(discarding)\n", length, n201); msgb_free(msg); return send_rll_simple(RSL_MT_REL_IND, &dl->mctx); } @@ -792,23 +804,24 @@ static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl) /* L3 requests transfer of unnumbered information */ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl) { + struct lapdm_entity *le = dl->entity; + int ui_bts = (le->mode == LAPDM_MODE_BTS); struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); uint8_t chan_nr = rllh->chan_nr; uint8_t link_id = rllh->link_id; uint8_t sapi = link_id & 7; struct tlv_parsed tv; int length; - uint8_t ta = 0, tx_power = 0; /* check if the layer3 message length exceeds N201 */ rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); if (TLVP_PRESENT(&tv, RSL_IE_TIMING_ADVANCE)) { - ta = *TLVP_VAL(&tv, RSL_IE_TIMING_ADVANCE); + le->ta = *TLVP_VAL(&tv, RSL_IE_TIMING_ADVANCE); } if (TLVP_PRESENT(&tv, RSL_IE_MS_POWER)) { - tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER); + le->tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER); } if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { LOGP(DLLAPD, LOGL_ERROR, "unit data request without message " @@ -819,15 +832,15 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl) msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); length = TLVP_LEN(&tv, RSL_IE_L3_INFO); /* check if the layer3 message length exceeds N201 */ - if (length + 5 > 23) { /* FIXME: do we know the channel N201? */ + if (length + 4 + !ui_bts > 23) { LOGP(DLLAPD, LOGL_ERROR, "frame too large: %d > N201(%d) " - "(discarding)\n", length + 5, 23); + "(discarding)\n", length, 18 + ui_bts); msgb_free(msg); return -EIO; } LOGP(DLLAPD, LOGL_INFO, "sending unit data (tx_power=%d, ta=%d)\n", - tx_power, ta); + le->tx_power, le->ta); /* Remove RLL header from msgb and set length to L3-info */ msgb_pull_l2h(msg); @@ -835,13 +848,13 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl) msg->tail = msg->data + length; /* Push L1 + LAPDm header on msgb */ - msg->l2h = msgb_push(msg, 2 + 3); - msg->l2h[0] = tx_power; - msg->l2h[1] = ta; + msg->l2h = msgb_push(msg, 4 + !ui_bts); + msg->l2h[0] = le->tx_power; + msg->l2h[1] = le->ta; msg->l2h[2] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, dl->dl.cr.loc2rem.cmd); msg->l2h[3] = LAPDm_CTRL_U(LAPDm_U_UI, 0); - msg->l2h[4] = LAPDm_LEN(length); - // FIXME: short L2 header support + if (!ui_bts) + msg->l2h[4] = LAPDm_LEN(length); /* Tramsmit */ return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, 23); @@ -907,7 +920,7 @@ static int rslms_rx_rll_res_req(struct msgb *msg, struct lapdm_datalink *dl) uint8_t sapi = rllh->link_id & 7; struct tlv_parsed tv; uint8_t length; - uint8_t n201 = 20; //FIXME + uint8_t n201 = (rllh->link_id & 0x40) ? N201_AB_SACCH : N201_AB_SDCCH; struct osmo_dlsap_prim dp; /* Set LAPDm context for established connection */ @@ -1033,6 +1046,7 @@ static int rslms_rx_rll(struct msgb *msg, struct lapdm_channel *lc) if (msgb_l2len(msg) < sizeof(*rllh)) { LOGP(DLLAPD, LOGL_ERROR, "Message too short for RLL hdr!\n"); + msgb_free(msg); return -EINVAL; } @@ -1047,11 +1061,12 @@ static int rslms_rx_rll(struct msgb *msg, struct lapdm_channel *lc) dl = datalink_for_sapi(le, sapi); if (!dl) { LOGP(DLLAPD, LOGL_ERROR, "No instance for SAPI %d!\n", sapi); + msgb_free(msg); return -EINVAL; } - LOGP(DLLAPD, LOGL_INFO, "(%p) RLL Message '%s' received.\n", - lc->name, rsl_msg_name(msg_type)); + LOGP(DLLAPD, LOGL_INFO, "(%p) RLL Message '%s' received. (sapi %d)\n", + lc->name, rsl_msg_name(msg_type), sapi); switch (msg_type) { case RSL_MT_UNIT_DATA_REQ: diff --git a/src/shared/libosmocore/src/logging.c b/src/shared/libosmocore/src/logging.c index db00331a..f8ed4cb1 100644 --- a/src/shared/libosmocore/src/logging.c +++ b/src/shared/libosmocore/src/logging.c @@ -100,6 +100,12 @@ static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = { .description = "A-bis Input Driver for B-Channels (voice)", .enabled = 0, .loglevel = LOGL_NOTICE, }, + [INT2IDX(DLSMS)] = { + .name = "DLSMS", + .description = "Layer3 Short Message Service (SMS)", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\033[1;38m", + }, }; /* You have to keep this in sync with the structure loglevel_strs. */ diff --git a/src/shared/libosmocore/src/rbtree.c b/src/shared/libosmocore/src/rbtree.c index 07e1041a..4e7c0f3a 100644 --- a/src/shared/libosmocore/src/rbtree.c +++ b/src/shared/libosmocore/src/rbtree.c @@ -161,17 +161,14 @@ static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, { if (!other->rb_right || rb_is_black(other->rb_right)) { - struct rb_node *o_left; - if ((o_left = other->rb_left)) - rb_set_black(o_left); + rb_set_black(other->rb_left); rb_set_red(other); __rb_rotate_right(other, root); other = parent->rb_right; } rb_set_color(other, rb_color(parent)); rb_set_black(parent); - if (other->rb_right) - rb_set_black(other->rb_right); + rb_set_black(other->rb_right); __rb_rotate_left(parent, root); node = root->rb_node; break; @@ -198,17 +195,14 @@ static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, { if (!other->rb_left || rb_is_black(other->rb_left)) { - register struct rb_node *o_right; - if ((o_right = other->rb_right)) - rb_set_black(o_right); + rb_set_black(other->rb_right); rb_set_red(other); __rb_rotate_left(other, root); other = parent->rb_left; } rb_set_color(other, rb_color(parent)); rb_set_black(parent); - if (other->rb_left) - rb_set_black(other->rb_left); + rb_set_black(other->rb_left); __rb_rotate_right(parent, root); node = root->rb_node; break; @@ -235,34 +229,34 @@ void rb_erase(struct rb_node *node, struct rb_root *root) node = node->rb_right; while ((left = node->rb_left) != NULL) node = left; + + if (rb_parent(old)) { + if (rb_parent(old)->rb_left == old) + rb_parent(old)->rb_left = node; + else + rb_parent(old)->rb_right = node; + } else + root->rb_node = node; + child = node->rb_right; parent = rb_parent(node); color = rb_color(node); - if (child) - rb_set_parent(child, parent); if (parent == old) { - parent->rb_right = child; parent = node; - } else + } else { + if (child) + rb_set_parent(child, parent); parent->rb_left = child; + node->rb_right = old->rb_right; + rb_set_parent(old->rb_right, node); + } + node->rb_parent_color = old->rb_parent_color; - node->rb_right = old->rb_right; node->rb_left = old->rb_left; - - if (rb_parent(old)) - { - if (rb_parent(old)->rb_left == old) - rb_parent(old)->rb_left = node; - else - rb_parent(old)->rb_right = node; - } else - root->rb_node = node; - rb_set_parent(old->rb_left, node); - if (old->rb_right) - rb_set_parent(old->rb_right, node); + goto color; } @@ -289,7 +283,7 @@ void rb_erase(struct rb_node *node, struct rb_root *root) /* * This function returns the first node (in sort order) of the tree. */ -struct rb_node *rb_first(struct rb_root *root) +struct rb_node *rb_first(const struct rb_root *root) { struct rb_node *n; @@ -301,7 +295,7 @@ struct rb_node *rb_first(struct rb_root *root) return n; } -struct rb_node *rb_last(struct rb_root *root) +struct rb_node *rb_last(const struct rb_root *root) { struct rb_node *n; @@ -313,7 +307,7 @@ struct rb_node *rb_last(struct rb_root *root) return n; } -struct rb_node *rb_next(struct rb_node *node) +struct rb_node *rb_next(const struct rb_node *node) { struct rb_node *parent; @@ -326,7 +320,7 @@ struct rb_node *rb_next(struct rb_node *node) node = node->rb_right; while (node->rb_left) node=node->rb_left; - return node; + return (struct rb_node *)node; } /* No right-hand children. Everything down and left is @@ -341,7 +335,7 @@ struct rb_node *rb_next(struct rb_node *node) return parent; } -struct rb_node *rb_prev(struct rb_node *node) +struct rb_node *rb_prev(const struct rb_node *node) { struct rb_node *parent; @@ -354,7 +348,7 @@ struct rb_node *rb_prev(struct rb_node *node) node = node->rb_left; while (node->rb_right) node=node->rb_right; - return node; + return (struct rb_node *)node; } /* No left-hand children. Go up till we find an ancestor which diff --git a/src/shared/libosmocore/src/socket.c b/src/shared/libosmocore/src/socket.c index 1a1d71dc..8a8829b7 100644 --- a/src/shared/libosmocore/src/socket.c +++ b/src/shared/libosmocore/src/socket.c @@ -18,6 +18,8 @@ #include <sys/socket.h> #include <sys/types.h> +#include <netinet/in.h> + #include <stdio.h> #include <unistd.h> #include <stdint.h> diff --git a/src/shared/libosmocore/src/timer.c b/src/shared/libosmocore/src/timer.c index 217f6521..79d4ad6d 100644 --- a/src/shared/libosmocore/src/timer.c +++ b/src/shared/libosmocore/src/timer.c @@ -69,6 +69,7 @@ static void __add_timer(struct osmo_timer_list *timer) */ void osmo_timer_add(struct osmo_timer_list *timer) { + osmo_timer_del(timer); timer->active = 1; INIT_LLIST_HEAD(&timer->list); __add_timer(timer); diff --git a/src/shared/libosmocore/src/utils.c b/src/shared/libosmocore/src/utils.c index a25479f1..d32c5c07 100644 --- a/src/shared/libosmocore/src/utils.c +++ b/src/shared/libosmocore/src/utils.c @@ -173,11 +173,15 @@ char *osmo_hexdump(const unsigned char *buf, int len) * This function will print a sequence of bytes as hexadecimal numbers, * without any space character between each byte (e.g. "1aefd9") */ -char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) +char *osmo_hexdump_nospc(const unsigned char *buf, int len) { return _osmo_hexdump(buf, len, ""); } + /* Compat with previous typo to preserve abi */ +char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) + __attribute__((alias("osmo_hexdump_nospc"))); + #include "../config.h" #ifdef HAVE_CTYPE_H #include <ctype.h> diff --git a/src/shared/libosmocore/src/vty/telnet_interface.c b/src/shared/libosmocore/src/vty/telnet_interface.c index ed64cdab..167acc18 100644 --- a/src/shared/libosmocore/src/vty/telnet_interface.c +++ b/src/shared/libosmocore/src/vty/telnet_interface.c @@ -221,4 +221,16 @@ void vty_event(enum event event, int sock, struct vty *vty) } } +void telnet_exit(void) +{ + struct telnet_connection *tc, *tc2; + + llist_for_each_entry_safe(tc, tc2, &active_connections, entry) + telnet_close_client(&tc->fd); + + osmo_fd_unregister(&server_socket); + close(server_socket.fd); + talloc_free(tall_telnet_ctx); +} + /*! }@ */ diff --git a/src/shared/libosmocore/src/vty/vty.c b/src/shared/libosmocore/src/vty/vty.c index 5f5e6a4c..da035969 100644 --- a/src/shared/libosmocore/src/vty/vty.c +++ b/src/shared/libosmocore/src/vty/vty.c @@ -802,9 +802,11 @@ static void vty_backward_word(struct vty *vty) static void vty_down_level(struct vty *vty) { vty_out(vty, "%s", VTY_NEWLINE); - /* FIXME: we need to call the exit function of the specific node - * in question, not this generic one that doesn't know all nodes */ - (*config_exit_cmd.func) (NULL, vty, 0, NULL); + /* call the exit function of the specific node */ + if (vty->node > CONFIG_NODE) + vty_go_parent(vty); + else + (*config_exit_cmd.func) (NULL, vty, 0, NULL); vty_prompt(vty); vty->cp = 0; } |