diff options
author | Daniel Willmann <dwillmann@sysmocom.de> | 2018-08-31 17:35:54 +0200 |
---|---|---|
committer | Daniel Willmann <dwillmann@sysmocom.de> | 2018-08-31 17:35:54 +0200 |
commit | e373a09becac632161bb69aa0373fdf41da27b8a (patch) | |
tree | 83b3f579a6762bcfdd903f5d5c5c8415e854ee47 | |
parent | f9f7e016e84f8e0e88692dbd0aa0582794190eb8 (diff) | |
parent | 1a72bafa5df03718bb1c328f3d64078dc5e70feb (diff) |
Merge remote-tracking branch 'origin/master' into daniel/onwaves
Change-Id: Iaba2f4312f251e8324409bdb230df86ab2c2438e
59 files changed, 1480 insertions, 178 deletions
diff --git a/TODO-RELEASE b/TODO-RELEASE index 7b225cc4..64833d23 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -7,6 +7,6 @@ # If any interfaces have been added since the last public release: c:r:a + 1. # If any interfaces have been removed or changed since the last public release: c:r:0. #library what description / commit summary line -gsup gsup.h the 'osmo_gsup_message' struct extended with - session information => ABI changed - SS/USSD information => ABI changed +libosmogsm gsm0480_l3hdr_push() removed from gsm/gsm0480.h (was not exposed) +libosmogsm gsm48_push_l3hdr() (re)introduced in gsm/gsm48.h (GSM 04.08 API) +libosmogsm gsm48_push_l3hdr_tid() a wrapper around gsm48_push_l3hdr() diff --git a/contrib/jenkins_arm.sh b/contrib/jenkins_arm.sh index 8229fc85..1d72f2f5 100755 --- a/contrib/jenkins_arm.sh +++ b/contrib/jenkins_arm.sh @@ -2,6 +2,10 @@ . $(dirname "$0")/jenkins_common.sh + +# from ../configure.ac +WERROR_FLAGS="-Werror -Wno-error=deprecated -Wno-error=deprecated-declarations -Wno-error=cpp" + src_dir="$PWD" build() { build_dir="$1" @@ -14,7 +18,7 @@ build() { --enable-embedded \ --disable-doxygen \ --disable-shared \ - CFLAGS="-Os -ffunction-sections -fdata-sections -nostartfiles -nodefaultlibs -Werror" + CFLAGS="-Os -ffunction-sections -fdata-sections -nostartfiles -nodefaultlibs $WERROR_FLAGS" $MAKE $PARALLEL_MAKE } diff --git a/debian/changelog b/debian/changelog index 80f9e976..9bf32a48 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,120 @@ +libosmocore (0.12.0) unstable; urgency=medium + + [ Pau Espin Pedrol ] + * control_if: Avoid heap-use-after-free in osmo_wqueue_bfd_cb + * configure: Check separately for lib implementing dlopen and dlsym + * tests: bitrev_test: Fix dynamic-stack-buffer-overflow + * tests: gea_test: Use correct max size for key in buffer + * tests: a5_test: Print wrong buffer correctly on error + * gsm: kasumi: Fix dynamic-stack-buffer-overflow on out buffers not multiple of 64 bits + * gsm: lapdm.c: Add missing new line char in notice log string + * ctrl: Log CMD TYPE on invalid ID number + * ctrl: Fix parsing of ERROR recvd msgs with id=err + * gsm0808: Add value_string for encryption algorithms + * ctrl: Introduce ctrl_cmd_parse3 API + * ctrl: ctrl_handle_msg: Avoid sending back received ERROR msgs + * tests: ctrl: Test received ERROR messages are handled correctly + * libosmocoding: clarify return values for TCH decoding functions + * libosmocodec: FR err concealment: Fix too many silent frames generated + * tests: codec: ecu_fr: Print XMAXC fields + * tests: codec: ecu_fr: Add buffer with unequal XMAXC values + * rate_ctr: Improve logging + * logging: log_vty_command_string: Fix undercount of buf alloc size + * logging_vty: Simplify code in config_write_log_single + * logging.c: Fix whitespace typo + + [ Harald Welte ] + * lapdm: Fix back-pointer from lapdm_entity to lapdm_channel + * lapdm: Implement SABM related constraints + * lapdm: cleanup: send_rslms_rll_l3_ui(): Use msgb_tv_push() + * lapdm: send_rslms_rll_l3_ui(): Don't include B4/SACCH IE unless needed + * lapdm: don't enforce contention resolution on SAPI0/DCCH + * Add osmo_timerfd_* functions for osmo_fd-wrapped timerfd + * import isdn4linux HDLC code from linux kernel + * isdnhdlc: Port from kernel to userspace + * mncc: properly export osmo_mncc_name() + * cosmetic: Whitespace fixes in control_if.c + * ctrl: Introduce libosmoctrl.map to avoid unintended exports + * ctrl: Add doxygen API documentation; generate html from it + * debian: Add libosmoctrl-doc sub-package + * gsm_08_08.h: Add enum for LCLS config, control and status + * gsm0808: Add encoding functions for LCLS BSSMAP messages + * gsm0808: Add value_string for LCLS related IEs + * tlv: Add TLVP_VAL_MINLEN() to obtain value _if_ length is >= minimum + * Add osmo_isqrt32() to compute 32bit integer square root + * fsm: Change semantics of LOGPFSML() log-level + * vty: Don't dump deprecated commands in XML export + * vty: Add logging_vty_add_deprecated_subsys + * gsup: Add osmo_gsup_get_err_msg_type() function + * gsup: Add value_string for Session State IE + * gsm 04.80: Add value_string for component type and op code + * Fix embedded (arm-none-eabi) builds + * jenkins_arm.sh: Don't run 'make check' on embedded builds + * jenkins_arch.sh: Accept "arm-none-eabi" as alias for "arm" + * jenkins_arch.sh: Exit with error on unknown architecture + * Don't call abort() directly, always use osmo_panic() + * osmo_panic(): Annotate as __attribute__ ((noreturn)) + * gprs_ns.h: Declare gprs_ns_cause_str() which already existed in c file + + [ Stefan Sperling ] + * define a constant for the max length of called party BCD IE + * introduce vty_out_rate_ctr_group_fmt() function + * Add a 'show rate-counters' VTY command. + * remove unused argument from pad_append_ctr() helper function + * check bssgp_tlv_parse() return code in bssgp_rcvmsg() + * return error to sender upon bssgp_tlv_parse() failure + + [ Neels Hofmeyr ] + * add gsm0808 channel enum to IE val conversion functions + * add gsm0808_cell_id_to_list() + * add support for gsm0808 HANDOVER REQUIRED message + * add gsm0808_create_handover_request_ack() + * add osmo_fsm_inst_state_chg_keep_timer() + * fix gsm0808_permitted_speech(): don't return HR3 for TCH_F + AMR + * add and tweak inter-BSC HO API + * vty/command.c: talloc from tall_vty_cmd_ctx, not NULL + * vty: cosmetic: cmd_deopt(): use talloc_strndup(), not memcpy() + * vty: fix use-after-free and memleaks in is_cmd_ambiguous() + * utils_test: fix isqrt_test calculation range + * utils_test: check stderr to catch sanitizer issues + * add osmo_sockaddr_to_str_and_uint() + + [ Philipp Maier ] + * fsm: guard action callback + * gsm_04_08: add function to get value string + * gsm_08_08: gsm0808_permitted_speech does not have value strings + + [ Thorsten Alteholz ] + * fix spelling + + [ Keith ] + * Add enum gsm48_cause_coding from GSM 04.08 Section 10.5.4.11 + * Add enum gsm48_progress_desc + + [ Daniel Willmann ] + * ports.h: Add ctrl port for osmo-gbproxy + * Add function gprs_nsvc_state_append + * stats_vty: Add asciidoc sections between the different counters + + [ Vadim Yanitskiy ] + * gsm0480: fix: don't overwrite the data of RELEASE_COMPLETE + * GSUP: implement TCAP-like session management + * GSUP: introduce new messages for SS/USSD payloads + * Doxygen: gitignore generated files for libosmoctrl + * gsm/gsm0480.c: introduce gsm0480_extract_ie_by_tag() + * gsm/gsm0480: refactor and expose gsm0480_parse_facility_ie() + * Don't enforce Python 2 for utilities + + [ Alexander Chemeris ] + * coding: Fix (E)GPRS BER calculation to correctly account for puncturing. + * coding: Documentation typo fix. + + [ Alexander Couzens ] + * vty: initialize termios before using it + * stats_statsd: sanitize statsd name + + -- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 27 Jul 2018 17:31:46 +0200 + libosmocore (0.11.0+ow2) unstable; urgency=medium * Release version for On-Waves diff --git a/debian/control b/debian/control index 1de0ceb2..540a8b5e 100644 --- a/debian/control +++ b/debian/control @@ -27,9 +27,9 @@ Architecture: any Multi-Arch: foreign Depends: libosmocodec0 (= ${binary:Version}), libosmocoding0 (= ${binary:Version}), - libosmocore10 (= ${binary:Version}), + libosmocore11 (= ${binary:Version}), libosmogb6 (= ${binary:Version}), - libosmogsm9 (= ${binary:Version}), + libosmogsm10 (= ${binary:Version}), libosmovty4 (= ${binary:Version}), libosmoctrl0 (= ${binary:Version}), libosmosim0 (= ${binary:Version}), @@ -110,7 +110,7 @@ Description: Documentation for the osmo coding library . This package contains the documentation for the libosmocoding library. -Package: libosmocore10 +Package: libosmocore11 Section: libs Architecture: any Multi-Arch: same @@ -124,14 +124,14 @@ Description: Osmo Core library (at least) other programs that are developed in the sphere of Free Software / Open Source mobile communication. . - The libosmocore10 library in particular is a collection of common code used in + The libosmocore11 library in particular is a collection of common code used in various sub-projects inside the Osmocom family of projects. Package: libosmocore-doc Architecture: all Section: doc Depends: ${misc:Depends}, - libosmocore10, + libosmocore11, libjs-jquery, libosmocodec-doc, libosmocoding-doc, @@ -178,7 +178,7 @@ Description: Documentation for the Osmo GPRS Gb library . This package contains the documentation for the libosmogb library. -Package: libosmogsm9 +Package: libosmogsm10 Section: libs Architecture: any Multi-Arch: same @@ -202,7 +202,7 @@ Package: libosmogsm-doc Architecture: all Section: doc Depends: ${misc:Depends}, - libosmogsm9, + libosmogsm10, libjs-jquery Description: Documentation for the Osmo GSM utility library This is part of the libosmocore "meta"-library. The libosmocore library diff --git a/debian/libosmocore10.install b/debian/libosmocore11.install index b73331b9..b73331b9 100644 --- a/debian/libosmocore10.install +++ b/debian/libosmocore11.install diff --git a/debian/libosmogsm9.install b/debian/libosmogsm10.install index 5e617298..5e617298 100644 --- a/debian/libosmogsm9.install +++ b/debian/libosmogsm10.install diff --git a/debian/rules b/debian/rules index 6eb7346c..a9d961c7 100755 --- a/debian/rules +++ b/debian/rules @@ -25,10 +25,6 @@ override_dh_install: override_dh_auto_test: dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false) -override_dh_autoreconf: - echo $(VERSION) > .tarball-version - dh_autoreconf - override_dh_auto_configure: dh_auto_configure -- --enable-static diff --git a/include/Makefile.am b/include/Makefile.am index 38ba14cd..ef8ec656 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -100,6 +100,7 @@ nobase_include_HEADERS = \ osmocom/gsm/prim.h \ osmocom/gsm/l1sap.h \ osmocom/gsm/oap.h \ + osmocom/gsm/oap_client.h \ osmocom/gsm/protocol/gsm_03_40.h \ osmocom/gsm/protocol/gsm_03_41.h \ osmocom/gsm/protocol/gsm_04_08.h \ diff --git a/include/osmocom/core/fsm.h b/include/osmocom/core/fsm.h index 9e1062f5..54bbad5d 100644 --- a/include/osmocom/core/fsm.h +++ b/include/osmocom/core/fsm.h @@ -178,7 +178,7 @@ static inline const char *osmo_fsm_inst_state_name(struct osmo_fsm_inst *fi) */ #define osmo_fsm_inst_state_chg(fi, new_state, timeout_secs, T) \ _osmo_fsm_inst_state_chg(fi, new_state, timeout_secs, T, \ - __BASE_FILE__, __LINE__) + __FILE__, __LINE__) int _osmo_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t new_state, unsigned long timeout_secs, int T, const char *file, int line); @@ -194,7 +194,7 @@ int _osmo_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t new_state, */ #define osmo_fsm_inst_state_chg_keep_timer(fi, new_state) \ _osmo_fsm_inst_state_chg_keep_timer(fi, new_state, \ - __BASE_FILE__, __LINE__) + __FILE__, __LINE__) int _osmo_fsm_inst_state_chg_keep_timer(struct osmo_fsm_inst *fi, uint32_t new_state, const char *file, int line); @@ -205,7 +205,7 @@ int _osmo_fsm_inst_state_chg_keep_timer(struct osmo_fsm_inst *fi, uint32_t new_s * purposes. See there for documentation. */ #define osmo_fsm_inst_dispatch(fi, event, data) \ - _osmo_fsm_inst_dispatch(fi, event, data, __BASE_FILE__, __LINE__) + _osmo_fsm_inst_dispatch(fi, event, data, __FILE__, __LINE__) int _osmo_fsm_inst_dispatch(struct osmo_fsm_inst *fi, uint32_t event, void *data, const char *file, int line); @@ -216,7 +216,7 @@ int _osmo_fsm_inst_dispatch(struct osmo_fsm_inst *fi, uint32_t event, void *data * See there for documentation. */ #define osmo_fsm_inst_term(fi, cause, data) \ - _osmo_fsm_inst_term(fi, cause, data, __BASE_FILE__, __LINE__) + _osmo_fsm_inst_term(fi, cause, data, __FILE__, __LINE__) void _osmo_fsm_inst_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause, void *data, const char *file, int line); @@ -228,7 +228,7 @@ void _osmo_fsm_inst_term(struct osmo_fsm_inst *fi, * purposes. See there for documentation. */ #define osmo_fsm_inst_term_children(fi, cause, data) \ - _osmo_fsm_inst_term_children(fi, cause, data, __BASE_FILE__, __LINE__) + _osmo_fsm_inst_term_children(fi, cause, data, __FILE__, __LINE__) void _osmo_fsm_inst_term_children(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause, void *data, diff --git a/include/osmocom/core/gsmtap.h b/include/osmocom/core/gsmtap.h index b4239f8c..9f5049f8 100644 --- a/include/osmocom/core/gsmtap.h +++ b/include/osmocom/core/gsmtap.h @@ -33,7 +33,7 @@ #define GSMTAP_TYPE_UM 0x01 #define GSMTAP_TYPE_ABIS 0x02 #define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */ -#define GSMTAP_TYPE_SIM 0x04 +#define GSMTAP_TYPE_SIM 0x04 /* ISO 7816 smart card interface */ #define GSMTAP_TYPE_TETRA_I1 0x05 /* tetra air interface */ #define GSMTAP_TYPE_TETRA_I1_BURST 0x06 /* tetra air interface */ #define GSMTAP_TYPE_WMX_BURST 0x07 /* WiMAX burst */ @@ -103,6 +103,18 @@ /* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */ +/* sub-types for GSMTAP_TYPE_SIM */ +#define GSMTAP_SIM_APDU 0x00 /* APDU data (complete APDU) */ +#define GSMTAP_SIM_ATR 0x01 /* card ATR data */ +#define GSMTAP_SIM_PPS_REQ 0x02 /* PPS request data */ +#define GSMTAP_SIM_PPS_RSP 0x03 /* PPS response data */ +#define GSMTAP_SIM_TPDU_HDR 0x04 /* TPDU command header */ +#define GSMTAP_SIM_TPDU_CMD 0x05 /* TPDU command body */ +#define GSMTAP_SIM_TPDU_RSP 0x06 /* TPDU response body */ +#define GSMTAP_SIM_TPDU_SW 0x07 /* TPDU response trailer */ + +/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */ + /* sub-types for TYPE_TETRA_AIR */ #define GSMTAP_TETRA_BSCH 0x01 #define GSMTAP_TETRA_AACH 0x02 diff --git a/include/osmocom/core/logging.h b/include/osmocom/core/logging.h index e68f6181..8464043f 100644 --- a/include/osmocom/core/logging.h +++ b/include/osmocom/core/logging.h @@ -55,17 +55,17 @@ void logp(int subsys, const char *file, int line, int cont, const char *format, #define LOGPC(ss, level, fmt, args...) \ do { \ if (log_check_level(ss, level)) \ - logp2(ss, level, __BASE_FILE__, __LINE__, 1, fmt, ##args); \ + logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args); \ } while(0) /*! Log through the Osmocom logging framework with explicit source. - * If caller_file is passed as NULL, __BASE_FILE__ and __LINE__ are used + * If caller_file is passed as NULL, __FILE__ and __LINE__ are used * instead of caller_file and caller_line (so that this macro here defines * both cases in the same place, and to catch cases where callers fail to pass * a non-null filename string). * \param[in] ss logging subsystem (e.g. \ref DLGLOBAL) * \param[in] level logging level (e.g. \ref LOGL_NOTICE) - * \param[in] caller_file caller's source file string (e.g. __BASE_FILE__) + * \param[in] caller_file caller's source file string (e.g. __FILE__) * \param[in] caller_line caller's source line nr (e.g. __LINE__) * \param[in] fmt format string * \param[in] args variable argument list @@ -74,13 +74,13 @@ void logp(int subsys, const char *file, int line, int cont, const char *format, LOGPSRCC(ss, level, caller_file, caller_line, 0, fmt, ##args) /*! Log through the Osmocom logging framework with explicit source. - * If caller_file is passed as NULL, __BASE_FILE__ and __LINE__ are used + * If caller_file is passed as NULL, __FILE__ and __LINE__ are used * instead of caller_file and caller_line (so that this macro here defines * both cases in the same place, and to catch cases where callers fail to pass * a non-null filename string). * \param[in] ss logging subsystem (e.g. \ref DLGLOBAL) * \param[in] level logging level (e.g. \ref LOGL_NOTICE) - * \param[in] caller_file caller's source file string (e.g. __BASE_FILE__) + * \param[in] caller_file caller's source file string (e.g. __FILE__) * \param[in] caller_line caller's source line nr (e.g. __LINE__) * \param[in] cont continuation (1) or new line (0) * \param[in] fmt format string @@ -92,7 +92,7 @@ void logp(int subsys, const char *file, int line, int cont, const char *format, if (caller_file) \ logp2(ss, level, caller_file, caller_line, cont, fmt, ##args); \ else \ - logp2(ss, level, __BASE_FILE__, __LINE__, cont, fmt, ##args); \ + logp2(ss, level, __FILE__, __LINE__, cont, fmt, ##args); \ }\ } while(0) @@ -228,6 +228,12 @@ enum log_filename_type { LOG_FILENAME_BASENAME, }; +/*! Where on a log line source file and line should be logged. */ +enum log_filename_pos { + LOG_FILENAME_POS_HEADER_END, + LOG_FILENAME_POS_LINE_END, +}; + /*! structure representing a logging target */ struct log_target { struct llist_head entry; /*!< linked list */ @@ -313,6 +319,8 @@ struct log_target { bool print_category_hex; /* Should we print the source file and line, and in which way? */ enum log_filename_type print_filename2; + /* Where on a log line to put the source file info. */ + enum log_filename_pos print_filename_pos; }; /* use the above macros */ @@ -335,6 +343,7 @@ void log_set_print_extended_timestamp(struct log_target *target, int); void log_set_print_timestamp(struct log_target *target, int); void log_set_print_filename(struct log_target *target, int); void log_set_print_filename2(struct log_target *target, enum log_filename_type lft); +void log_set_print_filename_pos(struct log_target *target, enum log_filename_pos pos); void log_set_print_category(struct log_target *target, int); void log_set_print_category_hex(struct log_target *target, int); void log_set_print_level(struct log_target *target, int); diff --git a/include/osmocom/core/msgb.h b/include/osmocom/core/msgb.h index a8dc205b..b1cb6ec7 100644 --- a/include/osmocom/core/msgb.h +++ b/include/osmocom/core/msgb.h @@ -81,6 +81,40 @@ static inline void msgb_queue_free(struct llist_head *queue) while ((msg = msgb_dequeue(queue))) msgb_free(msg); } +/*! Enqueue message buffer to tail of a queue and increment queue size counter + * \param[in] queue linked list header of queue + * \param[in] msg message buffer to be added to the queue + * \param[in] count pointer to variable holding size of the queue + * + * The function will append the specified message buffer \a msg to the queue + * implemented by \ref llist_head \a queue using function \ref msgb_enqueue_count, + * then increment \a count + */ +static inline void msgb_enqueue_count(struct llist_head *queue, struct msgb *msg, + unsigned int *count) +{ + msgb_enqueue(queue, msg); + (*count)++; +} + +/*! Dequeue message buffer from head of queue and decrement queue size counter + * \param[in] queue linked list header of queue + * \param[in] count pointer to variable holding size of the queue + * \returns message buffer (if any) or NULL if queue empty + * + * The function will remove the first message buffer from the queue + * implemented by \ref llist_head \a queue using function \ref msgb_enqueue_count, + * and decrement \a count, all if queue is not empty. + */ +static inline struct msgb *msgb_dequeue_count(struct llist_head *queue, + unsigned int *count) +{ + struct msgb *msg = msgb_dequeue(queue); + if (msg) + (*count)--; + return msg; +} + #ifdef MSGB_DEBUG #include <osmocom/core/panic.h> #define MSGB_ABORT(msg, fmt, args ...) do { \ diff --git a/include/osmocom/core/signal.h b/include/osmocom/core/signal.h index ae78f152..449b9762 100644 --- a/include/osmocom/core/signal.h +++ b/include/osmocom/core/signal.h @@ -34,6 +34,7 @@ typedef int osmo_signal_cbfn(unsigned int subsys, unsigned int signal, void *han /* Management */ +void *osmo_signal_talloc_ctx_init(void *root_ctx); int osmo_signal_register_handler(unsigned int subsys, osmo_signal_cbfn *cbfn, void *data); void osmo_signal_unregister_handler(unsigned int subsys, osmo_signal_cbfn *cbfn, void *data); diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h index 20515b99..f23a2436 100644 --- a/include/osmocom/core/socket.h +++ b/include/osmocom/core/socket.h @@ -24,6 +24,8 @@ struct osmo_fd; #define OSMO_SOCK_F_NO_MCAST_LOOP (1 << 3) /*! disable receiving all multiast even for non-subscribed groups */ #define OSMO_SOCK_F_NO_MCAST_ALL (1 << 4) +/*! use SO_REUSEADDR on UDP ports (required for multicast) */ +#define OSMO_SOCK_F_UDP_REUSEADDR (1 << 5) int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto, const char *host, uint16_t port, unsigned int flags); diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h index dd4461cc..e2d51349 100644 --- a/include/osmocom/core/utils.h +++ b/include/osmocom/core/utils.h @@ -78,7 +78,7 @@ do { \ */ #define OSMO_ASSERT(exp) \ if (!(exp)) { \ - osmo_panic("Assert failed %s %s:%d\n", #exp, __BASE_FILE__, __LINE__); \ + osmo_panic("Assert failed %s %s:%d\n", #exp, __FILE__, __LINE__); \ } /*! duplicate a string using talloc and release its prior content (if any) diff --git a/include/osmocom/gprs/gprs_bssgp.h b/include/osmocom/gprs/gprs_bssgp.h index 2dead692..400c3e00 100644 --- a/include/osmocom/gprs/gprs_bssgp.h +++ b/include/osmocom/gprs/gprs_bssgp.h @@ -207,6 +207,9 @@ int bssgp_fc_in(struct bssgp_flow_control *fc, struct msgb *msg, int bssgp_fc_ms_init(struct bssgp_flow_control *fc_ms, uint16_t bvci, uint16_t nsei, uint32_t max_queue_depth); +void bssgp_flush_all_queues(); +void bssgp_fc_flush_queue(struct bssgp_flow_control *fc); + /* gprs_bssgp_vty.c */ int bssgp_vty_init(void); void bssgp_set_log_ss(int ss); diff --git a/include/osmocom/gsm/gsm0480.h b/include/osmocom/gsm/gsm0480.h index e928d83f..827464e1 100644 --- a/include/osmocom/gsm/gsm0480.h +++ b/include/osmocom/gsm/gsm0480.h @@ -108,6 +108,11 @@ int gsm0480_parse_facility_ie(const uint8_t *facility_ie, uint16_t length, int gsm0480_decode_ss_request(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request *request); +struct msgb *gsm0480_msgb_alloc_name(const char *name); +struct msgb *gsm0480_gen_ussd_resp_7bit(uint8_t invoke_id, const char *text); +struct msgb *gsm0480_gen_return_error(uint8_t invoke_id, uint8_t error_code); +struct msgb *gsm0480_gen_reject(int invoke_id, uint8_t problem_tag, uint8_t problem_code); + struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text); struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *text); struct msgb *gsm0480_create_notifySS(const char *text); @@ -116,6 +121,3 @@ struct msgb *gsm0480_create_ussd_release_complete(void); int gsm0480_wrap_invoke(struct msgb *msg, int op, int link_id); int gsm0480_wrap_facility(struct msgb *msg); - -struct gsm48_hdr *gsm0480_l3hdr_push(struct msgb *msg, uint8_t proto_discr, - uint8_t msg_type); diff --git a/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h index cfae83da..2b14e6cd 100644 --- a/include/osmocom/gsm/gsm48.h +++ b/include/osmocom/gsm/gsm48.h @@ -4,6 +4,8 @@ #include <stdbool.h> +#include <osmocom/core/msgb.h> + #include <osmocom/gsm/tlv.h> #include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/gsm/gsm48_ie.h> @@ -63,3 +65,9 @@ void gsm48_mcc_mnc_to_bcd(uint8_t *bcd_dst, uint16_t mcc, uint16_t mnc) OSMO_DEPRECATED("Use osmo_plmn_to_bcd() instead, to not lose leading zeros in the MNC"); void gsm48_mcc_mnc_from_bcd(uint8_t *bcd_src, uint16_t *mcc, uint16_t *mnc) OSMO_DEPRECATED("Use osmo_plmn_from_bcd() instead, to not lose leading zeros in the MNC"); + +struct gsm48_hdr *gsm48_push_l3hdr(struct msgb *msg, + uint8_t pdisc, uint8_t msg_type); + +#define gsm48_push_l3hdr_tid(msg, pdisc, tid, msg_type) \ + gsm48_push_l3hdr(msg, (pdisc & 0x0f) | (tid << 4), msg_type) diff --git a/include/osmocom/gsm/ipa.h b/include/osmocom/gsm/ipa.h index 7e1d7237..93cb1bf1 100644 --- a/include/osmocom/gsm/ipa.h +++ b/include/osmocom/gsm/ipa.h @@ -26,11 +26,16 @@ struct ipaccess_unit { /* obtain the human-readable name of an IPA CCM ID TAG */ const char *ipa_ccm_idtag_name(uint8_t tag); -/* parse a buffer of ID tags into a osmocom TLV style representation */ -int ipa_ccm_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len); +int ipa_ccm_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len) + OSMO_DEPRECATED("Use ipa_ccm_id_{get,resp}_parse instead"); +int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len, const int len_offset) + OSMO_DEPRECATED("Use ipa_ccm_id_{get,resp}_parse instead"); -/* Is the TAG included in the length field? */ -int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len, const int len_offset); +/* parse payload of IPA CCM ID GET into a osmocom TLV style representation */ +int ipa_ccm_id_get_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned int len); + +/* parse payload of IPA CCM ID RESP into a osmocom TLV style representation */ +int ipa_ccm_id_resp_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned int len); /* parse an Unit ID in string format into the 'ipaccess_unit' data structure */ int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data); diff --git a/include/osmocom/gsm/mncc.h b/include/osmocom/gsm/mncc.h index 6b94d469..7e7d12c3 100644 --- a/include/osmocom/gsm/mncc.h +++ b/include/osmocom/gsm/mncc.h @@ -90,7 +90,7 @@ void _osmo_mncc_log(int subsys, int level, const char *file, int line, const cha const uint8_t *msg, unsigned int len); #define osmo_mncc_log(ss, level, prefix, msg, len) \ - _osmo_mncc_log(ss, level, __BASE_FILE__, __LINE__, prefix, msg, len); + _osmo_mncc_log(ss, level, __FILE__, __LINE__, prefix, msg, len); extern const struct value_string osmo_mncc_names[]; static inline const char *osmo_mncc_name(uint32_t msg_type) { diff --git a/include/osmocom/gsm/oap_client.h b/include/osmocom/gsm/oap_client.h new file mode 100644 index 00000000..763f982c --- /dev/null +++ b/include/osmocom/gsm/oap_client.h @@ -0,0 +1,82 @@ +/* Osmocom Authentication Protocol API */ + +/* (C) 2015 by Sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Neels Hofmeyr + * + * 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/>. + * + */ + +#pragma once + +#include <stdint.h> + +struct msgb; +struct osmo_oap_message; + +/* This is the config part for vty. It is essentially copied in + * oap_client_state, where values are copied over once the config is + * considered valid. */ +struct osmo_oap_client_config { + uint16_t client_id; + int secret_k_present; + uint8_t secret_k[16]; + int secret_opc_present; + uint8_t secret_opc[16]; +}; + +/* The runtime state of the OAP client. client_id and the secrets are in fact + * duplicated from oap_client_config, so that a separate validation of the + * config data is possible, and so that only a struct oap_client_state* is + * passed around. */ +struct osmo_oap_client_state { + enum { + OSMO_OAP_UNINITIALIZED = 0, /* just allocated. */ + OSMO_OAP_DISABLED, /* disabled by config. */ + OSMO_OAP_INITIALIZED, /* enabled, config is valid. */ + OSMO_OAP_REQUESTED_CHALLENGE, + OSMO_OAP_SENT_CHALLENGE_RESULT, + OSMO_OAP_REGISTERED + } state; + uint16_t client_id; + uint8_t secret_k[16]; + uint8_t secret_opc[16]; + int registration_failures; +}; + +/* From config, initialize state. Return 0 on success. */ +int osmo_oap_client_init(struct osmo_oap_client_config *config, + struct osmo_oap_client_state *state); + +/* Construct an OAP registration message and return in *msg_tx. Use + * state->client_id and update state->state. + * Return 0 on success, or a negative value on error. + * If an error is returned, *msg_tx is guaranteed to be NULL. */ +int osmo_oap_client_register(struct osmo_oap_client_state *state, struct msgb **msg_tx); + +/* Decode and act on a received OAP message msg_rx. Update state->state. If a + * non-NULL pointer is returned in *msg_tx, that msgb should be sent to the OAP + * server (and freed) by the caller. The received msg_rx is not freed. + * Return 0 on success, or a negative value on error. + * If an error is returned, *msg_tx is guaranteed to be NULL. */ +int osmo_oap_client_handle(struct osmo_oap_client_state *state, + const struct msgb *msg_rx, struct msgb **msg_tx); + +/* Allocate a msgb and in it, return the encoded oap_client_msg. Return + * NULL on error. (Like oap_client_encode(), but also allocates a msgb.) + * About the name: the idea is do_something(oap_client_encoded(my_struct)) + */ +struct msgb *osmo_oap_client_encoded(const struct osmo_oap_message *oap_client_msg); diff --git a/osmo-release.sh b/osmo-release.sh index 86b41d89..4d4d080b 100755 --- a/osmo-release.sh +++ b/osmo-release.sh @@ -4,11 +4,23 @@ REL=$2 if [ "z$REL" = "z" ]; then echo "No REL value specified, defaulting to 'patch' release" - REL=patch + REL="patch" fi -BUMPVER=`command -v bumpversion` +ALLOW_NO_LIBVERSION_CHANGE="${ALLOW_NO_LIBVERSION_CHANGE:-0}" +ALLOW_NO_LIBVERSION_DEB_MATCH="${ALLOW_NO_LIBVERSION_DEB_MATCH:-0}" + +libversion_to_deb_major() { + libversion="$1" + current="$(echo "$libversion" | cut -d ":" -f 1)" + #revision="$(echo "$libversion" | cut -d ":" -f 2)" + age="$(echo "$libversion" | cut -d ":" -f 3)" + major="$(expr "$current" - "$age")" + echo "$major" +} +BUMPVER=`command -v bumpversion` +GIT_TOPDIR="$(git rev-parse --show-toplevel)" NEW_VER=`bumpversion --list --current-version $VERSION $REL --allow-dirty | awk -F '=' '{ print $2 }'` LIBVERS=`git grep -n LIBVERSION | grep '=' | grep am | grep -v LDFLAGS` MAKEMOD=`git diff --cached -GLIBVERSION --stat | grep Makefile.am` @@ -27,12 +39,40 @@ fi echo "Releasing $VERSION -> $NEW_VER..." if [ "z$LIBVERS" != "z" ]; then - if [ "z$MAKEMOD" = "z" ]; then - echo "Before releasing, please modify some of the libversions: $LIBVERS" + if [ "z$MAKEMOD" = "z" ] && [ "z$ALLOW_NO_LIBVERSION_CHANGE" = "z0" ]; then + echo "ERROR: Before releasing, please modify some of the libversions: $LIBVERS" echo "You should NOT be doing this unless you've read and understood following article:" echo "https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info" exit 1 fi + if [ "z$ALLOW_NO_LIBVERSION_DEB_MATCH" = "z0" ]; then + echo "$LIBVERS" | while read -r line; do + libversion=$(echo "$line" | cut -d "=" -f 2) + major="$(libversion_to_deb_major "$libversion")" + file_matches="$(find "${GIT_TOPDIR}/debian" -name "lib*${major}.install" | wc -l)" + if [ "z$file_matches" = "z0" ]; then + echo "ERROR: Found no matching debian/lib*$major.install file for LIBVERSION=$libversion" + exit 1 + elif [ "z$file_matches" = "z1" ]; then + echo "OK: Found matching debian/lib*$major.install for LIBVERSION=$libversion" + else + echo "WARN: Found $file_matches files matching debian/lib*$major.install for LIBVERSION=$libversion, manual check required!" + fi + control_matches="$(grep -e "Package" "${GIT_TOPDIR}/debian/control" | grep "lib" | grep "$major$" | wc -l)" + if [ "z$control_matches" = "z0" ]; then + echo "ERROR: Found no matching Package lib*$major in debian/control for LIBVERSION=$libversion" + exit 1 + elif [ "z$control_matches" = "z1" ]; then + echo "OK: Found 'Package: lib*$major' in debian/control for LIBVERSION=$libversion" + else + echo "WARN: Found $file_matches files matching 'Package: lib*$major' in debian/control for LIBVERSION=$libversion, manual check required!" + fi + done + # catch and forward exit from pipe subshell "while read": + if [ $? -ne 0 ]; then + exit 1 + fi + fi if [ -f "TODO-RELEASE" ]; then grep '#' TODO-RELEASE > TODO-RELEASE.clean mv TODO-RELEASE.clean TODO-RELEASE diff --git a/src/Makefile.am b/src/Makefile.am index 45fb89df..e9db32fd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ # This is _NOT_ the library release version, it's an API version. # Please read chapter "Library interface versions" of the libtool documentation # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html -LIBVERSION=10:0:0 +LIBVERSION=11:0:0 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include AM_CFLAGS = -Wall $(TALLOC_CFLAGS) diff --git a/src/codec/Makefile.am b/src/codec/Makefile.am index 60fce5e6..b522d43a 100644 --- a/src/codec/Makefile.am +++ b/src/codec/Makefile.am @@ -1,7 +1,7 @@ # This is _NOT_ the library release version, it's an API version. # Please read chapter "Library interface versions" of the libtool documentation # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html -LIBVERSION=1:0:1 +LIBVERSION=1:1:1 AM_CPPFLAGS = -I$(top_srcdir)/include $(TALLOC_CFLAGS) AM_CFLAGS = -Wall diff --git a/src/coding/Makefile.am b/src/coding/Makefile.am index a17e3d14..c001c139 100644 --- a/src/coding/Makefile.am +++ b/src/coding/Makefile.am @@ -1,7 +1,7 @@ # This is _NOT_ the library release version, it's an API version. # Please read Chapter 6 "Library interface versions" of the libtool # documentation before making any modification -LIBVERSION = 1:0:1 +LIBVERSION = 1:1:1 AM_CPPFLAGS = \ -I"$(top_srcdir)/include" \ diff --git a/src/ctrl/Makefile.am b/src/ctrl/Makefile.am index a51ae053..fe7c47dd 100644 --- a/src/ctrl/Makefile.am +++ b/src/ctrl/Makefile.am @@ -1,7 +1,7 @@ # This is _NOT_ the library release version, it's an API version. # Please read chapter "Library interface versions" of the libtool documentation # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html -LIBVERSION=2:0:2 +LIBVERSION=3:0:3 AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS) diff --git a/src/ctrl/fsm_ctrl_commands.c b/src/ctrl/fsm_ctrl_commands.c index 3c7e1bd0..c93d3a52 100644 --- a/src/ctrl/fsm_ctrl_commands.c +++ b/src/ctrl/fsm_ctrl_commands.c @@ -156,7 +156,7 @@ static int get_fsm_inst_dump(struct ctrl_cmd *cmd, void *data) if (fi->proc.parent) cmd->reply = talloc_asprintf_append(cmd->reply, ",parent='%s'", fi->proc.parent->name); - llist_for_each_entry(child, &fi->proc.children, list) { + llist_for_each_entry(child, &fi->proc.children, proc.child) { cmd->reply = talloc_asprintf_append(cmd->reply, ",child='%s'", child->name); } diff --git a/src/gb/Makefile.am b/src/gb/Makefile.am index 70a451d2..03052fa8 100644 --- a/src/gb/Makefile.am +++ b/src/gb/Makefile.am @@ -1,6 +1,6 @@ # This is _NOT_ the library release version, it's an API version. # Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification -LIBVERSION=6:0:0 +LIBVERSION=7:0:1 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN} -fno-strict-aliasing $(TALLOC_CFLAGS) @@ -23,4 +23,3 @@ libosmogb_la_SOURCES = gprs_ns.c gprs_ns_frgre.c gprs_ns_vty.c \ endif EXTRA_DIST = libosmogb.map - diff --git a/src/gb/gprs_bssgp.c b/src/gb/gprs_bssgp.c index 5dfce16c..3b9fbf95 100644 --- a/src/gb/gprs_bssgp.c +++ b/src/gb/gprs_bssgp.c @@ -1263,3 +1263,31 @@ void bssgp_set_log_ss(int ss) { DBSSGP = ss; } + +/*! + * \brief Flush the queue of the bssgp_flow_control + * \param[in] The flow control object which holds the queue. + */ +void bssgp_fc_flush_queue(struct bssgp_flow_control *fc) +{ + struct bssgp_fc_queue_element *element, *tmp; + + llist_for_each_entry_safe(element, tmp, &fc->queue, list) { + msgb_free(element->msg); + llist_del(&element->list); + talloc_free(element); + } +} + +/*! + * \brief Flush the queues of all BSSGP contexts. + */ +void bssgp_flush_all_queues() +{ + struct bssgp_bvc_ctx *bctx; + + llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) { + if (bctx->fc) + bssgp_fc_flush_queue(bctx->fc); + } +} diff --git a/src/gb/gprs_ns.c b/src/gb/gprs_ns.c index 75c31415..9b7cc056 100644 --- a/src/gb/gprs_ns.c +++ b/src/gb/gprs_ns.c @@ -87,8 +87,8 @@ #include "common_vty.h" -#define ns_set_state(ns_, st_) ns_set_state_with_log(ns_, st_, false, __BASE_FILE__, __LINE__) -#define ns_set_remote_state(ns_, st_) ns_set_state_with_log(ns_, st_, true, __BASE_FILE__, __LINE__) +#define ns_set_state(ns_, st_) ns_set_state_with_log(ns_, st_, false, __FILE__, __LINE__) +#define ns_set_remote_state(ns_, st_) ns_set_state_with_log(ns_, st_, true, __FILE__, __LINE__) #define ns_mark_blocked(ns_) ns_set_state(ns_, (ns_)->state | NSE_S_BLOCKED) #define ns_mark_unblocked(ns_) ns_set_state(ns_, (ns_)->state & (~NSE_S_BLOCKED)); diff --git a/src/gb/libosmogb.map b/src/gb/libosmogb.map index d56e6514..ec69670e 100644 --- a/src/gb/libosmogb.map +++ b/src/gb/libosmogb.map @@ -6,6 +6,8 @@ bssgp_pdu_str; bssgp_fc_in; bssgp_fc_init; bssgp_fc_ms_init; +bssgp_fc_flush_queue; +bssgp_flush_all_queues; bssgp_msgb_alloc; bssgp_msgb_copy; bssgp_msgb_tlli_put; diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am index 5387e3ab..29299a64 100644 --- a/src/gsm/Makefile.am +++ b/src/gsm/Makefile.am @@ -1,7 +1,7 @@ # This is _NOT_ the library release version, it's an API version. # Please read chapter "Library interface versions" of the libtool documentation # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html -LIBVERSION=9:0:0 +LIBVERSION=10:0:0 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS) AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN} @@ -30,7 +30,7 @@ libgsmint_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \ milenage/aes-internal.c milenage/aes-internal-enc.c \ milenage/milenage.c gan.c ipa.c gsm0341.c apn.c \ gsup.c gprs_gea.c gsm0503_conv.c oap.c gsm0808_utils.c \ - gsm23003.c mncc.c bts_features.c + gsm23003.c mncc.c bts_features.c oap_client.c libgsmint_la_LDFLAGS = -no-undefined libgsmint_la_LIBADD = $(top_builddir)/src/libosmocore.la diff --git a/src/gsm/comp128v23.c b/src/gsm/comp128v23.c index 68f4b2a3..550f6a49 100644 --- a/src/gsm/comp128v23.c +++ b/src/gsm/comp128v23.c @@ -1,8 +1,10 @@ /*! \file comp128v23.c * COMP128 version 2 and 3 implementation, common algorithm used for GSM Authentication (A3/A8). * - * This code is a C conversion of the original code from - * http://www.hackingprojects.net/ + * This code is a C conversion of the original code by Tamas Jos <info@skelsec.com> from: + * - original (out of service): http://www.hackingprojects.net/ + * - original (archive): https://web.archive.org/web/20130730113347/http://www.hackingprojects.net/ + * - new site: https://github.com/skelsec/COMP128 */ /* * (C) 2013 by Kévin Redon <kevredon@mail.tsaitgaist.info> diff --git a/src/gsm/gsm0411_utils.c b/src/gsm/gsm0411_utils.c index 53d37a43..ccefe546 100644 --- a/src/gsm/gsm0411_utils.c +++ b/src/gsm/gsm0411_utils.c @@ -35,7 +35,6 @@ #include <osmocom/core/logging.h> #include <osmocom/gsm/gsm48.h> -#include <osmocom/gsm/gsm0480.h> #include <osmocom/gsm/gsm_utils.h> #include <osmocom/gsm/protocol/gsm_03_40.h> #include <osmocom/gsm/protocol/gsm_04_11.h> @@ -354,7 +353,7 @@ int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans, uint8_t msg_type) { /* Outgoing proto_discr needs the highest bit set */ - gsm0480_l3hdr_push(msg, proto | (trans << 4), msg_type); + gsm48_push_l3hdr_tid(msg, proto, trans, msg_type); return 0; } diff --git a/src/gsm/gsm0480.c b/src/gsm/gsm0480.c index 165b309f..7756ecba 100644 --- a/src/gsm/gsm0480.c +++ b/src/gsm/gsm0480.c @@ -25,6 +25,7 @@ * */ +#include <osmocom/gsm/gsm48.h> #include <osmocom/gsm/gsm0480.h> #include <osmocom/gsm/gsm_utils.h> @@ -87,6 +88,15 @@ static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag, return data; } +static inline unsigned char *msgb_push_NULL(struct msgb *msgb) +{ + uint8_t *data = msgb_push(msgb, 2); + + data[0] = ASN1_NULL_TYPE_TAG; + data[1] = 0; + return data; +} + /* wrap an invoke around it... the other way around * * 1.) Invoke Component tag @@ -122,7 +132,7 @@ struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char * uint8_t *seq_len_ptr, *ussd_len_ptr, *data; int len; - msg = msgb_alloc_headroom(1024, 128, "GSM 04.80"); + msg = gsm0480_msgb_alloc_name("TS 04.80 USSD Notify"); if (!msg) return NULL; @@ -168,7 +178,7 @@ struct msgb *gsm0480_create_notifySS(const char *text) if (len < 1 || len > 160) return NULL; - msg = msgb_alloc_headroom(1024, 128, "GSM 04.80"); + msg = gsm0480_msgb_alloc_name("TS 04.80 NotifySS"); if (!msg) return NULL; @@ -787,13 +797,22 @@ static int parse_ss_for_bs_req(const uint8_t *ss_req_data, return rc; } -struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text) +struct msgb *gsm0480_msgb_alloc_name(const char *name) +{ + return msgb_alloc_headroom(1024, 128, name); +} + +/*! Generate a USSD ReturnResult component containing a string in default GSM alphabet. + * \param[in] invoke_id InvokeID of the request to which we respond + * \param[in] text USSD text in ASCII; to be encoded as GSM 7-but alphabet + */ +struct msgb *gsm0480_gen_ussd_resp_7bit(uint8_t invoke_id, const char *text) { struct msgb *msg; uint8_t *ptr8; int response_len; - msg = msgb_alloc_headroom(1024, 128, "GSM 04.80"); + msg = gsm0480_msgb_alloc_name("TS 04.80 USSD Resp"); if (!msg) return NULL; @@ -824,25 +843,95 @@ struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const /* Wrap this up as a Return Result component */ msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT); + return msg; +} + +/*! Legacy helper: Generate USSD response including FACILITY IE + L3 header. + * + * This function is just like \ref gsm0480_gen_ussd_resp_7bit, but it generates + * not only the FACILITY value, but the full L3 message including message header + * and FACILITY IE Tag+Length. + */ +struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text) +{ + struct msgb *msg; + + msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text); + if (!msg) + return NULL; + /* Wrap the component in a Facility message */ msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); /* And finally pre-pend the L3 header */ - gsm0480_l3hdr_push(msg, - GSM48_PDISC_NC_SS | trans_id - | (1<<7) /* TI direction = 1 */, - GSM0480_MTYPE_RELEASE_COMPLETE); + gsm48_push_l3hdr_tid(msg, GSM48_PDISC_NC_SS, + /* FIXME: TI direction is always 1 ?!? */ + trans_id | (1 << 7), + GSM0480_MTYPE_RELEASE_COMPLETE); + + return msg; +} + +/*! Generate a ReturnError component (see section 3.6.1) and given error code (see section 3.6.6). + * \param[in] invoke_id InvokeID of the request + * \param[in] error_code Error code (section 4.5) + * \return message buffer containing the Reject component + * + * Note: if InvokeID is not available, e.g. when message parsing failed, any incorrect vlue + * can be passed (0x00 > x > 0xff), so the universal NULL-tag (see table 3.6) will be used instead. + */ +struct msgb *gsm0480_gen_return_error(uint8_t invoke_id, uint8_t error_code) +{ + struct msgb *msg; + + msg = gsm0480_msgb_alloc_name("TS 04.80 ReturnError"); + if (!msg) + return NULL; + + /* First insert the problem code */ + msgb_push_TLV1(msg, GSM_0480_ERROR_CODE_TAG, error_code); + + /* Before it, insert the invoke ID */ + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id); + + /* Wrap this up as a Reject component */ + msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_ERROR); + + /* FIXME: Wrap in Facility + L3? */ return msg; } -struct gsm48_hdr *gsm0480_l3hdr_push(struct msgb *msg, uint8_t proto_discr, - uint8_t msg_type) +/*! Generate a Reject component (see section 3.6.1) and given error code (see section 3.6.7). + * \param[in] invoke_id InvokeID of the request + * \param[in] problem_tag Problem code tag (table 3.13) + * \param[in] problem_code Problem code (table 3.14-3.17) + * \return message buffer containing the Reject component + * + * Note: if InvokeID is not available, e.g. when message parsing failed, any incorrect vlue + * can be passed (0x00 > x > 0xff), so the universal NULL-tag (see table 3.6) will be used instead. + */ +struct msgb *gsm0480_gen_reject(int invoke_id, uint8_t problem_tag, uint8_t problem_code) { - struct gsm48_hdr *gh; - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - gh->proto_discr = proto_discr; - gh->msg_type = msg_type; - return gh; + struct msgb *msg; + + msg = gsm0480_msgb_alloc_name("TS 04.80 Reject"); + if (!msg) + return NULL; + + /* First insert the problem code */ + msgb_push_TLV1(msg, problem_tag, problem_code); + + /* If the Invoke ID is not available, Universal NULL (table 3.9) with length=0 shall be used */ + if (invoke_id < 0 || invoke_id > 255) + msgb_push_NULL(msg); + else + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id); + + /* Wrap this up as a Reject component */ + msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT); + + /* FIXME: Wrap in Facility + L3? */ + return msg; } struct msgb *gsm0480_create_ussd_notify(int level, const char *text) @@ -856,7 +945,11 @@ struct msgb *gsm0480_create_ussd_notify(int level, const char *text) gsm0480_wrap_invoke(msg, GSM0480_OP_CODE_USS_NOTIFY, 0); gsm0480_wrap_facility(msg); - gsm0480_l3hdr_push(msg, GSM48_PDISC_NC_SS, GSM0480_MTYPE_REGISTER); + /* And finally pre-pend the L3 header */ + gsm48_push_l3hdr(msg, GSM48_PDISC_NC_SS, + /* FIXME: no transactionID?!? */ + GSM0480_MTYPE_REGISTER); + return msg; } @@ -864,12 +957,14 @@ struct msgb *gsm0480_create_ussd_release_complete(void) { struct msgb *msg; - msg = msgb_alloc_headroom(1024, 128, "GSM 04.80 USSD REL COMPL"); + msg = gsm0480_msgb_alloc_name("TS 04.80 USSD REL COMPL"); if (!msg) return NULL; - /* FIXME: should this set trans_id and TI direction flag? */ - gsm0480_l3hdr_push(msg, GSM48_PDISC_NC_SS, - GSM0480_MTYPE_RELEASE_COMPLETE); + /* And finally pre-pend the L3 header */ + gsm48_push_l3hdr(msg, GSM48_PDISC_NC_SS, + /* FIXME: no transactionID?!? */ + GSM0480_MTYPE_RELEASE_COMPLETE); + return msg; } diff --git a/src/gsm/gsm0808.c b/src/gsm/gsm0808.c index a90aa227..e3b10d0c 100644 --- a/src/gsm/gsm0808.c +++ b/src/gsm/gsm0808.c @@ -588,7 +588,7 @@ struct msgb *gsm0808_create_clear_rqst(uint8_t cause) /*! Create BSSMAP PAGING message * \param[in] imsi Mandatory paged IMSI in string representation * \param[in] tmsi Optional paged TMSI - * \param[in] cil Cell Identity List (where to page) + * \param[in] cil Mandatory Cell Identity List (where to page) * \param[in] chan_needed Channel Type needed * \returns callee-allocated msgb with BSSMAP PAGING message */ struct msgb *gsm0808_create_paging2(const char *imsi, const uint32_t *tmsi, @@ -615,7 +615,7 @@ struct msgb *gsm0808_create_paging2(const char *imsi, const uint32_t *tmsi, /* Message Type 3.2.2.1 */ msgb_v_put(msg, BSS_MAP_MSG_PAGING); - /* IMSI 3.2.2.6 */ + /* mandatory IMSI 3.2.2.6 */ mid_len = gsm48_generate_mid_from_imsi(mid_buf, imsi); msgb_tlv_put(msg, GSM0808_IE_IMSI, mid_len - 2, mid_buf + 2); @@ -626,9 +626,8 @@ struct msgb *gsm0808_create_paging2(const char *imsi, const uint32_t *tmsi, (uint8_t *) & tmsi_sw); } - /* Cell Identifier List 3.2.2.27 */ - if (cil) - gsm0808_enc_cell_id_list2(msg, cil); + /* mandatory Cell Identifier List 3.2.2.27 */ + gsm0808_enc_cell_id_list2(msg, cil); /* Channel Needed 3.2.2.36 */ if (chan_needed) { @@ -763,6 +762,9 @@ struct msgb *gsm0808_create_handover_request_ack(const uint8_t *l3_info, uint8_t if (chosen_speech_version != 0) msgb_tv_put(msg, GSM0808_IE_SPEECH_VERSION, chosen_speech_version); + /* prepend header with final length */ + msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); + return msg; } @@ -780,6 +782,9 @@ struct msgb *gsm0808_create_handover_detect() /* Message Type, 3.2.2.1 */ msgb_v_put(msg, BSS_MAP_MSG_HANDOVER_DETECT); + /* prepend header with final length */ + msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); + return msg; } @@ -816,6 +821,9 @@ struct msgb *gsm0808_create_handover_complete(const struct gsm0808_handover_comp if (params->lcls_bss_status_present) msgb_tv_put(msg, GSM0808_IE_LCLS_BSS_STATUS, params->lcls_bss_status); + /* prepend header with final length */ + msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); + return msg; } @@ -843,6 +851,9 @@ struct msgb *gsm0808_create_handover_failure(const struct gsm0808_handover_failu if (params->codec_list_bss_supported.len) gsm0808_enc_speech_codec_list(msg, ¶ms->codec_list_bss_supported); + /* prepend header with final length */ + msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); + return msg; } diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c index b4892dea..136b9375 100644 --- a/src/gsm/gsm48.c +++ b/src/gsm/gsm48.c @@ -1024,4 +1024,24 @@ const struct value_string gsm48_reject_value_names[] = { { 0, NULL } }; +/*! Wrap a given \ref msg with \ref gsm48_hdr structure + * \param[out] msg A message to be wrapped + * \param[in] pdisc GSM TS 04.07 protocol discriminator 1/2, + * sub-pdisc, trans_id or skip_ind 1/2, + * see section 11.2.3.1 for details + * \param[in] msg_type GSM TS 04.08 message type + * @return pointer to pushed header within \ref msg + */ +struct gsm48_hdr *gsm48_push_l3hdr(struct msgb *msg, + uint8_t pdisc, uint8_t msg_type) +{ + struct gsm48_hdr *gh; + + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + gh->proto_discr = pdisc; + gh->msg_type = msg_type; + + return gh; +} + /*! @} */ diff --git a/src/gsm/ipa.c b/src/gsm/ipa.c index 0c7aaad6..d423c262 100644 --- a/src/gsm/ipa.c +++ b/src/gsm/ipa.c @@ -137,6 +137,83 @@ int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len, return 0; } +/*! Parse the payload part of an IPA CCM ID GET, return \ref tlv_parsed format. + * The odd payload format of those messages is structured as follows: + * * 8bit length value (length of payload *and tag*) + * * 8bit tag value + * * optional, variable-length payload + * \param[out] dec Caller-provided/allocated output structure for parsed payload + * \param[in] buf Buffer containing the payload (excluding 1 byte msg_type) of the message + * \param[in] len Length of \a buf in octets + * \returns 0 on success; negative on error */ +int ipa_ccm_id_get_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned int len) +{ + uint8_t t_len; + uint8_t t_tag; + const uint8_t *cur = buf; + + memset(dec, 0, sizeof(*dec)); + + while (len >= 2) { + len -= 2; + t_len = *cur++; + t_tag = *cur++; + + if (t_len > len + 1) { + LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1); + return -EINVAL; + } + + DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur); + + dec->lv[t_tag].len = t_len-1; + dec->lv[t_tag].val = cur; + + cur += t_len-1; + len -= t_len-1; + } + return 0; +} + +/*! Parse the payload part of an IPA CCM ID RESP, return \ref tlv_parsed format. + * The odd payload format of those messages is structured as follows: + * * 16bit length value (length of payload *and tag*) + * * 8bit tag value + * * optional, variable-length payload + * \param[out] dec Caller-provided/allocated output structure for parsed payload + * \param[in] buf Buffer containing the payload (excluding 1 byte msg_type) of the message + * \param[in] len Length of \a buf in octets + * \returns 0 on success; negative on error */ +int ipa_ccm_id_resp_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned int len) +{ + uint8_t t_len; + uint8_t t_tag; + const uint8_t *cur = buf; + + memset(dec, 0, sizeof(*dec)); + + while (len >= 3) { + len -= 3; + t_len = *cur++ << 8; + t_len += *cur++; + t_tag = *cur++; + + if (t_len > len + 1) { + LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1); + return -EINVAL; + } + + DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur); + + dec->lv[t_tag].len = t_len-1; + dec->lv[t_tag].val = cur; + + cur += t_len-1; + len -= t_len-1; + } + return 0; +} + int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data) { unsigned long ul; @@ -251,23 +328,23 @@ struct msgb *ipa_ccm_make_id_resp(const struct ipaccess_unit *dev, break; case IPAC_IDTAG_LOCATION1: if (dev->location1) - strncpy(str, dev->location1, IPA_STRING_MAX); + osmo_strlcpy(str, dev->location1, sizeof(str)); break; case IPAC_IDTAG_LOCATION2: if (dev->location2) - strncpy(str, dev->location2, IPA_STRING_MAX); + osmo_strlcpy(str, dev->location2, sizeof(str)); break; case IPAC_IDTAG_EQUIPVERS: if (dev->equipvers) - strncpy(str, dev->equipvers, IPA_STRING_MAX); + osmo_strlcpy(str, dev->equipvers, sizeof(str)); break; case IPAC_IDTAG_SWVERSION: if (dev->swversion) - strncpy(str, dev->swversion, IPA_STRING_MAX); + osmo_strlcpy(str, dev->swversion, sizeof(str)); break; case IPAC_IDTAG_UNITNAME: if (dev->unit_name) { - snprintf(str, sizeof(str), dev->unit_name, IPA_STRING_MAX); + snprintf(str, sizeof(str), "%s", dev->unit_name); } else { snprintf(str, sizeof(str), "%02x-%02x-%02x-%02x-%02x-%02x", @@ -278,7 +355,7 @@ struct msgb *ipa_ccm_make_id_resp(const struct ipaccess_unit *dev, break; case IPAC_IDTAG_SERNR: if (dev->serno) - strncpy(str, dev->serno, IPA_STRING_MAX); + osmo_strlcpy(str, dev->serno, sizeof(str)); break; default: LOGP(DLINP, LOGL_NOTICE, @@ -286,7 +363,6 @@ struct msgb *ipa_ccm_make_id_resp(const struct ipaccess_unit *dev, msgb_free(msg); return NULL; } - str[IPA_STRING_MAX-1] = '\0'; LOGP(DLINP, LOGL_INFO, " tag %d: %s\n", ies_req[i], str); tag = msgb_put(msg, 3 + strlen(str) + 1); @@ -452,6 +528,9 @@ void ipa_prepend_header(struct msgb *msg, int proto) #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> +/*! Read one ipa message from socket fd without caching not fully received + * messages. See \ref ipa_msg_recv_buffered for further information. + */ int ipa_msg_recv(int fd, struct msgb **rmsg) { int rc = ipa_msg_recv_buffered(fd, rmsg, NULL); @@ -462,6 +541,25 @@ int ipa_msg_recv(int fd, struct msgb **rmsg) return rc; } +/*! Read one ipa message from socket fd or store part if still not fully received. + * \param[in] fd The fd for the socket to read from. + * \param[out] rmsg internally allocated msgb containing a fully received ipa message. + * \param[inout] tmp_msg internally allocated msgb caching data for not yet fully received message. + * + * As ipa can run on top of stream based protocols such as TCP, there's the + * possibility that such lower layers split ipa messages in several low level + * packets. If a low layer packet is received containing several ipa frames, + * this function will pull from the socket and return only the first one + * available in the stream. As the socket will remain with data, it will + * trigger again during next select() and then this function will fetch the + * next ipa message, and so on. + * + * \returns -EAGAIN and allocated tmp_msg if message was not yet fully + * received. Other negative values indicate an error and cached msgb will be + * freed. 0 if socket is found dead. Positive value indicating l2 msgb len and + * rmsg pointing to internally allocated msgb containing the ipa frame on + * scucess. + */ int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg) { struct msgb *msg = tmp_msg ? *tmp_msg : NULL; diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map index 3b403c2b..1da398c1 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -97,6 +97,10 @@ gsm0480_wrap_facility; gsm0480_wrap_invoke; gsm0480_comp_type_names; gsm0480_op_code_names; +gsm0480_msgb_alloc_name; +gsm0480_gen_ussd_resp_7bit; +gsm0480_gen_return_error; +gsm0480_gen_reject; gsm0502_calc_paging_group; @@ -237,6 +241,7 @@ gsm411_rp_state_names; gsm414_msgt_names; +gsm48_push_l3hdr; gsm48_att_tlvdef; gsm48_cc_msg_name; gsm48_rr_msg_name; @@ -458,6 +463,8 @@ ipa_ccm_tlv_to_unitdata; ipa_ccm_idtag_name; ipa_ccm_idtag_parse; ipa_ccm_idtag_parse_off; +ipa_ccm_id_get_parse; +ipa_ccm_id_resp_parse; ipa_ccm_make_id_resp; ipa_ccm_make_id_resp_from_req; ipa_msg_alloc; @@ -489,5 +496,10 @@ osmo_mncc_stringify; osmo_mncc_names; _osmo_mncc_log; +osmo_oap_client_encoded; +osmo_oap_client_handle; +osmo_oap_client_init; +osmo_oap_client_register; + local: *; }; diff --git a/src/gsm/oap_client.c b/src/gsm/oap_client.c new file mode 100644 index 00000000..ea406341 --- /dev/null +++ b/src/gsm/oap_client.c @@ -0,0 +1,280 @@ +/* Osmocom Authentication Protocol API */ + +/* (C) 2015 by Sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Neels Hofmeyr + * + * 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 <string.h> +#include <errno.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> +#include <osmocom/crypt/auth.h> +#include <osmocom/gsm/oap.h> + +#include <osmocom/gsm/oap_client.h> + +int osmo_oap_client_init(struct osmo_oap_client_config *config, + struct osmo_oap_client_state *state) +{ + OSMO_ASSERT(state->state == OSMO_OAP_UNINITIALIZED); + + if (!config) + goto disable; + + if (config->client_id == 0) + goto disable; + + if (config->secret_k_present == 0) { + LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret K missing.\n"); + goto disable; + } + + if (config->secret_opc_present == 0) { + LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret OPC missing.\n"); + goto disable; + } + + state->client_id = config->client_id; + memcpy(state->secret_k, config->secret_k, sizeof(state->secret_k)); + memcpy(state->secret_opc, config->secret_opc, sizeof(state->secret_opc)); + state->state = OSMO_OAP_INITIALIZED; + return 0; + +disable: + state->state = OSMO_OAP_DISABLED; + return 0; +} + +/* From the given state and received RAND and AUTN octets, validate the + * server's authenticity and formulate the matching milenage reply octets in + * *tx_xres. The state is not modified. + * On success, and if tx_res is not NULL, exactly 8 octets will be written to + * *tx_res. If not NULL, tx_res must point at allocated memory of at least 8 + * octets. The caller will want to send XRES back to the server in a challenge + * response message and update the state. + * Return 0 on success; -1 if OAP is disabled; -2 if rx_random and rx_autn fail + * the authentication check; -3 for any other errors. */ +static int oap_evaluate_challenge(const struct osmo_oap_client_state *state, + const uint8_t *rx_random, + const uint8_t *rx_autn, + uint8_t *tx_xres) +{ + struct osmo_auth_vector vec; + + struct osmo_sub_auth_data auth = { + .type = OSMO_AUTH_TYPE_UMTS, + .algo = OSMO_AUTH_ALG_MILENAGE, + }; + + osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.k) + == sizeof(state->secret_k), _secret_k_size_match); + osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.opc) + == sizeof(state->secret_opc), _secret_opc_size_match); + + switch (state->state) { + case OSMO_OAP_UNINITIALIZED: + case OSMO_OAP_DISABLED: + return -1; + default: + break; + } + + memcpy(auth.u.umts.k, state->secret_k, sizeof(auth.u.umts.k)); + memcpy(auth.u.umts.opc, state->secret_opc, sizeof(auth.u.umts.opc)); + memset(auth.u.umts.amf, '\0', sizeof(auth.u.umts.amf)); + auth.u.umts.sqn = 41; /* TODO use incrementing sequence nr */ + + memset(&vec, 0, sizeof(vec)); + osmo_auth_gen_vec(&vec, &auth, rx_random); + + if (vec.res_len != 8) { + LOGP(DLOAP, LOGL_ERROR, "OAP: Expected XRES to be 8 octets, got %d\n", + vec.res_len); + return -3; + } + + if (osmo_constant_time_cmp(vec.autn, rx_autn, sizeof(vec.autn)) != 0) { + LOGP(DLOAP, LOGL_ERROR, "OAP: AUTN mismatch!\n"); + LOGP(DLOAP, LOGL_INFO, "OAP: AUTN from server: %s\n", + osmo_hexdump_nospc(rx_autn, sizeof(vec.autn))); + LOGP(DLOAP, LOGL_INFO, "OAP: AUTN expected: %s\n", + osmo_hexdump_nospc(vec.autn, sizeof(vec.autn))); + return -2; + } + + if (tx_xres != NULL) + memcpy(tx_xres, vec.res, 8); + return 0; +} + +struct msgb *osmo_oap_client_encoded(const struct osmo_oap_message *oap_msg) +{ + struct msgb *msg = msgb_alloc_headroom(1000, 64, __func__); + OSMO_ASSERT(msg); + osmo_oap_encode(msg, oap_msg); + return msg; +} + +/* Create a new msgb containing an OAP registration message. + * On error, return NULL. */ +static struct msgb* oap_msg_register(uint16_t client_id) +{ + struct osmo_oap_message oap_msg = {0}; + + if (client_id < 1) { + LOGP(DLOAP, LOGL_ERROR, "OAP: Invalid client ID: %d\n", client_id); + return NULL; + } + + oap_msg.message_type = OAP_MSGT_REGISTER_REQUEST; + oap_msg.client_id = client_id; + return osmo_oap_client_encoded(&oap_msg); +} + +int osmo_oap_client_register(struct osmo_oap_client_state *state, struct msgb **msg_tx) +{ + *msg_tx = oap_msg_register(state->client_id); + if (!(*msg_tx)) + return -1; + + state->state = OSMO_OAP_REQUESTED_CHALLENGE; + return 0; +} + +/* Create a new msgb containing an OAP challenge response message. + * xres must point at 8 octets to return as challenge response. + * On error, return NULL. */ +static struct msgb* oap_msg_challenge_response(uint8_t *xres) +{ + struct osmo_oap_message oap_reply = {0}; + + oap_reply.message_type = OAP_MSGT_CHALLENGE_RESULT; + memcpy(oap_reply.xres, xres, sizeof(oap_reply.xres)); + oap_reply.xres_present = 1; + return osmo_oap_client_encoded(&oap_reply); +} + +static int handle_challenge(struct osmo_oap_client_state *state, + struct osmo_oap_message *oap_rx, + struct msgb **msg_tx) +{ + int rc; + uint8_t xres[8]; + + if (!(oap_rx->rand_present && oap_rx->autn_present)) { + LOGP(DLOAP, LOGL_ERROR, + "OAP challenge incomplete (rand_present: %d, autn_present: %d)\n", + oap_rx->rand_present, oap_rx->autn_present); + rc = -2; + goto failure; + } + + rc = oap_evaluate_challenge(state, + oap_rx->rand, + oap_rx->autn, + xres); + if (rc < 0) + goto failure; + + *msg_tx = oap_msg_challenge_response(xres); + if ((*msg_tx) == NULL) { + rc = -1; + goto failure; + } + + state->state = OSMO_OAP_SENT_CHALLENGE_RESULT; + return 0; + +failure: + OSMO_ASSERT(rc < 0); + state->state = OSMO_OAP_INITIALIZED; + return rc; +} + +int osmo_oap_client_handle(struct osmo_oap_client_state *state, + const struct msgb *msg_rx, struct msgb **msg_tx) +{ + uint8_t *data = msgb_l2(msg_rx); + size_t data_len = msgb_l2len(msg_rx); + struct osmo_oap_message oap_msg = {0}; + int rc = 0; + + *msg_tx = NULL; + + OSMO_ASSERT(data); + + rc = osmo_oap_decode(&oap_msg, data, data_len); + if (rc < 0) { + LOGP(DLOAP, LOGL_ERROR, + "Decoding OAP message failed with error '%s' (%d)\n", + get_value_string(gsm48_gmm_cause_names, -rc), -rc); + return -10; + } + + switch (state->state) { + case OSMO_OAP_UNINITIALIZED: + LOGP(DLOAP, LOGL_ERROR, + "Received OAP message %d, but the OAP client is" + " not initialized\n", oap_msg.message_type); + return -ENOTCONN; + case OSMO_OAP_DISABLED: + LOGP(DLOAP, LOGL_ERROR, + "Received OAP message %d, but the OAP client is" + " disabled\n", oap_msg.message_type); + return -ENOTCONN; + default: + break; + } + + switch (oap_msg.message_type) { + case OAP_MSGT_CHALLENGE_REQUEST: + return handle_challenge(state, &oap_msg, msg_tx); + + case OAP_MSGT_REGISTER_RESULT: + /* successfully registered */ + state->state = OSMO_OAP_REGISTERED; + break; + + case OAP_MSGT_REGISTER_ERROR: + LOGP(DLOAP, LOGL_ERROR, + "OAP registration failed\n"); + state->state = OSMO_OAP_INITIALIZED; + if (state->registration_failures < 3) { + state->registration_failures++; + return osmo_oap_client_register(state, msg_tx); + } + return -11; + + case OAP_MSGT_REGISTER_REQUEST: + case OAP_MSGT_CHALLENGE_RESULT: + LOGP(DLOAP, LOGL_ERROR, + "Received invalid OAP message type for OAP client side: %d\n", + (int)oap_msg.message_type); + return -12; + + default: + LOGP(DLOAP, LOGL_ERROR, + "Unknown OAP message type: %d\n", + (int)oap_msg.message_type); + return -13; + } + + return 0; +} diff --git a/src/gsmtap_util.c b/src/gsmtap_util.c index b21c690b..385b4672 100644 --- a/src/gsmtap_util.c +++ b/src/gsmtap_util.c @@ -254,7 +254,9 @@ int gsmtap_source_add_sink_fd(int gsmtap_fd) if (osmo_sockaddr_is_local((struct sockaddr *)&ss, ss_len) == 1) { rc = osmo_sock_init_sa((struct sockaddr *)&ss, SOCK_DGRAM, - IPPROTO_UDP, OSMO_SOCK_F_BIND); + IPPROTO_UDP, + OSMO_SOCK_F_BIND | + OSMO_SOCK_F_UDP_REUSEADDR); if (rc >= 0) return rc; } diff --git a/src/logging.c b/src/logging.c index 1dfd4847..de0f2b0f 100644 --- a/src/logging.c +++ b/src/logging.c @@ -406,27 +406,55 @@ static void _output(struct log_target *target, unsigned int subsys, goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); } + + if (target->print_filename_pos == LOG_FILENAME_POS_HEADER_END) { + switch (target->print_filename2) { + case LOG_FILENAME_NONE: + break; + case LOG_FILENAME_PATH: + ret = snprintf(buf + offset, rem, "%s:%d ", file, line); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + break; + case LOG_FILENAME_BASENAME: + ret = snprintf(buf + offset, rem, "%s:%d ", const_basename(file), line); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + break; + } + } + } + ret = vsnprintf(buf + offset, rem, format, ap); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + + /* For LOG_FILENAME_POS_LAST, print the source file info only when the caller ended the log + * message in '\n'. If so, nip the last '\n' away, insert the source file info and re-append an + * '\n'. All this to allow LOGP("start..."); LOGPC("...end\n") constructs. */ + if (target->print_filename_pos == LOG_FILENAME_POS_LINE_END + && offset > 0 && buf[offset-1] == '\n') { switch (target->print_filename2) { case LOG_FILENAME_NONE: break; case LOG_FILENAME_PATH: - ret = snprintf(buf + offset, rem, "%s:%d ", file, line); + offset --; + ret = snprintf(buf + offset, rem, " (%s:%d)\n", file, line); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); break; case LOG_FILENAME_BASENAME: - ret = snprintf(buf + offset, rem, "%s:%d ", const_basename(file), line); + offset --; + ret = snprintf(buf + offset, rem, " (%s:%d)\n", const_basename(file), line); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); break; } } - ret = vsnprintf(buf + offset, rem, format, ap); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); if (target->use_color) { ret = snprintf(buf + offset, rem, "\033[0;m"); @@ -677,6 +705,17 @@ void log_set_print_filename2(struct log_target *target, enum log_filename_type l target->print_filename2 = lft; } +/*! Set the position where on a log line the source file info should be logged. + * \param[in] target Log target to be affected. + * \param[in] pos A LOG_FILENAME_POS_* enum value. + * LOG_FILENAME_POS_DEFAULT logs just before the caller supplied log message. + * LOG_FILENAME_POS_LAST logs only at the end of a log line, where the caller issued an '\n' to end the + */ +void log_set_print_filename_pos(struct log_target *target, enum log_filename_pos pos) +{ + target->print_filename_pos = pos; +} + /*! Enable or disable printing of the category name * \param[in] target Log target to be affected * \param[in] print_catname Enable (1) or disable (0) filenames @@ -759,7 +798,7 @@ struct log_target *log_target_create(void) if (!target) return NULL; - target->categories = talloc_zero_array(target, + target->categories = talloc_zero_array(target, struct log_category, osmo_log_info->num_cat); if (!target->categories) { @@ -932,7 +971,7 @@ const char *log_vty_command_string() { struct log_info *info = osmo_log_info; int len = 0, offset = 0, ret, i, rem; - int size = strlen("logging level () ()") + 1; + int size = strlen("logging level (all|) ()") + 1; char *str; assert_loginfo(__func__); diff --git a/src/signal.c b/src/signal.c index 745d7c38..188876b8 100644 --- a/src/signal.c +++ b/src/signal.c @@ -46,6 +46,15 @@ struct signal_handler { void *data; }; +/*! Initialize a signal_handler talloc context for \ref osmo_signal_register_handler. + * Create a talloc context called "osmo_signal". + * \param[in] root_ctx talloc context used as parent for the new "osmo_signal" ctx. + * \returns the new osmo_signal talloc context, e.g. for reporting + */ +void *osmo_signal_talloc_ctx_init(void *root_ctx) { + tall_sigh_ctx = talloc_named_const(tall_sigh_ctx, 0, "osmo_signal"); + return tall_sigh_ctx; +} /*! Register a new signal handler * \param[in] subsys Subsystem number diff --git a/src/socket.c b/src/socket.c index 04058474..6f56efb5 100644 --- a/src/socket.c +++ b/src/socket.c @@ -209,16 +209,20 @@ int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto, if (sfd < 0) continue; - rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)); - if (rc < 0) { - LOGP(DLGLOBAL, LOGL_ERROR, - "cannot setsockopt socket:" - " %s:%u: %s\n", - local_host, local_port, strerror(errno)); - close(sfd); - continue; + if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) { + rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, + &on, sizeof(on)); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, + "cannot setsockopt socket:" + " %s:%u: %s\n", + local_host, local_port, + strerror(errno)); + close(sfd); + continue; + } } + if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) { LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n", local_host, local_port, strerror(errno)); @@ -345,15 +349,17 @@ int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto, continue; } } else { - rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)); - if (rc < 0) { - LOGP(DLGLOBAL, LOGL_ERROR, - "cannot setsockopt socket:" - " %s:%u: %s\n", - host, port, strerror(errno)); - close(sfd); - continue; + if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) { + rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, + &on, sizeof(on)); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, + "cannot setsockopt socket:" + " %s:%u: %s\n", + host, port, strerror(errno)); + close(sfd); + continue; + } } if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) { LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket:" @@ -373,7 +379,16 @@ int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto, return -ENODEV; } - setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) { + rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, + "cannot setsockopt socket: %s:%u: %s\n", host, + port, strerror(errno)); + close(sfd); + sfd = -1; + } + } rc = osmo_sock_init_tail(sfd, type, flags); if (rc < 0) { @@ -590,23 +605,29 @@ int osmo_sock_unix_init(uint16_t type, uint8_t proto, struct sockaddr_un local; int sfd, rc, on = 1; unsigned int namelen; + const size_t socket_path_len = strlen(socket_path); if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) return -EINVAL; local.sun_family = AF_UNIX; - strncpy(local.sun_path, socket_path, sizeof(local.sun_path)); - local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + if (socket_path_len == sizeof(local.sun_path)) { + /* Handle corner-case where sun_path is not NUL-terminated. See the unix(7) man page. */ + memcpy(local.sun_path, socket_path, sizeof(local.sun_path)); + } else if (osmo_strlcpy(local.sun_path, socket_path, sizeof(local.sun_path)) >= sizeof(local.sun_path)) { + LOGP(DLGLOBAL, LOGL_ERROR, "Socket path exceeds maximum length of %zd bytes: %s\n", + sizeof(local.sun_path), socket_path); + return -ENOSPC; + } #if defined(BSD44SOCKETS) || defined(__UNIXWARE__) - local.sun_len = strlen(local.sun_path); + local.sun_len = socket_path_len; #endif #if defined(BSD44SOCKETS) || defined(SUN_LEN) namelen = SUN_LEN(&local); #else - namelen = strlen(local.sun_path) + - offsetof(struct sockaddr_un, sun_path); + namelen = socket_path_len + offsetof(struct sockaddr_un, sun_path); #endif sfd = socket(AF_UNIX, type, proto); diff --git a/src/stats_statsd.c b/src/stats_statsd.c index c11c0132..5ae25702 100644 --- a/src/stats_statsd.c +++ b/src/stats_statsd.c @@ -68,6 +68,25 @@ struct osmo_stats_reporter *osmo_stats_reporter_create_statsd(const char *name) return srep; } +/*! Replace all illegal ':' in the stats name, but not when used as value seperator. + * ':' is used as seperator between the name and the value in the statsd protocol. + * \param[inout] buf is a null terminated string containing name, value, unit. */ +static void osmo_stats_reporter_sanitize_name(char *buf) +{ + /* e.g. msc.loc_update_type:normal:1|c -> msc.loc_update_type.normal:1|c + * last is the seperator between name and value */ + char *last = strrchr(buf, ':'); + char *tmp = strchr(buf, ':'); + + if (!last) + return; + + while (tmp < last) { + *tmp = '.'; + tmp = strchr(buf, ':'); + } +} + static int osmo_stats_reporter_statsd_send(struct osmo_stats_reporter *srep, const char *name1, unsigned int index1, const char *name2, int64_t value, const char *unit) @@ -134,8 +153,10 @@ static int osmo_stats_reporter_statsd_send(struct osmo_stats_reporter *srep, return -EMSGSIZE; } - if (nchars > 0) + if (nchars > 0) { + osmo_stats_reporter_sanitize_name(buf); msgb_trim(srep->buffer, msgb_length(srep->buffer) + nchars); + } if (!srep->agg_enabled) rc = osmo_stats_reporter_send_buffer(srep); diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am index 18d3a842..49813c51 100644 --- a/src/vty/Makefile.am +++ b/src/vty/Makefile.am @@ -1,7 +1,7 @@ # This is _NOT_ the library release version, it's an API version. # Please read chapter "Library interface versions" of the libtool documentation # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html -LIBVERSION=4:1:0 +LIBVERSION=5:0:1 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include AM_CFLAGS = -Wall $(TALLOC_CFLAGS) diff --git a/src/vty/fsm_vty.c b/src/vty/fsm_vty.c index 8628d159..9bde241c 100644 --- a/src/vty/fsm_vty.c +++ b/src/vty/fsm_vty.c @@ -102,7 +102,7 @@ void vty_out_fsm_inst(struct vty *vty, struct osmo_fsm_inst *fsmi) fsmi->proc.parent_term_event), VTY_NEWLINE); } - llist_for_each_entry(child, &fsmi->proc.children, list) { + llist_for_each_entry(child, &fsmi->proc.children, proc.child) { vty_out(vty, " Child: '%s'%s", child->name, VTY_NEWLINE); } } diff --git a/src/vty/logging_vty.c b/src/vty/logging_vty.c index 8151fda0..8c8a3326 100644 --- a/src/vty/logging_vty.c +++ b/src/vty/logging_vty.c @@ -246,12 +246,14 @@ static const struct value_string logging_print_file_args[] = { DEFUN(logging_prnt_file, logging_prnt_file_cmd, - "logging print file (0|1|basename)", + "logging print file (0|1|basename) [last]", LOGGING_STR "Log output settings\n" "Configure log message\n" "Don't prefix each log message\n" "Prefix each log message with the source file and line\n" - "Prefix each log message with the source file's basename (strip leading paths) and line\n") + "Prefix each log message with the source file's basename (strip leading paths) and line\n" + "Log source file info at the end of a log line. If omitted, log source file info just" + " before the log text.\n") { struct log_target *tgt = osmo_log_vty2tgt(vty); @@ -259,6 +261,10 @@ DEFUN(logging_prnt_file, return CMD_WARNING; log_set_print_filename2(tgt, get_string_value(logging_print_file_args, argv[0])); + if (argc > 1) + log_set_print_filename_pos(tgt, LOG_FILENAME_POS_LINE_END); + else + log_set_print_filename_pos(tgt, LOG_FILENAME_POS_HEADER_END); return CMD_SUCCESS; } @@ -814,15 +820,9 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt) /* stupid old osmo logging API uses uppercase strings... */ osmo_str2lower(cat_lower, osmo_log_info->cat[i].name+1); osmo_str2lower(level_lower, log_level_str(cat->loglevel)); - - if (strcmp(level_lower, "everything") != 0) /* FIXME: remove this check once 'everything' is phased out */ - vty_out(vty, " logging level %s %s%s", cat_lower, level_lower, VTY_NEWLINE); - else - LOGP(DLSTATS, LOGL_ERROR, "logging level everything is deprecated and should not be used\n"); + vty_out(vty, " logging level %s %s%s", cat_lower, level_lower, VTY_NEWLINE); } - /* FIXME: levels */ - return 1; } diff --git a/src/vty/stats_vty.c b/src/vty/stats_vty.c index 8d90945a..5ded7a44 100644 --- a/src/vty/stats_vty.c +++ b/src/vty/stats_vty.c @@ -516,12 +516,15 @@ DEFUN(show_stats_asciidoc_table, host.app_info->name ? host.app_info->name : "", VTY_NEWLINE, VTY_NEWLINE); /* 2x VTY_NEWLINE are intentional otherwise it would interpret the first table header * as usual text*/ + vty_out(vty, "=== Rate Counters%s%s", VTY_NEWLINE, VTY_NEWLINE); vty_out(vty, "// generating tables for rate_ctr_group%s", VTY_NEWLINE); rate_ctr_for_each_group(asciidoc_rate_ctr_group_handler, vty); + vty_out(vty, "== Osmo Stat Items%s%s", VTY_NEWLINE, VTY_NEWLINE); vty_out(vty, "// generating tables for osmo_stat_items%s", VTY_NEWLINE); osmo_stat_item_for_each_group(asciidoc_osmo_stat_item_group_handler, vty); + vty_out(vty, "== Osmo Counters%s%s", VTY_NEWLINE, VTY_NEWLINE); vty_out(vty, "// generating tables for osmo_counters%s", VTY_NEWLINE); asciidoc_counter_generate(vty); return CMD_SUCCESS; diff --git a/src/vty/vty.c b/src/vty/vty.c index 6ca7a154..ad535371 100644 --- a/src/vty/vty.c +++ b/src/vty/vty.c @@ -1694,6 +1694,8 @@ static int vty_config_write(struct vty *vty) /* login */ if (!password_check) vty_out(vty, " no login%s", VTY_NEWLINE); + else + vty_out(vty, " login%s", VTY_NEWLINE); /* bind */ if (vty_bind_addr && (strcmp(vty_bind_addr, VTY_BIND_ADDR_DEFAULT) != 0)) @@ -1766,8 +1768,6 @@ void vty_init_vtysh(void) vtyvec = vector_init(VECTOR_MIN_SIZE); } -extern void *tall_bsc_ctx; - /*! Initialize VTY layer * \param[in] app_info application information */ diff --git a/tests/Makefile.am b/tests/Makefile.am index eaaa8df2..072bb4a2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -23,7 +23,8 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \ coding/coding_test conv/conv_gsm0503_test \ abis/abis_test endian/endian_test sercomm/sercomm_test \ prbs/prbs_test gsm23003/gsm23003_test \ - codec/codec_ecu_fr_test timer/clk_override_test + codec/codec_ecu_fr_test timer/clk_override_test \ + oap/oap_client_test if ENABLE_MSGFILE check_PROGRAMS += msgfile/msgfile_test @@ -172,6 +173,9 @@ gsup_gsup_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la oap_oap_test_SOURCES = oap/oap_test.c oap_oap_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la +oap_oap_client_test_SOURCES = oap/oap_client_test.c +oap_oap_client_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la + fsm_fsm_test_SOURCES = fsm/fsm_test.c fsm_fsm_test_LDADD = $(LDADD) $(top_builddir)/src/ctrl/libosmoctrl.la @@ -253,7 +257,8 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \ conv/conv_gsm0503_test.ok endian/endian_test.ok \ sercomm/sercomm_test.ok prbs/prbs_test.ok \ gsm23003/gsm23003_test.ok \ - timer/clk_override_test.ok + timer/clk_override_test.ok \ + oap/oap_client_test.ok oap/oap_client_test.err DISTCLEANFILES = atconfig atlocal conv/gsm0503_test_vectors.c BUILT_SOURCES = conv/gsm0503_test_vectors.c diff --git a/tests/oap/Makefile.am b/tests/oap/Makefile.am deleted file mode 100644 index 06ccf338..00000000 --- a/tests/oap/Makefile.am +++ /dev/null @@ -1,37 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - -ggdb3 \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(NULL) - -EXTRA_DIST = \ - oap_test.ok \ - $(NULL) - -if HAVE_LIBGTP -if HAVE_LIBCARES -noinst_PROGRAMS = \ - oap_test \ - $(NULL) -endif -endif - -oap_test_SOURCES = \ - oap_test.c \ - $(NULL) - -oap_test_LDADD = \ - $(top_builddir)/src/gprs/oap.o \ - $(top_builddir)/src/gprs/oap_messages.o \ - $(top_builddir)/src/gprs/gprs_utils.o \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - -lrt - diff --git a/tests/oap/oap_client_test.c b/tests/oap/oap_client_test.c new file mode 100644 index 00000000..a841b381 --- /dev/null +++ b/tests/oap/oap_client_test.c @@ -0,0 +1,271 @@ +/* Test Osmocom Authentication Protocol */ +/* + * (C) 2015 by sysmocom s.f.m.c. GmbH + * 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 <osmocom/core/application.h> +#include <osmocom/core/logging.h> +#include <osmocom/gsm/oap.h> + +#include <osmocom/gsm/oap_client.h> + +#include <stdio.h> +#include <string.h> + +static void test_oap_api(void) +{ + printf("Testing OAP API\n"); + + struct osmo_oap_client_config _config; + struct osmo_oap_client_config *config = &_config; + + struct osmo_oap_client_state _state; + struct osmo_oap_client_state *state = &_state; + + struct osmo_oap_message oap_rx; + struct msgb *msg_rx; + + struct osmo_oap_message oap_tx; + struct msgb *msg_tx; + + memset(config, 0, sizeof(*config)); + memset(state, 0, sizeof(*state)); + + OSMO_ASSERT(osmo_hexparse("0102030405060708090a0b0c0d0e0f10", config->secret_k, 16) == 16); + OSMO_ASSERT(osmo_hexparse("1112131415161718191a1b1c1d1e1f20", config->secret_opc, 16) == 16); + + fprintf(stderr, "- make sure filling with zeros means uninitialized\n"); + OSMO_ASSERT(state->state == OSMO_OAP_UNINITIALIZED); + + fprintf(stderr, "- reject messages in uninitialized state\n"); + memset(&oap_rx, 0, sizeof(oap_rx)); + state->client_id = 1; + oap_rx.message_type = OAP_MSGT_REGISTER_ERROR; + msg_rx = osmo_oap_client_encoded(&oap_rx); + OSMO_ASSERT(osmo_oap_client_handle(state, msg_rx, &msg_tx) < 0); + OSMO_ASSERT(state->state == OSMO_OAP_UNINITIALIZED); + msgb_free(msg_rx); + OSMO_ASSERT(!msg_tx); + + fprintf(stderr, "- NULL config should disable\n"); + OSMO_ASSERT( osmo_oap_client_init(NULL, state) == 0 ); + OSMO_ASSERT(state->state == OSMO_OAP_DISABLED); + + fprintf(stderr, "- reject messages in disabled state\n"); + memset(state, 0, sizeof(*state)); + memset(&oap_rx, 0, sizeof(oap_rx)); + state->state = OSMO_OAP_DISABLED; + state->client_id = 1; + oap_rx.message_type = OAP_MSGT_REGISTER_ERROR; + msg_rx = osmo_oap_client_encoded(&oap_rx); + OSMO_ASSERT(osmo_oap_client_handle(state, msg_rx, &msg_tx) < 0); + OSMO_ASSERT(state->state == OSMO_OAP_DISABLED); + msgb_free(msg_rx); + OSMO_ASSERT(!msg_tx); + + fprintf(stderr, "- invalid client_id and shared secret\n"); + memset(state, 0, sizeof(*state)); + config->client_id = 0; + config->secret_k_present = 0; + config->secret_opc_present = 0; + OSMO_ASSERT( osmo_oap_client_init(config, state) == 0 ); + OSMO_ASSERT(state->state == OSMO_OAP_DISABLED); + + fprintf(stderr, "- reset state\n"); + memset(state, 0, sizeof(*state)); + + fprintf(stderr, "- only client_id is invalid\n"); + config->client_id = 0; + config->secret_k_present = 1; + config->secret_opc_present = 1; + OSMO_ASSERT( osmo_oap_client_init(config, state) == 0 ); + OSMO_ASSERT(state->state == OSMO_OAP_DISABLED); + + memset(state, 0, sizeof(*state)); + + fprintf(stderr, "- valid id, but omitted shared_secret (1/2)\n"); + config->client_id = 12345; + config->secret_k_present = 0; + config->secret_opc_present = 1; + OSMO_ASSERT( osmo_oap_client_init(config, state) == 0 ); + OSMO_ASSERT(state->state == OSMO_OAP_DISABLED); + + memset(state, 0, sizeof(*state)); + + fprintf(stderr, "- valid id, but omitted shared_secret (2/2)\n"); + config->client_id = 12345; + config->secret_k_present = 1; + config->secret_opc_present = 0; + OSMO_ASSERT( osmo_oap_client_init(config, state) == 0 ); + OSMO_ASSERT(state->state == OSMO_OAP_DISABLED); + + memset(state, 0, sizeof(*state)); + + + fprintf(stderr, "- mint configuration\n"); + config->client_id = 12345; + config->secret_k_present = 1; + config->secret_opc_present = 1; + /*config->secret_* buffers are still set from the top */ + OSMO_ASSERT( osmo_oap_client_init(config, state) == 0 ); + OSMO_ASSERT(state->state == OSMO_OAP_INITIALIZED); + + + fprintf(stderr, "- Missing challenge data\n"); + memset(&oap_rx, 0, sizeof(oap_rx)); + oap_rx.message_type = OAP_MSGT_CHALLENGE_REQUEST; + oap_rx.rand_present = 0; + oap_rx.autn_present = 0; + msg_rx = osmo_oap_client_encoded(&oap_rx); + OSMO_ASSERT(osmo_oap_client_handle(state, msg_rx, &msg_tx) == -2); + msgb_free(msg_rx); + OSMO_ASSERT(!msg_tx); + + fprintf(stderr, "- AUTN missing\n"); + osmo_hexparse("0102030405060708090a0b0c0d0e0f10", + oap_rx.rand, 16); + oap_rx.rand_present = 1; + msg_rx = osmo_oap_client_encoded(&oap_rx); + OSMO_ASSERT(osmo_oap_client_handle(state, msg_rx, &msg_tx) == -2); + msgb_free(msg_rx); + OSMO_ASSERT(!msg_tx); + + fprintf(stderr, "- RAND missing\n"); + oap_rx.rand_present = 0; + osmo_hexparse("cec4e3848a33000086781158ca40f136", + oap_rx.autn, 16); + oap_rx.autn_present = 1; + msg_rx = osmo_oap_client_encoded(&oap_rx); + OSMO_ASSERT(osmo_oap_client_handle(state, msg_rx, &msg_tx) == -2); + msgb_free(msg_rx); + OSMO_ASSERT(!msg_tx); + + fprintf(stderr, "- wrong autn (by one bit)\n"); + osmo_hexparse("0102030405060708090a0b0c0d0e0f10", + oap_rx.rand, 16); + osmo_hexparse("dec4e3848a33000086781158ca40f136", + oap_rx.autn, 16); + oap_rx.rand_present = 1; + oap_rx.autn_present = 1; + msg_rx = osmo_oap_client_encoded(&oap_rx); + OSMO_ASSERT(osmo_oap_client_handle(state, msg_rx, &msg_tx) == -2); + msgb_free(msg_rx); + OSMO_ASSERT(!msg_tx); + + fprintf(stderr, "- all data correct\n"); + osmo_hexparse("cec4e3848a33000086781158ca40f136", + oap_rx.autn, 16); + msg_rx = osmo_oap_client_encoded(&oap_rx); + + fprintf(stderr, "- but refuse to evaluate in uninitialized state\n"); + OSMO_ASSERT(state->state == OSMO_OAP_INITIALIZED); + + state->state = OSMO_OAP_UNINITIALIZED; + OSMO_ASSERT(osmo_oap_client_handle(state, msg_rx, &msg_tx) < 0); + OSMO_ASSERT(!msg_tx); + + state->state = OSMO_OAP_DISABLED; + OSMO_ASSERT(osmo_oap_client_handle(state, msg_rx, &msg_tx) < 0); + OSMO_ASSERT(!msg_tx); + + state->state = OSMO_OAP_INITIALIZED; + + fprintf(stderr, "- now everything is correct\n"); + /* a successful return value here indicates correct autn */ + OSMO_ASSERT(osmo_oap_client_handle(state, msg_rx, &msg_tx) == 0); + msgb_free(msg_rx); + + fprintf(stderr, "- Expect the challenge response in msg_tx\n"); + OSMO_ASSERT(msg_tx); + OSMO_ASSERT(osmo_oap_decode(&oap_tx, msg_tx->data, msg_tx->len) == 0); + OSMO_ASSERT(oap_tx.message_type == OAP_MSGT_CHALLENGE_RESULT); + OSMO_ASSERT(strcmp("e2d05b598c61d9ba", + osmo_hexdump_nospc(oap_tx.xres, sizeof(oap_tx.xres))) + == 0); + OSMO_ASSERT(state->state == OSMO_OAP_SENT_CHALLENGE_RESULT); + msgb_free(msg_tx); + msg_tx = 0; + + struct osmo_oap_client_state saved_state = _state; + + fprintf(stderr, "- Receive registration error for the first time.\n"); + + memset(&oap_rx, 0, sizeof(oap_rx)); + oap_rx.message_type = OAP_MSGT_REGISTER_ERROR; + oap_rx.cause = GMM_CAUSE_PROTO_ERR_UNSPEC; + msg_rx = osmo_oap_client_encoded(&oap_rx); + + OSMO_ASSERT(state->registration_failures == 0); + OSMO_ASSERT(osmo_oap_client_handle(state, msg_rx, &msg_tx) == 0); + OSMO_ASSERT(state->registration_failures == 1); + OSMO_ASSERT(msg_tx); + OSMO_ASSERT(osmo_oap_decode(&oap_tx, msg_tx->data, msg_tx->len) == 0); + OSMO_ASSERT(oap_tx.message_type == OAP_MSGT_REGISTER_REQUEST); + OSMO_ASSERT(state->state == OSMO_OAP_REQUESTED_CHALLENGE); + msgb_free(msg_tx); + msg_tx = 0; + + fprintf(stderr, "- Receive registration error for the Nth time.\n"); + state->registration_failures = 999; + OSMO_ASSERT(osmo_oap_client_handle(state, msg_rx, &msg_tx) == -11); + OSMO_ASSERT(!msg_tx); + OSMO_ASSERT(state->state == OSMO_OAP_INITIALIZED); + msgb_free(msg_tx); + msg_tx = 0; + + msgb_free(msg_rx); + + fprintf(stderr, "- Registration success\n"); + + _state = saved_state; + memset(&oap_rx, 0, sizeof(oap_rx)); + oap_rx.message_type = OAP_MSGT_REGISTER_RESULT; + msg_rx = osmo_oap_client_encoded(&oap_rx); + OSMO_ASSERT(osmo_oap_client_handle(state, msg_rx, &msg_tx) == 0); + OSMO_ASSERT(!msg_tx); + OSMO_ASSERT(state->state == OSMO_OAP_REGISTERED); + msgb_free(msg_rx); +} + +static struct log_info_cat oap_client_test_categories[] = { +}; + +static struct log_info info = { + .cat = oap_client_test_categories, + .num_cat = ARRAY_SIZE(oap_client_test_categories), +}; + +int main(int argc, char **argv) +{ + void *ctx = talloc_named_const(NULL, 0, "oap_client_test"); + msgb_talloc_ctx_init(ctx, 0); + osmo_init_logging2(ctx, &info); + + OSMO_ASSERT(osmo_stderr_target); + log_set_use_color(osmo_stderr_target, 0); + log_set_print_timestamp(osmo_stderr_target, 0); + log_set_print_filename(osmo_stderr_target, 0); + log_set_print_category(osmo_stderr_target, 1); + log_parse_category_mask(osmo_stderr_target, "DLOAP,1"); + + test_oap_api(); + printf("Done\n"); + + return 0; +} + diff --git a/tests/oap/oap_client_test.err b/tests/oap/oap_client_test.err new file mode 100644 index 00000000..62ddc9ef --- /dev/null +++ b/tests/oap/oap_client_test.err @@ -0,0 +1,35 @@ +- make sure filling with zeros means uninitialized +- reject messages in uninitialized state +DLOAP Received OAP message 5, but the OAP client is not initialized +- NULL config should disable +- reject messages in disabled state +DLOAP Received OAP message 5, but the OAP client is disabled +- invalid client_id and shared secret +- reset state +- only client_id is invalid +- valid id, but omitted shared_secret (1/2) +DLOAP OAP: client ID set, but secret K missing. +- valid id, but omitted shared_secret (2/2) +DLOAP OAP: client ID set, but secret OPC missing. +- mint configuration +- Missing challenge data +DLOAP OAP challenge incomplete (rand_present: 0, autn_present: 0) +- AUTN missing +DLOAP OAP challenge incomplete (rand_present: 1, autn_present: 0) +- RAND missing +DLOAP OAP challenge incomplete (rand_present: 0, autn_present: 1) +- wrong autn (by one bit) +DLOAP OAP: AUTN mismatch! +DLOAP OAP: AUTN from server: dec4e3848a33000086781158ca40f136 +DLOAP OAP: AUTN expected: cec4e3848a33000086781158ca40f136 +- all data correct +- but refuse to evaluate in uninitialized state +DLOAP Received OAP message 8, but the OAP client is not initialized +DLOAP Received OAP message 8, but the OAP client is disabled +- now everything is correct +- Expect the challenge response in msg_tx +- Receive registration error for the first time. +DLOAP OAP registration failed +- Receive registration error for the Nth time. +DLOAP OAP registration failed +- Registration success diff --git a/tests/oap/oap_client_test.ok b/tests/oap/oap_client_test.ok new file mode 100644 index 00000000..59108a79 --- /dev/null +++ b/tests/oap/oap_client_test.ok @@ -0,0 +1,2 @@ +Testing OAP API +Done diff --git a/tests/testsuite.at b/tests/testsuite.at index 43b58e8d..a1cf98ae 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -274,6 +274,13 @@ touch experr AT_CHECK([$abs_top_builddir/tests/oap/oap_test], [0], [expout], [experr]) AT_CLEANUP +AT_SETUP([oap_client]) +AT_KEYWORDS([oap_client]) +cat $abs_srcdir/oap/oap_client_test.ok > expout +cat $abs_srcdir/oap/oap_client_test.err > experr +AT_CHECK([$abs_top_builddir/tests/oap/oap_client_test], [0], [expout], [experr]) +AT_CLEANUP + AT_SETUP([socket]) AT_KEYWORDS([socket]) cat $abs_srcdir/socket/socket_test.ok > expout diff --git a/tests/utils/utils_test.c b/tests/utils/utils_test.c index a146190d..2f1e87da 100644 --- a/tests/utils/utils_test.c +++ b/tests/utils/utils_test.c @@ -21,6 +21,7 @@ */ #include <osmocom/gsm/ipa.h> +#include <osmocom/gsm/protocol/ipaccess.h> #include <osmocom/core/logging.h> #include <osmocom/core/utils.h> @@ -170,12 +171,65 @@ static void hexparse_test(void) printf("rc = %d\n", rc); } -static void test_idtag_parsing(void) +static void test_ipa_ccm_id_resp_parsing(void) { struct tlv_parsed tvp; int rc; - static uint8_t data[] = { + static const uint8_t id_resp_data[] = { + 0x00, 0x13, IPAC_IDTAG_MACADDR, + '0','0',':','0','2',':','9','5',':','0','0',':','6','2',':','9','e','\0', + 0x00, 0x11, IPAC_IDTAG_IPADDR, + '1','9','2','.','1','6','8','.','1','0','0','.','1','9','0','\0', + 0x00, 0x0a, IPAC_IDTAG_UNIT, + '1','2','3','4','/','0','/','0','\0', + 0x00, 0x02, IPAC_IDTAG_LOCATION1, + '\0', + 0x00, 0x0d, IPAC_IDTAG_LOCATION2, + 'B','T','S','_','N','B','T','1','3','1','G','\0', + 0x00, 0x0c, IPAC_IDTAG_EQUIPVERS, + '1','6','5','a','0','2','9','_','5','5','\0', + 0x00, 0x14, IPAC_IDTAG_SWVERSION, + '1','6','8','d','4','7','2','_','v','2','0','0','b','4','1','1','d','0','\0', + 0x00, 0x18, IPAC_IDTAG_UNITNAME, + 'n','b','t','s','-','0','0','-','0','2','-','9','5','-','0','0','-','6','2','-','9','E','\0', + 0x00, 0x0a, IPAC_IDTAG_SERNR, + '0','0','1','1','0','7','8','1','\0' + }; + + printf("\nTesting IPA CCM ID RESP parsing\n"); + + rc = ipa_ccm_id_resp_parse(&tvp, (uint8_t *) id_resp_data, sizeof(id_resp_data)); + OSMO_ASSERT(rc == 0); + + OSMO_ASSERT(TLVP_PRESENT(&tvp, IPAC_IDTAG_MACADDR)); + OSMO_ASSERT(TLVP_LEN(&tvp, IPAC_IDTAG_MACADDR) == 0x12); + OSMO_ASSERT(TLVP_PRESENT(&tvp, IPAC_IDTAG_IPADDR)); + OSMO_ASSERT(TLVP_LEN(&tvp, IPAC_IDTAG_IPADDR) == 0x10); + OSMO_ASSERT(TLVP_PRESENT(&tvp, IPAC_IDTAG_UNIT)); + OSMO_ASSERT(TLVP_LEN(&tvp, IPAC_IDTAG_UNIT) == 0x09); + OSMO_ASSERT(TLVP_PRESENT(&tvp, IPAC_IDTAG_LOCATION1)); + OSMO_ASSERT(TLVP_LEN(&tvp, IPAC_IDTAG_LOCATION1) == 0x01); + OSMO_ASSERT(TLVP_PRESENT(&tvp, IPAC_IDTAG_LOCATION2)); + OSMO_ASSERT(TLVP_LEN(&tvp, IPAC_IDTAG_LOCATION2) == 0x0c); + OSMO_ASSERT(TLVP_PRESENT(&tvp, IPAC_IDTAG_EQUIPVERS)); + OSMO_ASSERT(TLVP_LEN(&tvp, IPAC_IDTAG_EQUIPVERS) == 0x0b); + OSMO_ASSERT(TLVP_PRESENT(&tvp, IPAC_IDTAG_SWVERSION)); + OSMO_ASSERT(TLVP_LEN(&tvp, IPAC_IDTAG_EQUIPVERS) == 0x0b); + OSMO_ASSERT(TLVP_LEN(&tvp, IPAC_IDTAG_SWVERSION) == 0x13); + OSMO_ASSERT(TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME)); + OSMO_ASSERT(TLVP_LEN(&tvp, IPAC_IDTAG_UNITNAME) == 0x17); + OSMO_ASSERT(TLVP_PRESENT(&tvp, IPAC_IDTAG_SERNR)); + OSMO_ASSERT(TLVP_LEN(&tvp, IPAC_IDTAG_SERNR) == 0x09); +} + +static void test_ipa_ccm_id_get_parsing(void) +{ + struct tlv_parsed tvp; + int rc; + + /* IPA CCM IDENTITY REQUEST message: 8bit length followed by respective value */ + static const uint8_t id_get_data[] = { 0x01, 0x08, 0x01, 0x07, 0x01, 0x02, @@ -188,7 +242,9 @@ static void test_idtag_parsing(void) 0x11, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - rc = ipa_ccm_idtag_parse_off(&tvp, data, sizeof(data), 1); + printf("\nTesting IPA CCM ID GET parsing\n"); + + rc = ipa_ccm_id_get_parse(&tvp, id_get_data, sizeof(id_get_data)); OSMO_ASSERT(rc == 0); OSMO_ASSERT(TLVP_PRESENT(&tvp, 8)); @@ -567,7 +623,8 @@ int main(int argc, char **argv) hexdump_test(); hexparse_test(); - test_idtag_parsing(); + test_ipa_ccm_id_get_parsing(); + test_ipa_ccm_id_resp_parsing(); test_is_hexstr(); bcd_test(); str_escape_test(); diff --git a/tests/utils/utils_test.ok b/tests/utils/utils_test.ok index b158bf7b..abc7317a 100644 --- a/tests/utils/utils_test.ok +++ b/tests/utils/utils_test.ok @@ -27,6 +27,10 @@ rc = -1 Hexparse with invalid char rc = -1 +Testing IPA CCM ID GET parsing + +Testing IPA CCM ID RESP parsing + ----- test_is_hexstr 0: pass str='(null)' min=0 max=10 even=0 expect=valid 1: pass str='(null)' min=1 max=10 even=0 expect=invalid diff --git a/utils/osmo-sim-test.c b/utils/osmo-sim-test.c index ea241206..5588294a 100644 --- a/utils/osmo-sim-test.c +++ b/utils/osmo-sim-test.c @@ -74,7 +74,8 @@ static int verify_pin(struct osim_chan_hdl *st, uint8_t pin_nr, char *pin) msg = osim_new_apdumsg(0x00, 0x20, 0x00, pin_nr, 8, 0); pindst = (char *) msgb_put(msg, 8); memset(pindst, 0xFF, 8); - strncpy(pindst, pin, strlen(pin)); + /* Do not copy the terminating \0 */ + memcpy(pindst, pin, strlen(pin)); return osim_transceive_apdu(st, msg); } |