aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.checkpatch.conf1
-rw-r--r--.github/FUNDING.yml1
-rw-r--r--.gitignore13
-rw-r--r--Doxyfile.codec.in2
-rw-r--r--Doxyfile.coding.in2
-rw-r--r--Doxyfile.core.in4
-rw-r--r--Doxyfile.ctrl.in2
-rw-r--r--Doxyfile.gb.in2
-rw-r--r--Doxyfile.gsm.in2
-rw-r--r--Doxyfile.isdn.in1716
-rw-r--r--Doxyfile.sim.in1716
-rw-r--r--Doxyfile.usb.in1716
-rw-r--r--Doxyfile.vty.in2
-rw-r--r--Makefile.am128
-rw-r--r--README.md4
-rw-r--r--TODO-RELEASE19
-rw-r--r--configure.ac148
-rwxr-xr-xcontrib/jenkins_amd64.sh8
-rwxr-xr-xcontrib/jenkins_arm.sh1
-rw-r--r--contrib/jenkins_common.sh31
-rw-r--r--contrib/libosmocore.spec.in135
-rwxr-xr-xcontrib/struct_endianness.py (renamed from contrib/struct_endianess.py)4
-rwxr-xr-xcontrib/talloc_count.sh53
-rw-r--r--debian/changelog907
-rw-r--r--debian/compat2
-rw-r--r--debian/control118
-rw-r--r--debian/copyright18
-rw-r--r--debian/libosmocodec4.install (renamed from debian/libosmocodec0.install)0
-rw-r--r--debian/libosmocore-utils.install1
-rw-r--r--debian/libosmocore.dirs1
-rw-r--r--debian/libosmocore21.install (renamed from debian/libosmocore17.install)0
-rw-r--r--debian/libosmogb14.install (renamed from debian/libosmogb12.install)0
-rw-r--r--debian/libosmogsm20.install (renamed from debian/libosmogsm16.install)0
-rw-r--r--debian/libosmoisdn-doc.doc-base7
-rw-r--r--debian/libosmoisdn-doc.install1
-rw-r--r--debian/libosmoisdn0.install1
-rw-r--r--debian/libosmosim-doc.doc-base7
-rw-r--r--debian/libosmosim-doc.install1
-rw-r--r--debian/libosmousb-doc.doc-base7
-rw-r--r--debian/libosmousb-doc.install1
-rw-r--r--debian/libosmovty13.install (renamed from debian/libosmovty9.install)0
-rwxr-xr-xdebian/rules23
-rw-r--r--include/Makefile.am219
-rw-r--r--include/osmocom/Makefile.am13
-rw-r--r--include/osmocom/codec/Makefile.am7
-rw-r--r--include/osmocom/codec/codec.h54
-rw-r--r--include/osmocom/codec/ecu.h6
-rw-r--r--include/osmocom/coding/Makefile.am10
-rw-r--r--include/osmocom/coding/gsm0503_amr_dtx.h11
-rw-r--r--include/osmocom/coding/gsm0503_coding.h39
-rw-r--r--include/osmocom/coding/gsm0503_interleaving.h3
-rw-r--r--include/osmocom/core/Makefile.am103
-rw-r--r--include/osmocom/core/base64.h69
-rw-r--r--include/osmocom/core/bitXXgen.h.tpl4
-rw-r--r--include/osmocom/core/bitcomp.h4
-rw-r--r--include/osmocom/core/bitvec.h9
-rw-r--r--include/osmocom/core/conv.h5
-rw-r--r--include/osmocom/core/counter.h4
-rw-r--r--include/osmocom/core/crcXXgen.h.tpl4
-rw-r--r--include/osmocom/core/crcgen.h4
-rw-r--r--include/osmocom/core/endian.h2
-rw-r--r--include/osmocom/core/exec.h4
-rw-r--r--include/osmocom/core/fsm.h8
-rw-r--r--include/osmocom/core/gsmtap.h7
-rw-r--r--include/osmocom/core/gsmtap_util.h25
-rw-r--r--include/osmocom/core/isdnhdlc.h9
-rw-r--r--include/osmocom/core/linuxlist.h9
-rw-r--r--include/osmocom/core/linuxrbtree.h5
-rw-r--r--include/osmocom/core/logging.h46
-rw-r--r--include/osmocom/core/loggingrb.h4
-rw-r--r--include/osmocom/core/msgb.h50
-rw-r--r--include/osmocom/core/msgfile.h4
-rw-r--r--include/osmocom/core/netdev.h49
-rw-r--r--include/osmocom/core/netns.h24
-rw-r--r--include/osmocom/core/osmo_io.h207
-rw-r--r--include/osmocom/core/prim.h6
-rw-r--r--include/osmocom/core/rate_ctr.h18
-rw-r--r--include/osmocom/core/select.h3
-rw-r--r--include/osmocom/core/sercomm.h5
-rw-r--r--include/osmocom/core/serial.h4
-rw-r--r--include/osmocom/core/sockaddr_str.h20
-rw-r--r--include/osmocom/core/socket.h134
-rw-r--r--include/osmocom/core/socket_compat.h.tpl10
-rw-r--r--include/osmocom/core/soft_uart.h149
-rw-r--r--include/osmocom/core/stat_item.h60
-rw-r--r--include/osmocom/core/stats.h7
-rw-r--r--include/osmocom/core/stats_tcp.h16
-rw-r--r--include/osmocom/core/strrb.h8
-rw-r--r--include/osmocom/core/tdef.h17
-rw-r--r--include/osmocom/core/thread.h4
-rw-r--r--include/osmocom/core/time_cc.h187
-rw-r--r--include/osmocom/core/timer.h4
-rw-r--r--include/osmocom/core/timer_compat.h4
-rw-r--r--include/osmocom/core/tun.h43
-rw-r--r--include/osmocom/core/use_count.h4
-rw-r--r--include/osmocom/core/utils.h90
-rw-r--r--include/osmocom/core/write_queue.h5
-rw-r--r--include/osmocom/crypt/Makefile.am8
-rw-r--r--include/osmocom/crypt/auth.h52
-rw-r--r--include/osmocom/crypt/kdf.h21
-rw-r--r--include/osmocom/crypt/utran_cipher.h19
-rw-r--r--include/osmocom/ctrl/Makefile.am13
-rw-r--r--include/osmocom/ctrl/control_cmd.h6
-rw-r--r--include/osmocom/ctrl/control_if.h10
-rw-r--r--include/osmocom/ctrl/control_vty.h5
-rw-r--r--include/osmocom/ctrl/ports.h6
-rw-r--r--include/osmocom/gprs/Makefile.am17
-rw-r--r--include/osmocom/gprs/bssgp_bvc_fsm.h2
-rw-r--r--include/osmocom/gprs/gprs_bssgp.h2
-rw-r--r--include/osmocom/gprs/gprs_bssgp2.h2
-rw-r--r--include/osmocom/gprs/gprs_bssgp_rim.h2
-rw-r--r--include/osmocom/gprs/gprs_ns2.h2
-rw-r--r--include/osmocom/gprs/gprs_rlc.h54
-rw-r--r--include/osmocom/gprs/protocol/Makefile.am8
-rw-r--r--include/osmocom/gprs/protocol/gsm_04_60.h201
-rw-r--r--include/osmocom/gprs/protocol/gsm_08_18.h9
-rw-r--r--include/osmocom/gsm/Makefile.am69
-rw-r--r--include/osmocom/gsm/a5.h4
-rw-r--r--include/osmocom/gsm/bsslap.h4
-rw-r--r--include/osmocom/gsm/bssmap_le.h4
-rw-r--r--include/osmocom/gsm/bts_features.h9
-rw-r--r--include/osmocom/gsm/cbsp.h33
-rw-r--r--include/osmocom/gsm/gsm0502.h66
-rw-r--r--include/osmocom/gsm/gsm0808.h284
-rw-r--r--include/osmocom/gsm/gsm0808_lcs.h4
-rw-r--r--include/osmocom/gsm/gsm0808_utils.h278
-rw-r--r--include/osmocom/gsm/gsm23003.h7
-rw-r--r--include/osmocom/gsm/gsm29118.h4
-rw-r--r--include/osmocom/gsm/gsm29205.h8
-rw-r--r--include/osmocom/gsm/gsm44021.h8
-rw-r--r--include/osmocom/gsm/gsm48.h12
-rw-r--r--include/osmocom/gsm/gsm48_ie.h5
-rw-r--r--include/osmocom/gsm/gsm48_rest_octets.h5
-rw-r--r--include/osmocom/gsm/gsm_utils.h26
-rw-r--r--include/osmocom/gsm/gsup.h41
-rw-r--r--include/osmocom/gsm/i460_mux.h121
-rw-r--r--include/osmocom/gsm/ipa.h2
-rw-r--r--include/osmocom/gsm/iuup.h129
-rw-r--r--include/osmocom/gsm/l1sap.h8
-rw-r--r--include/osmocom/gsm/lapd_core.h177
-rw-r--r--include/osmocom/gsm/lapdm.h14
-rw-r--r--include/osmocom/gsm/meas_rep.h4
-rw-r--r--include/osmocom/gsm/prim.h3
-rw-r--r--include/osmocom/gsm/protocol/Makefile.am30
-rw-r--r--include/osmocom/gsm/protocol/gsm_03_41.h8
-rw-r--r--include/osmocom/gsm/protocol/gsm_04_08.h231
-rw-r--r--include/osmocom/gsm/protocol/gsm_04_08_gprs.h65
-rw-r--r--include/osmocom/gsm/protocol/gsm_04_11.h10
-rw-r--r--include/osmocom/gsm/protocol/gsm_04_12.h4
-rw-r--r--include/osmocom/gsm/protocol/gsm_04_14.h8
-rw-r--r--include/osmocom/gsm/protocol/gsm_08_08.h162
-rw-r--r--include/osmocom/gsm/protocol/gsm_08_58.h293
-rw-r--r--include/osmocom/gsm/protocol/gsm_09_02.h5
-rw-r--r--include/osmocom/gsm/protocol/gsm_12_21.h165
-rw-r--r--include/osmocom/gsm/protocol/gsm_23_032.h16
-rw-r--r--include/osmocom/gsm/protocol/gsm_23_041.h12
-rw-r--r--include/osmocom/gsm/protocol/gsm_25_415.h222
-rw-r--r--include/osmocom/gsm/protocol/gsm_44_004.h14
-rw-r--r--include/osmocom/gsm/protocol/gsm_44_060.h252
-rw-r--r--include/osmocom/gsm/protocol/gsm_44_068.h136
-rw-r--r--include/osmocom/gsm/protocol/gsm_44_318.h4
-rw-r--r--include/osmocom/gsm/protocol/gsm_48_071.h4
-rw-r--r--include/osmocom/gsm/protocol/gsm_49_031.h32
-rw-r--r--include/osmocom/gsm/protocol/ipaccess.h1
-rw-r--r--include/osmocom/gsm/rlp.h81
-rw-r--r--include/osmocom/gsm/tlv.h16
-rw-r--r--include/osmocom/isdn/Makefile.am8
-rw-r--r--include/osmocom/isdn/i460_mux.h120
-rw-r--r--include/osmocom/isdn/lapd_core.h192
-rw-r--r--include/osmocom/isdn/v110.h57
-rw-r--r--include/osmocom/isdn/v110_ta.h113
-rw-r--r--include/osmocom/sim/Makefile.am6
-rw-r--r--include/osmocom/sim/class_tables.h4
-rw-r--r--include/osmocom/sim/sim.h8
-rw-r--r--include/osmocom/usb/Makefile.am7
-rw-r--r--include/osmocom/usb/libusb.h4
-rw-r--r--include/osmocom/vty/Makefile.am17
-rw-r--r--include/osmocom/vty/command.h10
-rw-r--r--include/osmocom/vty/logging.h6
-rw-r--r--include/osmocom/vty/misc.h12
-rw-r--r--include/osmocom/vty/ports.h54
-rw-r--r--include/osmocom/vty/telnet_interface.h12
-rw-r--r--include/osmocom/vty/vector.h5
-rw-r--r--include/osmocom/vty/vty.h20
-rw-r--r--libosmocodec.pc.in6
-rw-r--r--libosmocoding.pc.in6
-rw-r--r--libosmocore.pc.in6
-rw-r--r--libosmoctrl.pc.in4
-rw-r--r--libosmogb.pc.in4
-rw-r--r--libosmogsm.pc.in4
-rw-r--r--libosmoisdn.pc.in11
-rw-r--r--libosmosim.pc.in4
-rw-r--r--libosmousb.pc.in4
-rw-r--r--libosmovty.pc.in4
-rwxr-xr-xosmo-release.sh8
-rw-r--r--src/Makefile.am112
-rw-r--r--src/codec/Makefile.am18
-rw-r--r--src/codec/ecu.c20
-rw-r--r--src/codec/ecu_fr.c387
-rw-r--r--src/codec/ecu_fr_old.c166
-rw-r--r--src/codec/gsm610.c160
-rw-r--r--src/codec/gsm620.c55
-rw-r--r--src/codec/gsm660.c166
-rw-r--r--src/codec/gsm690.c4
-rw-r--r--src/coding/Makefile.am21
-rw-r--r--src/coding/gsm0503_amr_dtx.c191
-rw-r--r--src/coding/gsm0503_coding.c1125
-rw-r--r--src/coding/gsm0503_interleaving.c56
-rw-r--r--src/coding/gsm0503_mapping.c4
-rw-r--r--src/coding/gsm0503_parity.c4
-rw-r--r--src/coding/gsm0503_tables.c4
-rw-r--r--src/coding/libosmocoding.map23
-rw-r--r--src/core/Makefile.am166
-rw-r--r--src/core/application.c (renamed from src/application.c)4
-rw-r--r--src/core/backtrace.c (renamed from src/backtrace.c)4
-rw-r--r--src/core/base64.c195
-rw-r--r--src/core/bitcomp.c (renamed from src/bitcomp.c)4
-rw-r--r--src/core/bits.c (renamed from src/bits.c)14
-rw-r--r--src/core/bitvec.c (renamed from src/bitvec.c)37
-rw-r--r--src/core/context.c (renamed from src/context.c)9
-rw-r--r--src/core/conv.c (renamed from src/conv.c)199
-rw-r--r--src/core/conv_acc.c (renamed from src/conv_acc.c)27
-rw-r--r--src/core/conv_acc_generic.c (renamed from src/conv_acc_generic.c)4
-rw-r--r--src/core/conv_acc_neon.c (renamed from src/conv_acc_neon.c)4
-rw-r--r--src/core/conv_acc_neon_impl.h (renamed from src/conv_acc_neon_impl.h)4
-rw-r--r--src/core/conv_acc_sse.c (renamed from src/conv_acc_sse.c)4
-rw-r--r--src/core/conv_acc_sse_avx.c (renamed from src/conv_acc_sse_avx.c)4
-rw-r--r--src/core/conv_acc_sse_impl.h (renamed from src/conv_acc_sse_impl.h)4
-rw-r--r--src/core/counter.c (renamed from src/counter.c)6
-rw-r--r--src/core/crc16.c (renamed from src/crc16.c)0
-rw-r--r--src/core/crcXXgen.c.tpl (renamed from src/crcXXgen.c.tpl)4
-rw-r--r--src/core/exec.c (renamed from src/exec.c)23
-rw-r--r--src/core/fsm.c (renamed from src/fsm.c)22
-rw-r--r--src/core/gsmtap_util.c (renamed from src/gsmtap_util.c)233
-rw-r--r--src/core/isdnhdlc.c (renamed from src/isdnhdlc.c)11
-rw-r--r--src/core/it_q.c (renamed from src/it_q.c)18
-rw-r--r--src/core/libosmocore.map631
-rw-r--r--src/core/logging.c (renamed from src/logging.c)629
-rw-r--r--src/core/logging_gsmtap.c (renamed from src/logging_gsmtap.c)11
-rw-r--r--src/core/logging_syslog.c (renamed from src/logging_syslog.c)6
-rw-r--r--src/core/logging_systemd.c (renamed from src/logging_systemd.c)4
-rw-r--r--src/core/loggingrb.c (renamed from src/loggingrb.c)4
-rw-r--r--src/core/macaddr.c (renamed from src/macaddr.c)4
-rw-r--r--src/core/mnl.c (renamed from src/mnl.c)9
-rw-r--r--src/core/msgb.c (renamed from src/msgb.c)62
-rw-r--r--src/core/msgfile.c (renamed from src/msgfile.c)4
-rw-r--r--src/core/netdev.c962
-rw-r--r--src/core/netns.c208
-rw-r--r--src/core/osmo_io.c1007
-rw-r--r--src/core/osmo_io_internal.h166
-rw-r--r--src/core/osmo_io_poll.c201
-rw-r--r--src/core/osmo_io_uring.c532
-rw-r--r--src/core/panic.c (renamed from src/panic.c)6
-rw-r--r--src/core/plugin.c (renamed from src/plugin.c)6
-rw-r--r--src/core/prbs.c (renamed from src/prbs.c)0
-rw-r--r--src/core/prim.c (renamed from src/prim.c)0
-rw-r--r--src/core/probes.d (renamed from src/probes.d)0
-rw-r--r--src/core/rate_ctr.c (renamed from src/rate_ctr.c)89
-rw-r--r--src/core/rbtree.c (renamed from src/rbtree.c)5
-rw-r--r--src/core/select.c (renamed from src/select.c)172
-rw-r--r--src/core/sercomm.c (renamed from src/sercomm.c)4
-rw-r--r--src/core/serial.c (renamed from src/serial.c)4
-rw-r--r--src/core/signal.c (renamed from src/signal.c)4
-rw-r--r--src/core/sockaddr_str.c (renamed from src/sockaddr_str.c)26
-rw-r--r--src/core/socket.c (renamed from src/socket.c)1163
-rw-r--r--src/core/soft_uart.c518
-rw-r--r--src/core/stat_item.c463
-rw-r--r--src/core/stat_item_internal.h35
-rw-r--r--src/core/stats.c (renamed from src/stats.c)78
-rw-r--r--src/core/stats_statsd.c (renamed from src/stats_statsd.c)60
-rw-r--r--src/core/stats_tcp.c327
-rw-r--r--src/core/strrb.c (renamed from src/strrb.c)8
-rw-r--r--src/core/tdef.c (renamed from src/tdef.c)33
-rw-r--r--src/core/thread.c (renamed from src/thread.c)4
-rw-r--r--src/core/time_cc.c228
-rw-r--r--src/core/timer.c (renamed from src/timer.c)13
-rw-r--r--src/core/timer_clockgettime.c (renamed from src/timer_clockgettime.c)4
-rw-r--r--src/core/timer_gettimeofday.c (renamed from src/timer_gettimeofday.c)4
-rw-r--r--src/core/tun.c577
-rw-r--r--src/core/use_count.c (renamed from src/use_count.c)6
-rw-r--r--src/core/utils.c (renamed from src/utils.c)218
-rw-r--r--src/core/write_queue.c (renamed from src/write_queue.c)28
-rw-r--r--src/ctrl/Makefile.am8
-rw-r--r--src/ctrl/control_cmd.c106
-rw-r--r--src/ctrl/control_if.c164
-rw-r--r--src/ctrl/control_vty.c17
-rw-r--r--src/ctrl/libosmoctrl.map3
-rw-r--r--src/gb/Makefile.am16
-rw-r--r--src/gb/bssgp_bvc_fsm.c6
-rw-r--r--src/gb/frame_relay.c4
-rw-r--r--src/gb/gprs_bssgp.c17
-rw-r--r--src/gb/gprs_bssgp2.c29
-rw-r--r--src/gb/gprs_bssgp_bss.c4
-rw-r--r--src/gb/gprs_bssgp_rim.c78
-rw-r--r--src/gb/gprs_ns.c54
-rw-r--r--src/gb/gprs_ns2.c129
-rw-r--r--src/gb/gprs_ns2_fr.c206
-rw-r--r--src/gb/gprs_ns2_frgre.c12
-rw-r--r--src/gb/gprs_ns2_internal.h89
-rw-r--r--src/gb/gprs_ns2_message.c69
-rw-r--r--src/gb/gprs_ns2_sns.c1435
-rw-r--r--src/gb/gprs_ns2_udp.c156
-rw-r--r--src/gb/gprs_ns2_vc_fsm.c144
-rw-r--r--src/gb/gprs_ns2_vty.c118
-rw-r--r--src/gb/libosmogb.map3
-rw-r--r--src/gsm/Makefile.am32
-rw-r--r--src/gsm/a5.c4
-rw-r--r--src/gsm/abis_nm.c96
-rw-r--r--src/gsm/apn.c2
-rw-r--r--src/gsm/auth_comp128v1.c7
-rw-r--r--src/gsm/auth_comp128v23.c10
-rw-r--r--src/gsm/auth_core.c172
-rw-r--r--src/gsm/auth_milenage.c35
-rw-r--r--src/gsm/auth_tuak.c207
-rw-r--r--src/gsm/auth_xor.c28
-rw-r--r--src/gsm/auth_xor_2g.c81
-rw-r--r--src/gsm/bsslap.c6
-rw-r--r--src/gsm/bssmap_le.c85
-rw-r--r--src/gsm/bts_features.c26
-rw-r--r--src/gsm/cbsp.c275
-rw-r--r--src/gsm/comp128.c4
-rw-r--r--src/gsm/comp128v23.c4
-rw-r--r--src/gsm/gea.c4
-rw-r--r--src/gsm/gprs_cipher_core.c4
-rw-r--r--src/gsm/gprs_gea.c4
-rw-r--r--src/gsm/gprs_rlc.c3
-rw-r--r--src/gsm/gsm0341.c4
-rw-r--r--src/gsm/gsm0411_utils.c6
-rw-r--r--src/gsm/gsm0480.c4
-rw-r--r--src/gsm/gsm0502.c42
-rw-r--r--src/gsm/gsm0808.c928
-rw-r--r--src/gsm/gsm0808_utils.c744
-rw-r--r--src/gsm/gsm23003.c80
-rw-r--r--src/gsm/gsm23236.c26
-rw-r--r--src/gsm/gsm29118.c4
-rw-r--r--src/gsm/gsm29205.c4
-rw-r--r--src/gsm/gsm44021.c303
-rw-r--r--src/gsm/gsm44068.c121
-rw-r--r--src/gsm/gsm48.c204
-rw-r--r--src/gsm/gsm48_arfcn_range_encode.c4
-rw-r--r--src/gsm/gsm48_ie.c59
-rw-r--r--src/gsm/gsm48_rest_octets.c76
-rw-r--r--src/gsm/gsm_utils.c70
-rw-r--r--src/gsm/gsup.c125
-rw-r--r--src/gsm/ipa.c35
-rw-r--r--src/gsm/iuup.c1064
-rw-r--r--src/gsm/kasumi.c4
-rw-r--r--src/gsm/kdf.c163
-rw-r--r--src/gsm/kdf/common.h101
-rw-r--r--src/gsm/kdf/crypto.h470
-rw-r--r--src/gsm/kdf/sha1-internal.c307
-rw-r--r--src/gsm/kdf/sha1.c162
-rw-r--r--src/gsm/kdf/sha1.h33
-rw-r--r--src/gsm/kdf/sha1_i.h29
-rw-r--r--src/gsm/kdf/sha256-internal.c231
-rw-r--r--src/gsm/kdf/sha256.c156
-rw-r--r--src/gsm/kdf/sha256.h30
-rw-r--r--src/gsm/kdf/sha256_i.h31
-rw-r--r--src/gsm/lapdm.c315
-rw-r--r--src/gsm/libosmogsm.map110
-rw-r--r--src/gsm/milenage/milenage.c8
-rw-r--r--src/gsm/mncc.c2
-rw-r--r--src/gsm/rlp.c243
-rw-r--r--src/gsm/rsl.c85
-rw-r--r--src/gsm/rxlev_stat.c4
-rw-r--r--src/gsm/sysinfo.c2
-rw-r--r--src/gsm/tlv_parser.c16
-rw-r--r--src/gsm/tuak/KeccakP-1600-3gpp.c176
-rw-r--r--src/gsm/tuak/KeccakP-1600-3gpp.h25
-rw-r--r--src/gsm/tuak/tuak.c372
-rw-r--r--src/gsm/tuak/tuak.h33
-rw-r--r--src/isdn/Makefile.am26
-rw-r--r--src/isdn/i460_mux.c (renamed from src/gsm/i460_mux.c)36
-rw-r--r--src/isdn/lapd_core.c (renamed from src/gsm/lapd_core.c)284
-rw-r--r--src/isdn/libosmoisdn.map52
-rw-r--r--src/isdn/v110.c590
-rw-r--r--src/isdn/v110_ta.c881
-rw-r--r--src/pseudotalloc/Makefile.am3
-rw-r--r--src/pseudotalloc/pseudotalloc.c22
-rw-r--r--src/pseudotalloc/talloc.h3
-rw-r--r--src/sim/Makefile.am11
-rw-r--r--src/sim/card_fs_hpsim.c4
-rw-r--r--src/sim/card_fs_isim.c4
-rw-r--r--src/sim/card_fs_sim.c4
-rw-r--r--src/sim/card_fs_tetra.c4
-rw-r--r--src/sim/card_fs_uicc.c4
-rw-r--r--src/sim/card_fs_usim.c4
-rw-r--r--src/sim/class_tables.c94
-rw-r--r--src/sim/core.c4
-rw-r--r--src/sim/reader.c27
-rw-r--r--src/sim/reader_pcsc.c34
-rw-r--r--src/stat_item.c407
-rw-r--r--src/usb/Makefile.am11
-rw-r--r--src/usb/osmo_libusb.c26
-rw-r--r--src/vty/Makefile.am8
-rw-r--r--src/vty/command.c168
-rw-r--r--src/vty/cpu_sched_vty.c17
-rw-r--r--src/vty/fsm_vty.c8
-rw-r--r--src/vty/logging_vty.c69
-rw-r--r--src/vty/stats_vty.c213
-rw-r--r--src/vty/talloc_ctx_vty.c11
-rw-r--r--src/vty/tdef_vty.c9
-rw-r--r--src/vty/telnet_interface.c59
-rw-r--r--src/vty/utils.c114
-rw-r--r--src/vty/vector.c5
-rw-r--r--src/vty/vty.c55
-rw-r--r--tests/Makefile.am526
-rw-r--r--tests/a5/a5_test.c4
-rw-r--r--tests/abis/abis_test.c55
-rw-r--r--tests/abis/abis_test.ok1
-rw-r--r--tests/auth/tuak_test.c309
-rw-r--r--tests/auth/tuak_test.ok48
-rw-r--r--tests/auth/xor2g_test.c77
-rw-r--r--tests/auth/xor2g_test.ok6
-rw-r--r--tests/base64/base64_test.c55
-rw-r--r--tests/base64/base64_test.ok3
-rw-r--r--tests/bitgen/bitgen_test.c6
-rw-r--r--tests/bitvec/bitvec_test.c11
-rw-r--r--tests/bitvec/bitvec_test.ok32
-rw-r--r--tests/bsslap/bsslap_test.c4
-rw-r--r--tests/bssmap_le/bssmap_le_test.c36
-rw-r--r--tests/bssmap_le/bssmap_le_test.ok1
-rw-r--r--tests/codec/codec_ecu_fr_test.c6
-rw-r--r--tests/codec/codec_ecu_fr_test.ok84
-rw-r--r--tests/coding/coding_test.c194
-rw-r--r--tests/coding/coding_test.ok246
-rw-r--r--tests/conv/conv_gsm0503_test.ok40
-rw-r--r--tests/ctrl/ctrl_test.c13
-rw-r--r--tests/dtx/dtx_gsm0503_test.c168
-rw-r--r--tests/dtx/dtx_gsm0503_test.ok34
-rw-r--r--tests/fr/fr_test.c6
-rw-r--r--tests/fsm/fsm_dealloc_test.c6
-rw-r--r--tests/fsm/fsm_test.c96
-rw-r--r--tests/fsm/fsm_test.err52
-rw-r--r--tests/gad/gad_test.c8
-rw-r--r--tests/gb/gprs_bssgp_rim_test.c40
-rw-r--r--tests/gb/gprs_bssgp_test.c4
-rw-r--r--tests/gb/gprs_ns2_test.c133
-rw-r--r--tests/gb/gprs_ns2_test.err139
-rw-r--r--tests/gb/gprs_ns2_test.ok12
-rw-r--r--tests/gb/gprs_ns2_vty.vty26
-rw-r--r--tests/gb/gprs_ns_test.c16
-rw-r--r--tests/gb/gprs_ns_test.err147
-rw-r--r--tests/gsm0408/gsm0408_test.c365
-rw-r--r--tests/gsm0408/gsm0408_test.err6
-rw-r--r--tests/gsm0408/gsm0408_test.ok151
-rw-r--r--tests/gsm0502/gsm0502_test.c26
-rw-r--r--tests/gsm0808/gsm0808_test.c447
-rw-r--r--tests/gsm0808/gsm0808_test.ok805
-rw-r--r--tests/gsm23003/gsm23003_test.c18
-rw-r--r--tests/gsm23236/gsm23236_test.c16
-rw-r--r--tests/gsm29205/gsm29205_test.c6
-rw-r--r--tests/gsm44021/frame_csd_test.c93
-rw-r--r--tests/gsm44021/frame_csd_test.ok31
-rw-r--r--tests/gsm48/rest_octets_test.c8
-rw-r--r--tests/gsup/gsup_test.c20
-rw-r--r--tests/gsup/gsup_test.err2
-rw-r--r--tests/i460_mux/i460_mux_test.c2
-rw-r--r--tests/it_q/it_q_test.c39
-rw-r--r--tests/it_q/it_q_test.ok7
-rw-r--r--tests/iuup/iuup_test.c767
-rw-r--r--tests/iuup/iuup_test.err55
-rw-r--r--tests/iuup/iuup_test.ok61
-rw-r--r--tests/lapd/lapd_test.c18
-rw-r--r--tests/logging/logging_test.c17
-rw-r--r--tests/logging/logging_test.err2
-rw-r--r--tests/logging/logging_test_gsmtap.c64
-rw-r--r--tests/logging/logging_test_gsmtap.err200
-rw-r--r--tests/logging/logging_vty_test.c17
-rw-r--r--tests/logging/logging_vty_test.vty104
-rw-r--r--tests/loggingrb/logging_test.err5
-rw-r--r--tests/loggingrb/loggingrb_test.c4
-rw-r--r--tests/msgb/msgb_test.c34
-rw-r--r--tests/msgfile/msgfile_test.c4
-rw-r--r--tests/oap/oap_test.c4
-rw-r--r--tests/osmo-auc-gen/osmo-auc-gen_test.ok68
-rw-r--r--tests/osmo_io/osmo_io_test.c178
-rw-r--r--tests/osmo_io/osmo_io_test.err0
-rw-r--r--tests/osmo_io/osmo_io_test.ok9
-rw-r--r--tests/rlp/rlp_test.c217
-rw-r--r--tests/rlp/rlp_test.ok35
-rw-r--r--tests/sercomm/sercomm_test.c4
-rw-r--r--tests/sim/sim_test.c4
-rw-r--r--tests/sms/sms_test.c6
-rw-r--r--tests/smscb/cbsp_test.c108
-rw-r--r--tests/smscb/cbsp_test.ok2
-rw-r--r--tests/smscb/gsm0341_test.c14
-rw-r--r--tests/smscb/gsm0341_test.ok1
-rw-r--r--tests/smscb/smscb_test.c4
-rw-r--r--tests/sockaddr_str/sockaddr_str_test.c29
-rw-r--r--tests/sockaddr_str/sockaddr_str_test.ok228
-rw-r--r--tests/socket/socket_sctp_test.c9
-rw-r--r--tests/socket/socket_sctp_test.err8
-rw-r--r--tests/socket/socket_test.c85
-rw-r--r--tests/socket/socket_test.ok3
-rw-r--r--tests/soft_uart/soft_uart_test.c658
-rw-r--r--tests/soft_uart/soft_uart_test.ok278
-rw-r--r--tests/stats/stats_test.c361
-rw-r--r--tests/stats/stats_test.err94
-rw-r--r--tests/stats/stats_vty_test.c88
-rw-r--r--tests/stats/stats_vty_test.vty217
-rw-r--r--tests/strrb/strrb_test.c4
-rw-r--r--tests/tdef/tdef_test.c67
-rw-r--r--tests/tdef/tdef_test.err25
-rw-r--r--tests/tdef/tdef_test.ok24
-rw-r--r--tests/tdef/tdef_vty_config_root_test.c (renamed from tests/tdef/tdef_vty_test_config_root.c)14
-rw-r--r--tests/tdef/tdef_vty_config_root_test.vty (renamed from tests/tdef/tdef_vty_test_config_root.vty)0
-rw-r--r--tests/tdef/tdef_vty_config_subnode_test.c (renamed from tests/tdef/tdef_vty_test_config_subnode.c)12
-rw-r--r--tests/tdef/tdef_vty_config_subnode_test.vty (renamed from tests/tdef/tdef_vty_test_config_subnode.vty)0
-rw-r--r--tests/tdef/tdef_vty_dynamic_test.c (renamed from tests/tdef/tdef_vty_test_dynamic.c)10
-rw-r--r--tests/tdef/tdef_vty_dynamic_test.vty (renamed from tests/tdef/tdef_vty_test_dynamic.vty)0
-rw-r--r--tests/testsuite.at123
-rw-r--r--tests/time_cc/time_cc_test.c768
-rw-r--r--tests/time_cc/time_cc_test.ok328
-rw-r--r--tests/timer/clk_override_test.c4
-rw-r--r--tests/timer/timer_test.c6
-rw-r--r--tests/tlv/tlv_test.c35
-rw-r--r--tests/use_count/use_count_test.c8
-rw-r--r--tests/use_count/use_count_test.err10
-rw-r--r--tests/ussd/ussd_test.c4
-rw-r--r--tests/utils/utils_test.c346
-rw-r--r--tests/utils/utils_test.ok212
-rw-r--r--tests/v110/frame_test.c54
-rw-r--r--tests/v110/frame_test.ok20
-rw-r--r--tests/v110/ra1_test.c74
-rw-r--r--tests/v110/ra1_test.ok216
-rw-r--r--tests/v110/ta_test.c459
-rw-r--r--tests/v110/ta_test.err270
-rw-r--r--tests/vty/vty_test.c83
-rw-r--r--tests/vty/vty_test.err10
-rw-r--r--tests/vty/vty_test.ok48
-rw-r--r--tests/vty/vty_transcript_test.c80
-rw-r--r--tests/vty/vty_transcript_test.vty30
-rw-r--r--tests/write_queue/wqueue_test.c52
-rw-r--r--utils/Makefile.am23
-rw-r--r--utils/conv_codes_gsm.py109
-rw-r--r--utils/conv_gen.py4
-rw-r--r--utils/gsmtap-logsend.c139
-rw-r--r--utils/osmo-aka-verify.c6
-rw-r--r--utils/osmo-arfcn.c6
-rw-r--r--utils/osmo-auc-gen.c82
-rw-r--r--utils/osmo-config-merge.c4
-rw-r--r--utils/osmo-ns-dummy-vty.c2
-rw-r--r--utils/osmo-ns-dummy.c32
-rw-r--r--utils/osmo-sim-test.c4
-rw-r--r--utils/osmo-stat-dummy/Makefile.am10
-rw-r--r--utils/osmo-stat-dummy/README.md12
-rw-r--r--utils/osmo-stat-dummy/osmo-stat-dummy.c335
-rw-r--r--utils/osmo-stat-dummy/osmo-stat-dummy.cfg40
-rw-r--r--utils/osmo-stat-dummy/osmo-stat-dummy.html59
550 files changed, 45378 insertions, 6791 deletions
diff --git a/.checkpatch.conf b/.checkpatch.conf
new file mode 100644
index 00000000..46bf0eb1
--- /dev/null
+++ b/.checkpatch.conf
@@ -0,0 +1 @@
+--exclude ^src/gsm/tuak/KeccakP-1600-3gpp\.(c|h)$
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..7592debf
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+open_collective: osmocom
diff --git a/.gitignore b/.gitignore
index f5e794ac..264e739e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,10 +37,13 @@ libosmocore-*.tar.*
Doxyfile.core
Doxyfile.ctrl
Doxyfile.gsm
+Doxyfile.isdn
Doxyfile.vty
Doxyfile.codec
Doxyfile.coding
Doxyfile.gb
+Doxyfile.sim
+Doxyfile.usb
debian/autoreconf.after
debian/autoreconf.before
@@ -69,7 +72,9 @@ tests/*/*_test
utils/osmo-arfcn
utils/osmo-auc-gen
utils/osmo-config-merge
+utils/osmo-gsmtap-logsend
utils/osmo-sim-test
+utils/osmo-aka-verify
doc/codec
doc/coding
@@ -79,17 +84,21 @@ doc/vty/latex
doc/vty/html
doc/vty/doxygen_sqlite3.db
doc/gsm
+doc/isdn
doc/gb
+doc/sim
+doc/usb
doc/html.tar
doc/*.tag
doc/*.tag.prep
tags
-src/crc*gen.c
+src/core/crc*gen.c
src/gsm/gsm0503_conv.c
src/gsm/*_gen.c
include/osmocom/core/crc*gen.h
include/osmocom/core/bit*gen.h
+include/osmocom/core/socket_compat.h
include/osmocom/gsm/gsm0503.h
tests/conv/gsm0503_test_vectors.c
@@ -102,3 +111,5 @@ libosmocore-*-coverage*
coverage-cobertura.xml
contrib/libosmocore.spec
+/utils/osmo-ns-dummy
+utils/osmo-stat-dummy/osmo-stat-dummy
diff --git a/Doxyfile.codec.in b/Doxyfile.codec.in
index 88f0d27a..40be69fe 100644
--- a/Doxyfile.codec.in
+++ b/Doxyfile.codec.in
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Doxyfile.coding.in b/Doxyfile.coding.in
index d0e99425..50355de4 100644
--- a/Doxyfile.coding.in
+++ b/Doxyfile.coding.in
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Doxyfile.core.in b/Doxyfile.core.in
index c2bfcd5d..fb4ceffb 100644
--- a/Doxyfile.core.in
+++ b/Doxyfile.core.in
@@ -610,7 +610,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = @srcdir@/include/osmocom/core @srcdir@/src
+INPUT = @srcdir@/include/osmocom/core @srcdir@/src/core
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+TAGFILES = doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Doxyfile.ctrl.in b/Doxyfile.ctrl.in
index 5612a741..6d101dfc 100644
--- a/Doxyfile.ctrl.in
+++ b/Doxyfile.ctrl.in
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmogb.tag=../../gb/html
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmogb.tag=../../gb/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Doxyfile.gb.in b/Doxyfile.gb.in
index b3b2128d..da8e7705 100644
--- a/Doxyfile.gb.in
+++ b/Doxyfile.gb.in
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Doxyfile.gsm.in b/Doxyfile.gsm.in
index fd893907..c04a1f6a 100644
--- a/Doxyfile.gsm.in
+++ b/Doxyfile.gsm.in
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Doxyfile.isdn.in b/Doxyfile.isdn.in
new file mode 100644
index 00000000..9476ab2a
--- /dev/null
+++ b/Doxyfile.isdn.in
@@ -0,0 +1,1716 @@
+# Doxyfile 1.7.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = libosmoisdn
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Osmocom ISDN library"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/isdn
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit command for a brief description.)
+
+QT_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = @srcdir@/include/osmocom/isdn @srcdir@/src/isdn
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+# IMAGE_PATH = images/
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 1
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE = doc/libosmoisdn.tag
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH = /usr/bin/dot
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/Doxyfile.sim.in b/Doxyfile.sim.in
new file mode 100644
index 00000000..0c9dd73d
--- /dev/null
+++ b/Doxyfile.sim.in
@@ -0,0 +1,1716 @@
+# Doxyfile 1.7.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = libosmosim
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Osmocom SIM library"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/sim
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit command for a brief description.)
+
+QT_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = @srcdir@/include/osmocom/sim @srcdir@/src/sim
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+# IMAGE_PATH = images/
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 1
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE = doc/libosmosim.tag
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH = /usr/bin/dot
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/Doxyfile.usb.in b/Doxyfile.usb.in
new file mode 100644
index 00000000..7037713d
--- /dev/null
+++ b/Doxyfile.usb.in
@@ -0,0 +1,1716 @@
+# Doxyfile 1.7.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = libosmousb
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Osmocom USB library"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/usb
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit command for a brief description.)
+
+QT_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = @srcdir@/include/osmocom/usb @srcdir@/src/usb
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+# IMAGE_PATH = images/
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 1
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE = doc/libosmousb.tag
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH = /usr/bin/dot
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/Doxyfile.vty.in b/Doxyfile.vty.in
index 9f56fd7f..8d9fc69e 100644
--- a/Doxyfile.vty.in
+++ b/Doxyfile.vty.in
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Makefile.am b/Makefile.am
index ab2aad92..8a8f9636 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,12 +1,18 @@
ACLOCAL_AMFLAGS = -I m4
-AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
-SUBDIRS = include src src/vty src/codec src/gsm src/coding src/gb src/ctrl src/sim src/pseudotalloc src/usb utils tapset tests
+AM_CPPFLAGS = -I$(top_srcdir)/include
+SUBDIRS = \
+ include \
+ src \
+ utils \
+ tapset \
+ tests \
+ $(NULL)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libosmocore.pc libosmocodec.pc libosmovty.pc libosmogsm.pc \
libosmogb.pc libosmoctrl.pc libosmocoding.pc libosmosim.pc \
- libosmousb.pc
+ libosmousb.pc libosmoisdn.pc
aclocaldir = $(datadir)/aclocal
dist_aclocal_DATA = m4/osmo_ac_code_coverage.m4 \
@@ -41,6 +47,7 @@ EXTRA_DIST = \
HTML = \
$(top_builddir)/doc/core/html/index.html \
$(top_builddir)/doc/gsm/html/index.html \
+ $(top_builddir)/doc/isdn/html/index.html \
$(top_builddir)/doc/vty/html/index.html \
$(top_builddir)/doc/codec/html/index.html \
$(top_builddir)/doc/coding/html/index.html \
@@ -75,8 +82,8 @@ apidoc: $(HTML)
$(top_builddir)/doc/libosmocore.tag.prep: $(top_builddir)/Doxyfile.core \
$(top_srcdir)/include/osmocom/core/*.h \
- $(top_srcdir)/src/*.[hc] \
- $(top_srcdir)/src/crcXXgen.c.tpl \
+ $(top_srcdir)/src/core/*.[hc] \
+ $(top_srcdir)/src/core/crcXXgen.c.tpl \
$(top_srcdir)/src/pseudotalloc/*.[hc]
rm -rf $(top_builddir)/doc/core; mkdir -p $(top_builddir)/doc/core
rm -rf $(top_builddir)/doc/libosmocore.map
@@ -94,6 +101,14 @@ $(top_builddir)/doc/libosmogsm.tag.prep: $(top_builddir)/Doxyfile.gsm \
-$(DOXYGEN) $(top_builddir)/Doxyfile.gsm
touch "$@"
+$(top_builddir)/doc/libosmoisdn.tag.prep: $(top_builddir)/Doxyfile.isdn \
+ $(top_srcdir)/include/osmocom/isdn/*.h \
+ $(top_srcdir)/src/isdn/*.c
+ rm -rf $(top_builddir)/doc/isdn; mkdir -p $(top_builddir)/doc/isdn
+ rm -rf $(top_builddir)/doc/libosmoisdn.map
+ -$(DOXYGEN) $(top_builddir)/Doxyfile.isdn
+ touch "$@"
+
# Don't delete the entire doc/vty, it contains example.xml and vtydoc.xsd (OS#3986)
$(top_builddir)/doc/libosmovty.tag.prep: $(top_builddir)/Doxyfile.vty \
$(top_srcdir)/include/osmocom/vty/*.h \
@@ -135,6 +150,22 @@ $(top_builddir)/doc/libosmogb.tag.prep: $(top_builddir)/Doxyfile.gb \
-$(DOXYGEN) $(top_builddir)/Doxyfile.gb
touch "$@"
+$(top_builddir)/doc/libosmosim.tag.prep: $(top_builddir)/Doxyfile.sim \
+ $(top_srcdir)/include/osmocom/sim/*.h \
+ $(top_srcdir)/src/sim/*.[hc]
+ rm -rf $(top_builddir)/doc/sim; mkdir -p $(top_builddir)/doc/sim
+ rm -rf $(top_builddir)/doc/libosmosim.map
+ -$(DOXYGEN) $(top_builddir)/Doxyfile.sim
+ touch "$@"
+
+$(top_builddir)/doc/libosmousb.tag.prep: $(top_builddir)/Doxyfile.usb \
+ $(top_srcdir)/include/osmocom/usb/*.h \
+ $(top_srcdir)/src/usb/*.[hc]
+ rm -rf $(top_builddir)/doc/usb; mkdir -p $(top_builddir)/doc/usb
+ rm -rf $(top_builddir)/doc/libosmousb.map
+ -$(DOXYGEN) $(top_builddir)/Doxyfile.usb
+ touch "$@"
+
# Build for real once all depending .tag files are in place.
# Depend on the own .tag.prep file to avoid concurrent builds of the same doc
# tree, and to also depend on all the source files listed above.
@@ -149,83 +180,150 @@ $(top_builddir)/doc/libosmogb.tag.prep: $(top_builddir)/Doxyfile.gb \
$(top_builddir)/doc/core/html/index.html: $(top_builddir)/doc/libosmocore.tag.prep \
$(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmocoding.tag.prep \
$(top_builddir)/doc/libosmoctrl.tag.prep \
- $(top_builddir)/doc/libosmogb.tag.prep
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/core; mkdir -p $(top_builddir)/doc/core
$(DOXYGEN) Doxyfile.core
$(top_builddir)/doc/gsm/html/index.html: $(top_builddir)/doc/libosmogsm.tag.prep \
$(top_builddir)/doc/libosmocore.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmocoding.tag.prep \
$(top_builddir)/doc/libosmoctrl.tag.prep \
- $(top_builddir)/doc/libosmogb.tag.prep
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/gsm; mkdir -p $(top_builddir)/doc/gsm
$(DOXYGEN) Doxyfile.gsm
+$(top_builddir)/doc/isdn/html/index.html: $(top_builddir)/doc/libosmoisdn.tag.prep \
+ $(top_builddir)/doc/libosmocore.tag.prep \
+ $(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmovty.tag.prep \
+ $(top_builddir)/doc/libosmocodec.tag.prep \
+ $(top_builddir)/doc/libosmocoding.tag.prep \
+ $(top_builddir)/doc/libosmoctrl.tag.prep \
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
+ rm -rf $(top_builddir)/doc/isdn; mkdir -p $(top_builddir)/doc/isdn
+ $(DOXYGEN) Doxyfile.isdn
+
# Don't delete the entire doc/vty, it contains example.xml and vtydoc.xsd (OS#3986)
$(top_builddir)/doc/vty/html/index.html: $(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocore.tag.prep \
$(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmocoding.tag.prep \
$(top_builddir)/doc/libosmoctrl.tag.prep \
- $(top_builddir)/doc/libosmogb.tag.prep
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/vty/html $(top_builddir)/doc/vty/latex
$(DOXYGEN) Doxyfile.vty
$(top_builddir)/doc/codec/html/index.html: $(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmocore.tag.prep \
$(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocoding.tag.prep \
$(top_builddir)/doc/libosmoctrl.tag.prep \
- $(top_builddir)/doc/libosmogb.tag.prep
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/codec; mkdir -p $(top_builddir)/doc/codec
$(DOXYGEN) Doxyfile.codec
$(top_builddir)/doc/coding/html/index.html: $(top_builddir)/doc/libosmocoding.tag.prep \
$(top_builddir)/doc/libosmocore.tag.prep \
$(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmoctrl.tag.prep \
- $(top_builddir)/doc/libosmogb.tag.prep
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/coding; mkdir -p $(top_builddir)/doc/coding
$(DOXYGEN) Doxyfile.coding
$(top_builddir)/doc/ctrl/html/index.html: $(top_builddir)/doc/libosmoctrl.tag.prep \
$(top_builddir)/doc/libosmocore.tag.prep \
$(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmocoding.tag.prep \
- $(top_builddir)/doc/libosmogb.tag.prep
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/ctrl; mkdir -p $(top_builddir)/doc/ctrl
$(DOXYGEN) Doxyfile.ctrl
$(top_builddir)/doc/gb/html/index.html: $(top_builddir)/doc/libosmogb.tag.prep \
$(top_builddir)/doc/libosmocore.tag.prep \
$(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmocoding.tag.prep \
- $(top_builddir)/doc/libosmoctrl.tag.prep
+ $(top_builddir)/doc/libosmoctrl.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/gb; mkdir -p $(top_builddir)/doc/gb
$(DOXYGEN) Doxyfile.gb
+$(top_builddir)/doc/sim/html/index.html: $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmocore.tag.prep \
+ $(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
+ $(top_builddir)/doc/libosmovty.tag.prep \
+ $(top_builddir)/doc/libosmocodec.tag.prep \
+ $(top_builddir)/doc/libosmocoding.tag.prep \
+ $(top_builddir)/doc/libosmoctrl.tag.prep \
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
+ rm -rf $(top_builddir)/doc/sim; mkdir -p $(top_builddir)/doc/sim
+ $(DOXYGEN) Doxyfile.sim
+
+$(top_builddir)/doc/usb/html/index.html: $(top_builddir)/doc/libosmousb.tag.prep \
+ $(top_builddir)/doc/libosmocore.tag.prep \
+ $(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
+ $(top_builddir)/doc/libosmovty.tag.prep \
+ $(top_builddir)/doc/libosmocodec.tag.prep \
+ $(top_builddir)/doc/libosmocoding.tag.prep \
+ $(top_builddir)/doc/libosmoctrl.tag.prep \
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep
+ rm -rf $(top_builddir)/doc/usb; mkdir -p $(top_builddir)/doc/usb
+ $(DOXYGEN) Doxyfile.usb
+
if HAVE_DOXYGEN
install-data-hook:
cd $(DESTDIR)$(htmldir) && tar xf html.tar && rm -f html.tar
uninstall-hook:
- cd $(DESTDIR)$(htmldir) && rm -rf {core,gsm,vty,codec,coding,ctrl,gb}
-
-DX_CLEAN = doc/{core,gsm,vty,codec,coding,ctrl,gb}/html/search/* doc/{core,gsm,vty,codec,coding,ctrl,gb}/{html,latex}/* doc/html.tar doc/{core,gsm,vty,codec,coding,ctrl,gb}/doxygen_sqlite3.db doc/*.tag doc/*.tag.prep
+ cd $(DESTDIR)$(htmldir) && rm -rf {core,gsm,isdn,vty,codec,coding,ctrl,gb,sim,usb}
+
+DX_CLEAN = \
+ doc/{core,gsm,isdn,vty,codec,coding,ctrl,gb,sim,usb}/html/search/* \
+ doc/{core,gsm,isdn,vty,codec,coding,ctrl,gb,sim,usb}/{html,latex}/* \
+ doc/{core,gsm,isdn,vty,codec,coding,ctrl,gb,sim,usb}/doxygen_sqlite3.db \
+ doc/*.tag.prep \
+ doc/*.tag \
+ doc/html.tar \
+ $(NULL)
endif
MOSTLYCLEANFILES = $(DX_CLEAN)
diff --git a/README.md b/README.md
index 97573bc2..ed9bcd6b 100644
--- a/README.md
+++ b/README.md
@@ -41,9 +41,9 @@ GIT Repository
You can clone from the official libosmocore.git repository using
- git clone git://git.osmocom.org/libosmocore.git
+ git clone https://gitea.osmocom.org/osmocom/libosmocore
-There is a cgit interface at <https://git.osmocom.org/libosmocore/>
+There is a web interface at <https://gitea.osmocom.org/osmocom/libosmocore>
Documentation
-------------
diff --git a/TODO-RELEASE b/TODO-RELEASE
index c5d4ee80..ce041627 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -7,7 +7,18 @@
# 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
-libosmovty _LAST_OSMOVTY_NODE Raise _LAST_OSMOVTY_NODE by introducing some RESERVED*_NODE
-libosmogsm gsm0808_old_bss_to_new_bss_info ABI break (struct changes size), gsm0808_old_bss_to_new_bss_info_att_tlvdef symbol added
-libosmosim osim_card_hdl ABI + API breakage due to new struct members
-libosmocore osmo_tdef_fsm_inst_state_chg change default_timeout arg from unsigned long to long type (API breakage, not ABI)
+core ADD osmo_sock_multiaddr_{add,del}_local_addr()
+core ADD osmo_sock_multiaddr_get_ip_and_port(), osmo_multiaddr_ip_and_port_snprintf(), osmo_sock_multiaddr_get_name_buf()
+core ADD osmo_sock_sctp_get_peer_addr_info()
+core ADD gsmtap_inst_fd2() core, DEPRECATE gsmtap_inst_fd()
+core ADD osmo_sockaddr_str_from_osa() osmo_sockaddr_str_to_osa()
+core behavior change osmo_tdef_fsm_inst_state_chg(): allow millisecond precision
+core ABI change osmo_io_ops now contains a struct of structs, not union of structs
+core ABI change osmo_iofd_set_ioops() now returns a value (error code)
+isdn ABI change add states and flags for external T200 handling
+isdn ADD initial implementation of the V.110 Terminal Adapter
+gsm ABI change add T200 timer states to lapdm_datalink
+gsm ABI change add UI queue to struct lapdm_datalink
+gsm ADD gsup.h: struct osmo_gsup_pdp_info fields pdp_type_nr, pdp_type_org, deprecate pdp_type.
+gsm ABI change gsup.h: Add field pdp_address in struct osmo_gsup_pdp_info shifts the struct, and in turn fields in struct osmo_gsup_message.
+gsm ABI change gsup.h: Add field pco in struct osmo_gsup_message. Length of the struct osmo_gsup_message increase.
diff --git a/configure.ac b/configure.ac
index f5af0d02..6c7126f5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -53,18 +53,23 @@ case $host in
*)
LTLDFLAGS_OSMOGB='-Wl,--version-script=$(srcdir)/libosmogb.map'
LTLDFLAGS_OSMOGSM='-Wl,--version-script=$(srcdir)/libosmogsm.map'
+ LTLDFLAGS_OSMOISDN='-Wl,--version-script=$(srcdir)/libosmoisdn.map'
LTLDFLAGS_OSMOCODING='-Wl,--version-script=$(srcdir)/libosmocoding.map'
+ LTLDFLAGS_OSMOCORE='-Wl,--version-script=$(srcdir)/libosmocore.map'
LTLDFLAGS_OSMOCTRL='-Wl,--version-script=$(srcdir)/libosmoctrl.map'
;;
esac
AC_SUBST(LTLDFLAGS_OSMOGB)
AC_SUBST(LTLDFLAGS_OSMOGSM)
+AC_SUBST(LTLDFLAGS_OSMOISDN)
AC_SUBST(LTLDFLAGS_OSMOCODING)
+AC_SUBST(LTLDFLAGS_OSMOCORE)
AC_SUBST(LTLDFLAGS_OSMOCTRL)
dnl checks for header files
AC_HEADER_STDC
AC_CHECK_HEADERS(execinfo.h poll.h sys/select.h sys/socket.h sys/signalfd.h sys/eventfd.h sys/timerfd.h syslog.h ctype.h netinet/tcp.h netinet/in.h)
+AC_CHECK_DECL(HAVE_SYS_SOCKET_H, AC_SUBST(HAVE_SYS_SOCKET_H, 1), AC_SUBST(HAVE_SYS_SOCKET_H, 0))
# for src/conv.c
AC_FUNC_ALLOCA
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DLOPEN="$LIBS";LIBS=""])
@@ -78,6 +83,24 @@ AC_SUBST(BACKTRACE_LIB)
# check for pthread (PTHREAD_CFLAGS, PTHREAD_LIBS)
AX_PTHREAD
+AC_MSG_CHECKING(for pthread_getname_np(pthread_t, char*, size_t))
+saved_CFLAGS="$CFLAGS"
+saved_LIBS="$LIBS"
+CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+LIBS="$LIBS $PTHREAD_LIBS"
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [#define _GNU_SOURCE
+ #include <pthread.h>],
+ [pthread_getname_np(pthread_self(),"example",0)])],
+ [AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_PTHREAD_GETNAME_NP,1,
+ [Have function pthread_setname_np(const char*)])],
+ [AC_MSG_RESULT(no)])
+CFLAGS="$saved_CFLAGS"
+LIBS="$saved_LIBS"
+
+
# check for old glibc < 2.17 to get clock_gettime
AC_SEARCH_LIBS([clock_gettime], [rt posix4],
[AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [Define if clock_gettime is available])
@@ -150,7 +173,21 @@ ARG_ENABLE_DETECT_TLS_GCC_ARM_BUG
dnl Generate the output
AC_CONFIG_HEADER(config.h)
-PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
+PKG_CHECK_MODULES(TALLOC, [talloc >= 2.1.0])
+
+AC_ARG_ENABLE([uring], [AS_HELP_STRING([--disable-uring], [Build without io_uring support])],
+ [
+ ENABLE_URING=$enableval
+ ],
+ [
+ ENABLE_URING="yes"
+ ])
+AS_IF([test "x$ENABLE_URING" = "xyes"], [
+ PKG_CHECK_MODULES(URING, [liburing >= 0.7])
+ AC_DEFINE([HAVE_URING],[1],[Build with io_uring support])
+])
+AM_CONDITIONAL(ENABLE_URING, test "x$ENABLE_URING" = "xyes")
+AC_SUBST(ENABLE_URING)
AC_ARG_ENABLE([pcsc], [AS_HELP_STRING([--disable-pcsc], [Build without PC/SC support])],
[
@@ -213,7 +250,7 @@ AC_ARG_ENABLE([libmnl],
)],
[mnl=$enableval], [mnl="yes"])
AS_IF([test "x$mnl" = "xyes"], [
- PKG_CHECK_MODULES(LIBMNL, libmnl)
+ PKG_CHECK_MODULES(LIBMNL, libmnl, [AC_SUBST(LIBMNL_PC, [libmnl])])
AC_DEFINE([ENABLE_LIBMNL], [1], [Enable netlink socket support via libmnl])
])
AM_CONDITIONAL(ENABLE_LIBMNL, test "x$mnl" = "xyes")
@@ -223,22 +260,33 @@ AC_ARG_ENABLE([libsctp], [AS_HELP_STRING([--disable-libsctp], [Do not enable soc
[ENABLE_LIBSCTP=$enableval], [ENABLE_LIBSCTP="yes"])
AM_CONDITIONAL(ENABLE_LIBSCTP, test x"$ENABLE_LIBSCTP" = x"yes")
AS_IF([test "x$ENABLE_LIBSCTP" = "xyes"], [
- old_LIBS=$LIBS
- AC_SEARCH_LIBS([sctp_bindx], [sctp], [
- AC_DEFINE(HAVE_LIBSCTP, 1, [Define 1 to enable SCTP support])
- AC_SUBST(HAVE_LIBSCTP, [1])
- if test -n "$ac_lib"; then
- AC_SUBST(LIBSCTP_LIBS, [-l$ac_lib])
- fi
- ], [
- AC_MSG_ERROR([sctp_bindx not found in searched libs])])
- LIBS=$old_LIBS
+ AC_DEFINE(HAVE_LIBSCTP, 1, [Define 1 to enable SCTP support])
+ # Attempt finding .pc, otherwise set manually (<1.0.17 have no .pc file)
+ PKG_CHECK_MODULES(LIBSCTP, libsctp,
+ [AC_SUBST(LIBSCTP_PC, [libsctp])],
+ [
+ AC_MSG_NOTICE([libsctp.pc not found (building against <1.0.17 ?), attempting manual lib lookup])
+ old_LIBS=$LIBS
+ AC_SEARCH_LIBS([sctp_bindx], [sctp], [
+ AC_SUBST(HAVE_LIBSCTP, [1])
+ if test -n "$ac_lib"; then
+ AC_SUBST(LIBSCTP_LIBS, [-l$ac_lib])
+ fi
+ ], [
+ AC_MSG_ERROR([sctp_bindx not found in searched libs])])
+ LIBS=$old_LIBS
+ ])
])
AC_ARG_ENABLE([sctp-tests], [AS_HELP_STRING([--disable-sctp-tests], [Do not run socket tests requiring system SCTP support])],
[ENABLE_SCTP_TESTS=$enableval], [ENABLE_SCTP_TESTS="yes"])
AM_CONDITIONAL(ENABLE_SCTP_TESTS, test x"$ENABLE_SCTP_TESTS" = x"yes")
+AC_ARG_ENABLE([uring-tests], [AS_HELP_STRING([--disable-uring-tests], [Do not run io_uring tests])],
+ [ENABLE_URING_TESTS=$enableval], [ENABLE_URING_TESTS="yes"])
+AM_CONDITIONAL(ENABLE_URING_TESTS, test x"$ENABLE_URING_TESTS" = x"yes")
+AC_SUBST(ENABLE_URING_TESTS)
+
AC_ARG_ENABLE(plugin,
[AS_HELP_STRING(
[--disable-plugin],
@@ -268,13 +316,13 @@ fi
AC_ARG_ENABLE(bsc_fd_check,
[AS_HELP_STRING(
- [--enable-bsc-fd-check],
+ [--enable-ofd-check],
[Instrument osmo_fd_register to check that the fd is registered]
)],
[fd_check=$enableval], [fd_check="no"])
-if test x"$fd_check" = x"no"
+if test x"$fd_check" = x"yes"
then
- AC_DEFINE([OSMO_FD_CHECK],[1],[Instrument the osmo_fd_register])
+ AC_DEFINE([OSMO_FD_CHECK], [1], [Instrument the osmo_fd_register])
fi
AC_ARG_ENABLE([force_io_select],
@@ -284,7 +332,7 @@ AC_ARG_ENABLE([force_io_select],
)],
[force_io_select=$enableval], [force_io_select="no"])
AS_IF([test "x$force_io_select" = "xyes"], [
- AC_DEFINE([FORCE_IO_SELECT], [1], [Force the use of select() instaed of poll()])
+ AC_DEFINE([FORCE_IO_SELECT], [1], [Force the use of select() instead of poll()])
])
AC_ARG_ENABLE(msgfile,
@@ -356,9 +404,11 @@ then
AM_CONDITIONAL(ENABLE_CTRL, false)
AM_CONDITIONAL(ENABLE_UTILITIES, false)
AM_CONDITIONAL(ENABLE_GB, false)
- AM_CONDITIONAL(ENABLE_GNUTLS, false)
+ AM_CONDITIONAL(ENABLE_LIBMNL, false)
AM_CONDITIONAL(ENABLE_LIBSCTP, false)
+ AM_CONDITIONAL(ENABLE_LIBUSB, false)
AM_CONDITIONAL(ENABLE_PCSC, false)
+ AM_CONDITIONAL(ENABLE_URING, false)
AM_CONDITIONAL(ENABLE_PSEUDOTALLOC, true)
AM_CONDITIONAL(ENABLE_SERCOM_STUB, true)
AM_CONDITIONAL(EMBEDDED, true)
@@ -366,6 +416,17 @@ then
AC_DEFINE([PANIC_INFLOOP],[1],[Use infinite loop on panic rather than fprintf/abort])
fi
+AC_ARG_ENABLE(log_macros,
+ [AS_HELP_STRING(
+ [--disable-log-macros],
+ [Disable logging macros that are also used internally to print information]
+ )],
+ [log_macros="yes"], [log_macros="no"])
+if test x"$log_macros" == x"yes"
+then
+ AC_DEFINE([LIBOSMOCORE_NO_LOGGING],[1],[Disable logging macros])
+fi
+
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
[--enable-sanitize],
@@ -390,6 +451,7 @@ AC_ARG_ENABLE(werror,
if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
+ WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
@@ -404,7 +466,7 @@ if test "x$enable_ext_tests" = "xyes" ; then
AM_PATH_PYTHON
AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmo_verify_transcript_vty.py,yes)
if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
- AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
+ AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
fi
fi
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
@@ -505,6 +567,35 @@ dnl Check if the compiler supports runtime SIMD detection
CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports],
[Runtime SIMD detection will be disabled])
+dnl There are some members in struct tcp_info that might not exist on all linux versions
+AC_CHECK_MEMBER([struct tcp_info.tcpi_notsent_bytes],
+ AC_DEFINE([HAVE_TCP_INFO_TCPI_NOTSENT_BYTES],
+ [1],
+ [Define to 1 if your <linux/tcp.h> header file have the tcpi_notsent_bytes member in struct tcp_info]),
+ [],
+ [#include <linux/tcp.h>])
+
+AC_CHECK_MEMBER([struct tcp_info.tcpi_rwnd_limited],
+ AC_DEFINE([HAVE_TCP_INFO_TCPI_RWND_LIMITED],
+ [1],
+ [Define to 1 if your <linux/tcp.h> header file have the tcpi_rwnd_limited member in struct tcp_info]),
+ [],
+ [#include <linux/tcp.h>])
+
+AC_CHECK_MEMBER([struct tcp_info.tcpi_sndbuf_limited],
+ AC_DEFINE([HAVE_TCP_INFO_TCPI_SNDBUF_LIMITED],
+ [1],
+ [Define to 1 if your <linux/tcp.h> header file have the tcpi_sndbuf_limited member in struct tcp_info]),
+ [],
+ [#include <linux/tcp.h>])
+
+AC_CHECK_MEMBER([struct tcp_info.tcpi_reord_seen],
+ AC_DEFINE([HAVE_TCP_INFO_TCPI_REORD_SEEN],
+ [1],
+ [Define to 1 if your <linux/tcp.h> header file have the tcpi_reord_seen member in struct tcp_info]),
+ [],
+ [#include <linux/tcp.h>])
+
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
@@ -514,18 +605,35 @@ AC_OUTPUT(
libosmocoding.pc
libosmovty.pc
libosmogsm.pc
+ libosmoisdn.pc
libosmogb.pc
libosmoctrl.pc
libosmosim.pc
libosmousb.pc
include/Makefile
+ include/osmocom/Makefile
+ include/osmocom/codec/Makefile
+ include/osmocom/coding/Makefile
+ include/osmocom/core/Makefile
+ include/osmocom/crypt/Makefile
+ include/osmocom/ctrl/Makefile
+ include/osmocom/gprs/Makefile
+ include/osmocom/gprs/protocol/Makefile
+ include/osmocom/gsm/Makefile
+ include/osmocom/gsm/protocol/Makefile
+ include/osmocom/isdn/Makefile
+ include/osmocom/sim/Makefile
+ include/osmocom/usb/Makefile
+ include/osmocom/vty/Makefile
src/Makefile
+ src/core/Makefile
src/vty/Makefile
src/codec/Makefile
src/coding/Makefile
src/sim/Makefile
src/usb/Makefile
src/gsm/Makefile
+ src/isdn/Makefile
src/gb/Makefile
src/ctrl/Makefile
src/pseudotalloc/Makefile
@@ -533,12 +641,16 @@ AC_OUTPUT(
tests/Makefile
tests/atlocal
utils/Makefile
+ utils/osmo-stat-dummy/Makefile
Doxyfile.core
Doxyfile.gsm
+ Doxyfile.isdn
Doxyfile.vty
Doxyfile.codec
Doxyfile.coding
Doxyfile.gb
Doxyfile.ctrl
+ Doxyfile.sim
+ Doxyfile.usb
Makefile
contrib/libosmocore.spec)
diff --git a/contrib/jenkins_amd64.sh b/contrib/jenkins_amd64.sh
index 3fee57cf..f26faa86 100755
--- a/contrib/jenkins_amd64.sh
+++ b/contrib/jenkins_amd64.sh
@@ -3,19 +3,13 @@
. $(dirname "$0")/jenkins_common.sh
-ENABLE_SANITIZE="--enable-sanitize"
-
-if [ "x$label" = "xFreeBSD_amd64" ]; then
- ENABLE_SANITIZE=""
-fi
-
src_dir="$PWD"
build() {
build_dir="$1"
prep_build "$src_dir" "$build_dir"
- "$src_dir"/configure --disable-silent-rules --enable-static $ENABLE_SANITIZE --enable-werror \
+ "$src_dir"/configure --disable-silent-rules --enable-static --enable-sanitize --enable-werror \
--enable-external-tests
run_make
diff --git a/contrib/jenkins_arm.sh b/contrib/jenkins_arm.sh
index e7df37d1..87934158 100755
--- a/contrib/jenkins_arm.sh
+++ b/contrib/jenkins_arm.sh
@@ -21,7 +21,6 @@ build() {
--disable-libsctp \
--disable-libusb \
--disable-libmnl \
- --enable-external-tests \
CFLAGS="-Os -ffunction-sections -fdata-sections -nostartfiles -nodefaultlibs $WERROR_FLAGS"
$MAKE $PARALLEL_MAKE
diff --git a/contrib/jenkins_common.sh b/contrib/jenkins_common.sh
index b86a479a..82dd471a 100644
--- a/contrib/jenkins_common.sh
+++ b/contrib/jenkins_common.sh
@@ -12,6 +12,37 @@ osmo-clean-workspace.sh
verify_value_string_arrays_are_terminated.py
+# Validate enum fields in header are added to tlv_definition in source file (SYS#5891):
+
+verify_gsm0808_tlv_definition() {
+ set +x;
+ enums=$(grep "GSM0808_IE_" include/osmocom/gsm/protocol/gsm_08_08.h | grep "=" | awk '{ print $1 }')
+ counted_enums=$(for f in $enums; do printf "%-60s %s\n" "$f" "$(grep -c "\[$f\]" src/gsm/gsm0808.c)"; done)
+ missing_enums=$(echo "$counted_enums" | grep -v GSM0808_IE_RESERVED | grep "0$" || true)
+ if [ "x$missing_enums" != "x" ]; then
+ echo "Missing IEs in src/gsm/gsm0808.c!"
+ echo "$missing_enums"
+ exit 1
+ fi
+ set -x;
+}
+verify_gsm0808_tlv_definition
+
+verify_gsm_08_05_tlv_definition() {
+ set +x;
+ enums=$(grep "RSL_IE_" include/osmocom/gsm/protocol/gsm_08_58.h | grep -e "=" -e ",$" | awk '{ print $1 }' | tr -d ',')
+ counted_enums=$(for f in $enums; do printf "%-60s %s\n" "$f" "$(grep -c "\[$f\]" src/gsm/rsl.c)"; done)
+ # TODO: Add RSL_IE_SIEMENS_* to the tlv struct definitions.
+ missing_enums=$(echo "$counted_enums" | grep -v RSL_IE_SIEMENS |grep "0$" || true)
+ if [ "x$missing_enums" != "x" ]; then
+ echo "Missing IEs in src/gsm/rsl.c!"
+ echo "$missing_enums"
+ exit 1
+ fi
+ set -x;
+}
+verify_gsm_08_05_tlv_definition
+
prep_build() {
_src_dir="$1"
_build_dir="$2"
diff --git a/contrib/libosmocore.spec.in b/contrib/libosmocore.spec.in
index df36e89d..60ca3977 100644
--- a/contrib/libosmocore.spec.in
+++ b/contrib/libosmocore.spec.in
@@ -29,9 +29,12 @@ BuildRequires: xz
BuildRequires: pkgconfig(gnutls) >= 2.12.0
BuildRequires: pkgconfig(libpcsclite)
BuildRequires: pkgconfig(libusb-1.0)
-BuildRequires: pkgconfig(talloc) >= 2.0.1
+BuildRequires: pkgconfig(talloc) >= 2.1.0
BuildRequires: pkgconfig(libmnl)
BuildRequires: pkgconfig(libsystemd)
+%if 0%{?centos_ver} != 7
+BuildRequires: pkgconfig(liburing)
+%endif
%description
libosmocore is a package with various utility functions that were
@@ -54,14 +57,15 @@ originally developed as part of the OpenBSC project.
This package contains a program for frequency calculation for GSM
called "osmo-arfcn", and a program called "osmo-auc-gen" that is used
-for testing GSM authentication.
+for testing GSM authentication, as well as "osmo-config-merge", a tool
+for merging Osmocom configuration files.
-%package -n libosmocodec0
+%package -n libosmocodec4
Summary: GSM 06.10, 06.20, 06.60, 06.90 codec library
License: GPL-2.0-or-later
Group: System/Libraries
-%description -n libosmocodec0
+%description -n libosmocodec4
The libosmocodec library contains an implementation of multiple
GSM codecs:
@@ -74,7 +78,7 @@ GSM codecs:
Summary: Development files for the Osmocom GSM codec library
License: GPL-2.0-or-later
Group: Development/Libraries/C and C++
-Requires: libosmocodec0 = %version
+Requires: libosmocodec4 = %version
%description -n libosmocodec-devel
The libosmocodec library contains an implementation of multiple
@@ -111,13 +115,13 @@ transcoding routines.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmocoding.
-%package -n libosmocore17
+%package -n libosmocore21
Summary: Osmocom core library
# crc16.c has GPL2-only clauses, the rest (*.c) is GPL-2.0+
License: GPL-2.0-only AND GPL-2.0-or-later
Group: System/Libraries
-%description -n libosmocore17
+%description -n libosmocore21
libosmocore is a library with various utility functions shared
between OpenBSC and OsmocomBB.
@@ -126,8 +130,9 @@ Summary: Development files for the Osmocom core library
# crc16.h has GPL2-only clauses, the rest (*.h) is GPL-2.0+
License: GPL-2.0-only AND GPL-2.0-or-later
Group: Development/Libraries/C and C++
-Requires: libosmocore17 = %version
+Requires: libosmocore21 = %version
Requires: libtalloc-devel
+Requires: lksctp-tools-devel
%description -n libosmocore-devel
libosmocore is a library with various utility functions shared
@@ -163,12 +168,12 @@ interface, the control interface is meant to be used by programs.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmoctrl.
-%package -n libosmogb12
+%package -n libosmogb14
Summary: Osmocom GPRS Gb Interface (NS/BSSGP) library
License: AGPL-3.0-or-later
Group: System/Libraries
-%description -n libosmogb12
+%description -n libosmogb14
libosmocore is a package with various utility functions that were
originally developed as part of the OpenBSC project.
@@ -179,7 +184,7 @@ Summary: Development files for the Osmocom GPRS Gb interface library
License: AGPL-3.0-or-later
Group: Development/Libraries/C and C++
Requires: libosmocore-devel = %version
-Requires: libosmogb12 = %version
+Requires: libosmogb14 = %version
Requires: libosmovty-devel = %version
%description -n libosmogb-devel
@@ -188,12 +193,12 @@ The libosmogb library contains a GPRS BSSGP protocol implementation.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmogb.
-%package -n libosmogsm16
+%package -n libosmogsm20
Summary: Osmocom GSM utility library
License: GPL-2.0-or-later AND AGPL-3.0-or-later
Group: System/Libraries
-%description -n libosmogsm16
+%description -n libosmogsm20
libosmocore is a package with various utility functions that were
originally developed as part of the OpenBSC project.
@@ -208,7 +213,9 @@ Summary: Development files for the Osmocom GSM utility library
License: GPL-2.0-or-later AND AGPL-3.0-or-later
Group: Development/Libraries/C and C++
Requires: libosmocore-devel = %version
-Requires: libosmogsm16 = %version
+Requires: libosmogsm20 = %version
+Requires: libosmoisdn-devel = %version
+Requires: libosmoisdn0 = %version
%description -n libosmogsm-devel
The libosmogsm library in particular is a collection of common code
@@ -220,6 +227,34 @@ protocol definitions for a series of protocols.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmogsm.
+%package -n libosmoisdn0
+Summary: Osmocom ISDN utility library
+License: GPL-2.0-or-later
+Group: System/Libraries
+
+%description -n libosmoisdn0
+libosmocore is a package with various utility functions that were
+originally developed as part of the OpenBSC project.
+
+The libosmoisdn library in particular is a collection of common code used in
+various ISDN related sub-projects inside the Osmocom family of projects. It
+includes an I.460 sub-channel multiplex and a generic LAPD core.
+
+%package -n libosmoisdn-devel
+Summary: Development files for the Osmocom ISDN utility library
+License: GPL-2.0-or-later
+Group: Development/Libraries/C and C++
+Requires: libosmocore-devel = %version
+Requires: libosmoisdn0 = %version
+
+%description -n libosmoisdn-devel
+The libosmoisdn library in particular is a collection of common code used in
+various ISDN related sub-projects inside the Osmocom family of projects. It
+includes an I.460 sub-channel multiplex and a generic LAPD core.
+
+This subpackage contains libraries and header files for developing
+applications that want to make use of libosmogsm.
+
%package -n libosmosim2
Summary: Osmocom SIM card related utility library
License: GPL-2.0-or-later
@@ -246,12 +281,12 @@ access.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmosim.
-%package -n libosmovty9
+%package -n libosmovty13
Summary: Osmocom VTY interface library
License: GPL-2.0-or-later
Group: System/Libraries
-%description -n libosmovty9
+%description -n libosmovty13
libosmocore is a package with various utility functions that were
originally developed as part of the OpenBSC project.
@@ -263,7 +298,7 @@ Summary: Development files for the Osmocom VTY interface library
License: GPL-2.0-or-later
Group: Development/Libraries/C and C++
Requires: libosmocore-devel = %version
-Requires: libosmovty9 = %version
+Requires: libosmovty13 = %version
%description -n libosmovty-devel
The libosmovty library implements the interactive command-line on the
@@ -306,8 +341,18 @@ applications that want to make use of libosmousb.
%build
echo "%version" >.tarball-version
autoreconf -fiv
-%configure --enable-shared --disable-static --enable-systemd-logging \
+
+CONFIGURE_FLAGS="
+ --enable-shared \
+ --disable-static \
+ --enable-systemd-logging \
--includedir="%_includedir/%name"
+"
+%if 0%{?centos_ver} == 7
+ CONFIGURE_FLAGS="$CONFIGURE_FLAGS --disable-uring"
+%endif
+
+%configure $CONFIGURE_FLAGS
make %{?_smp_mflags} V=1
%install
@@ -318,22 +363,24 @@ find "$b/%_libdir" -type f -name "*.la" -delete
%check
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
-%post -n libosmocodec0 -p /sbin/ldconfig
-%postun -n libosmocodec0 -p /sbin/ldconfig
+%post -n libosmocodec4 -p /sbin/ldconfig
+%postun -n libosmocodec4 -p /sbin/ldconfig
%post -n libosmocoding0 -p /sbin/ldconfig
%postun -n libosmocoding0 -p /sbin/ldconfig
-%post -n libosmocore17 -p /sbin/ldconfig
-%postun -n libosmocore17 -p /sbin/ldconfig
+%post -n libosmocore21 -p /sbin/ldconfig
+%postun -n libosmocore21 -p /sbin/ldconfig
%post -n libosmoctrl0 -p /sbin/ldconfig
%postun -n libosmoctrl0 -p /sbin/ldconfig
-%post -n libosmogb12 -p /sbin/ldconfig
-%postun -n libosmogb12 -p /sbin/ldconfig
-%post -n libosmogsm16 -p /sbin/ldconfig
-%postun -n libosmogsm16 -p /sbin/ldconfig
+%post -n libosmogb14 -p /sbin/ldconfig
+%postun -n libosmogb14 -p /sbin/ldconfig
+%post -n libosmogsm20 -p /sbin/ldconfig
+%postun -n libosmogsm20 -p /sbin/ldconfig
+%post -n libosmoisdn0 -p /sbin/ldconfig
+%postun -n libosmoisdn0 -p /sbin/ldconfig
%post -n libosmosim2 -p /sbin/ldconfig
%postun -n libosmosim2 -p /sbin/ldconfig
-%post -n libosmovty9 -p /sbin/ldconfig
-%postun -n libosmovty9 -p /sbin/ldconfig
+%post -n libosmovty13 -p /sbin/ldconfig
+%postun -n libosmovty13 -p /sbin/ldconfig
%post -n libosmousb0 -p /sbin/ldconfig
%postun -n libosmousb0 -p /sbin/ldconfig
@@ -341,9 +388,9 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%defattr(-,root,root)
%_bindir/osmo-*
-%files -n libosmocodec0
+%files -n libosmocodec4
%defattr(-,root,root)
-%_libdir/libosmocodec.so.0*
+%_libdir/libosmocodec.so.4*
%files -n libosmocodec-devel
%defattr(-,root,root)
@@ -365,9 +412,9 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%_libdir/libosmocoding.so
%_libdir/pkgconfig/libosmocoding.pc
-%files -n libosmocore17
+%files -n libosmocore21
%defattr(-,root,root)
-%_libdir/libosmocore.so.17*
+%_libdir/libosmocore.so.21*
%files -n libosmocore-devel
%defattr(-,root,root)
@@ -391,9 +438,9 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%_libdir/libosmoctrl.so
%_libdir/pkgconfig/libosmoctrl.pc
-%files -n libosmogb12
+%files -n libosmogb14
%defattr(-,root,root)
-%_libdir/libosmogb.so.12*
+%_libdir/libosmogb.so.14*
%files -n libosmogb-devel
%defattr(-,root,root)
@@ -403,9 +450,9 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%_libdir/libosmogb.so
%_libdir/pkgconfig/libosmogb.pc
-%files -n libosmogsm16
+%files -n libosmogsm20
%defattr(-,root,root)
-%_libdir/libosmogsm.so.16*
+%_libdir/libosmogsm.so.20*
%files -n libosmogsm-devel
%defattr(-,root,root)
@@ -416,6 +463,18 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%_libdir/libosmogsm.so
%_libdir/pkgconfig/libosmogsm.pc
+%files -n libosmoisdn0
+%defattr(-,root,root)
+%_libdir/libosmoisdn.so.0*
+
+%files -n libosmoisdn-devel
+%defattr(-,root,root)
+%dir %_includedir/%name
+%dir %_includedir/%name/osmocom
+%_includedir/%name/osmocom/isdn/
+%_libdir/libosmoisdn.so
+%_libdir/pkgconfig/libosmoisdn.pc
+
%files -n libosmosim2
%defattr(-,root,root)
%_libdir/libosmosim.so.2*
@@ -428,9 +487,9 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%_libdir/libosmosim.so
%_libdir/pkgconfig/libosmosim.pc
-%files -n libosmovty9
+%files -n libosmovty13
%defattr(-,root,root)
-%_libdir/libosmovty.so.9*
+%_libdir/libosmovty.so.13*
%files -n libosmovty-devel
%defattr(-,root,root)
diff --git a/contrib/struct_endianess.py b/contrib/struct_endianness.py
index 6ce75fcb..e6cbe00b 100755
--- a/contrib/struct_endianess.py
+++ b/contrib/struct_endianness.py
@@ -253,7 +253,7 @@ def handle_struct_body(body_str):
new_lines = ['#if OSMO_IS_LITTLE_ENDIAN\n']
new_lines.append(body_str)
new_lines.append('#elif OSMO_IS_BIG_ENDIAN\n'
- '/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */\n')
+ '/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */\n')
new_lines.append(big_endian_body_str)
new_lines.append('\n#endif\n')
return ''.join(new_lines)
@@ -261,7 +261,7 @@ def handle_struct_body(body_str):
return body_str
def _check_file(f):
- if not (f.endswith('.h') or f.endswith('.c') or f.endswith('.cpp')):
+ if not f.endswith(('.h', '.c', '.cpp')):
return
# section the file into
diff --git a/contrib/talloc_count.sh b/contrib/talloc_count.sh
new file mode 100755
index 00000000..61aa3f57
--- /dev/null
+++ b/contrib/talloc_count.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# Print a summary of how often each named object appears in a talloc report.
+#
+# usage:
+# talloc_count.sh my_talloc_report.txt
+# or:
+# osmo_interact_vty.py -p 4242 -c 'show talloc-context application full' | talloc_count.sh
+#
+# produces output like:
+# 1 struct foo
+# 1 struct log_info
+# 1 struct log_info_cat
+# 21 msgb
+# 1391 SCCP-SCOC(N)[N]
+# 1402 struct osmo_fsm_inst
+# [...]
+
+f="$1"
+
+tmpdir="$(mktemp -d)"
+trap "rm -rf \"$tmpdir\"" EXIT
+
+# without input file, read stdin
+if [ "x$f" = "x" ]; then
+ f="$tmpdir/input"
+ cat > $f
+fi
+
+mangled="$tmpdir/mangled"
+grep contains "$f" \
+ | sed 's/[ \t]*contains.*//' \
+ | sed 's/^[ \t]*//' \
+ | sed 's/[ \t][ \t]*/ /g' \
+ | grep -v '^$' \
+ | grep -v '^[0-9]\+$' \
+ | sed 's/0x[0-9a-fA-F]\+/N/g' \
+ | sed 's/\<[0-9a-fA-F]\+\>/N/g' \
+ | sed 's/[0-9]\+/N/g' \
+ | sort \
+ > "$mangled"
+
+count() {
+ name="$1"
+ nr="$(grep -Fx "$name" "$mangled" | wc -l)"
+ printf "%6d $name\\n" $nr
+}
+
+{
+ cat "$mangled" | uniq | while read type; do
+ count "$type"
+ done
+} | sort -h
diff --git a/debian/changelog b/debian/changelog
index 8fffac54..c9184df0 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,910 @@
+libosmocore (1.9.0) unstable; urgency=medium
+
+ [ Oliver Smith ]
+ * gsm0808_dec_channel_type: add missing len check
+ * test_gsm0808_enc_dec_channel_type -> …_speech
+ * gsm0808_chan_indicator: add SPEECH_CTM_TEXT_TELEPHONY
+ * gsm0808_enc/dec_channel_type: support data
+ * Fix typo endianess -> endianness
+ * Run struct_endianness.py
+ * gsm0808_dec_channel_type: fix dec of ch_rate_type
+ * tests: add test_gsm0808_enc_dec_channel_type_sign
+ * gsm0808_enc/dec_channel_type: fix transparent flag
+ * libosmocore.map: add tall_{ctr/msgb}_ctx
+ * rsl: put values for Channel Mode into enums
+ * rsl: RSL_CMOD_CSD_T: make enum values consistent
+ * gsm0808: make CSD enum values consistent with RSL
+ * on_dso_load_select: run after on_dso_load_ctx
+ * debian: set compat level to 10
+ * debian: depend on liburing-dev for debian >= 11
+ * contrib/libosmocore.spec: centos7: disable uring
+ * debian: fix build on ubuntu 20.04 without liburing
+
+ [ Neels Hofmeyr ]
+ * add contrib/talloc_count.sh
+ * add osmo_prim_operation_name()
+ * add gsm0808_amr_modes_from_cfg
+ * improve test output for gsm0808_sc_cfg_from_gsm48_mr_cfg()
+ * fix 'make vty-test' for --disable-gb
+ * fix 'make vty-test' for --disable-external-tests --enable-gb
+ * contrib/talloc_count.sh: improve hexadecimal masking
+ * error log: osmo_sock_init2_multiaddr() v4/v6 mix
+ * logging vty: probe 'print' and 'logging timestamp' cmds
+ * vty: show bug in implicit go_parent_node
+ * vty: fix vty->index for implicit go_parent_node
+ * vty: move struct vty_parent_node to private API
+ * gsm: add osmo_mobile_identity_decode_from_l3_buf()
+ * gsm_04_08_gprs: add IEI "GMM TMSI Based NRI Container"
+ * improve API for osmo_routing_area_id
+
+ [ Max ]
+ * socket: propagate error in osmo_sock_unix_init() to the caller
+ * GSMTAP: fix typo
+ * GSMTAP: add gsmtap_source_init*2()
+ * GSMTAP: add missing parameter docstrings
+ * logging: print talloc report on exit from vty test
+
+ [ Philipp Maier ]
+ * i460_mux.c fix apidoc
+ * i460_mux: make osmo_i460_subchan_count public
+ * gsmtap_util: remove whitespace at the end of line
+ * i460_mux: add define constant for maximum number of subchannels
+ * codec: add define constants for RFC5993 and TS101318
+ * gprs_bssgp_rim: also print NSEI when sending RIM messages
+ * gprs_bssgp_rim: allow sending of encoded RIM messages
+ * gprs_bssgp_rim: add decoder for RIM ROUTING ADDRESS
+
+ [ Vadim Yanitskiy ]
+ * gsm: fix invalid check in gsm48_decode_ssversion()
+ * gsm: add missing features to osmo_bts_features_names[]
+ * gsm: ensure completeness of osmo_bts_features_{descs,names}[]
+ * gsm/{bsslap,bssmap_le}: zero-initialize structs using memset()
+ * msgb: use OSMO_ASSERT in msgb_alloc_headroom[_c]()
+ * gsm: use OSMO_ASSERT() in osmo_iuup_msgb_alloc_c()
+ * debian/control: make libosmocore-doc depend on libosmo{ctrl,gb}-doc
+ * debian/control: fix typo
+ * doxygen: also generate documentation for libosmo{sim,usb}
+ * contrib/struct_endianness.py: simplify file extension check
+ * doxygen: remove documentation for non-existent params
+ * doxygen: fix various typos in commands \param and \returns
+ * gsm0502: add burst length definitions from chapter 5.2
+ * coding: clean up Makefile.am
+ * utils/Makefile.am: remove duplicate libosmogsm.la
+ * utils/Makefile.am: do not overwrite AM_CFLAGS
+ * utils/osmo-stat-dummy/Makefile.am: drop empty variables
+ * gsmtap: add missing entries to gsmtap_type_names[]
+ * tests/v110: assert(user_data_chunk_bits) in test_ra1()
+ * isdn: fix identical operands in v110_adapt_IR8000_to_2400()
+ * gsm_04_08: document/clarify enum gsm48_chan_mode values
+ * gsm_04_08: add more enum gsm48_chan_mode speech values
+ * gsm_04_08: add more enum gsm48_chan_mode data values
+ * gsm0808: handle new enum gsm48_chan_mode speech/data values
+ * tests: make VTY tests depend on the respective binaries
+ * fixup (partial revert): "coding: clean up Makefile.am"
+ * core: remove unnecessary #include <osmocom/core/talloc.h>
+ * libosmocore.map: add missing symbols needed for osmo-qcdiag
+ * coding: fix doxygen doc for _xcch_encode_cB()
+ * copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
+ * coding: use GSM_MACBLOCK_LEN gsm0503_tch_fr_decode()
+ * coding: gsm0503_tch_f96_[de]interleave() not applicable to TCH/F2.4
+ * coding: declare gsm0503_tch_f96_[de]interleave()
+ * coding: fix API doc: TCH/H needs 6 bursts, not 8
+ * coding: fix API doc: TCH/AFS vs TCH/AHS
+ * coding: use gsm0503_tch_hr_decode2() in coding_test
+ * gsm: fix convolutional code definition for TCH/F4.8
+ * coding: implement TCH/F9.6, TCH/[FH]4.8, TCH/H2.4, TCH/F14.4
+ * coding: implement dedicated codec API for FACCH/[FH]
+ * coding: test FACCH/[FH] bitstealing in test_csd()
+ * coding: fix _tch_csd_burst_map(): do not overwrite FACCH
+ * struct osmo_sub_auth_data: remove OSMO_DEPRECATED_OUTSIDE
+ * coding: fix a copy-paste bug in gsm0503_tch_afs_decode_dtx()
+ * gsm: add gsm0502_fn2ccch_block()
+ * gsm0502: cosmetic: use ARRAY_SIZE in gsm0502_fn2ccch_block()
+ * ipa: fix a typo in ipa_ccm_rcvmsg_base(): PING -> PONG
+ * core: fix pointer access in msgb_l[1-4] macros
+ * coding: remove redundant memset()s in gsm0503_tch_fr{96,144}_encode()
+ * coding: implement encoding/decoding API for TCH/F2.4
+ * lapdm: cosmetic: simplify lapdm_phsap_up(), use OSMO_PRIM[_HDR]
+ * gsm_08_08: define GSM0808_SCT_EXT (separately)
+ * gsm48_ie: fix gsm48_encode_bearer_cap(): encode bcap->data.transp
+ * isdn: mux_timeslot_provide_bits(): remove unused 'count'
+ * .gitignore: add include/osmocom/core/socket_compat.h
+ * tests/{v110,gsm44021}: change naming: 'test_' -> '_test'
+ * gsm_12_21.h: add flags for NM_ATT_IPACC_SUPP_FEATURES
+ * gsm_12_21.h: fix typo: NM_IPAC_F_CHANT_P{C->D}CHF
+
+ [ Pau Espin Pedrol ]
+ * gsm_04_60.h: Better describe origin of enum osmo_gprs_nmo
+ * Move libosmogsm TS 44.060 declarations under include/osmocom/gsm/
+ * libosmogb.pc.in: Fix missing dependency on libosmogsm
+ * gsm: gsm_gsmtime2fn(): constify param
+ * cosmetic: stats_tcp: Fix typo in comment
+ * logging: Unregister osmo_fd before closing fd
+ * select: Optimize osmo_fd_get_by_fd
+ * select.c: Clarify osmo_fd_(un)register() API expectations of registered fd
+ * select.c: Clarify osmo_fd_unregister() can only be called on registered osmo_fds
+ * configure.ac: Fix logic enabling osmo_fd fd checks
+ * configure.ac: Fix typo in enable flag description
+ * select.c: osmo_fd_unregister(): Avoid assert hit with old buggy users of the API
+ * tests/Makefile.am: Move system libs at the end of list
+ * tests/Makefile.am: Drop duplicated libosmogb.la in LDADD
+ * tests/Makefile.am: Move LDADD to right position
+ * logging.c: Sanitize calls to osmo_fd_unregister()
+ * Fix parsing of TLV_TYPE_SINGLE_TV
+ * gsm: Add missing TS 24.008 SM layer IEs
+ * gsm_04_08_gprs.h: Add missing GMM IEs for T3302 and T3346
+ * gsm_04_08_gprs.h: Add enum field for GMM 'P-TMSI type' IE
+ * gb: ns2: Rename parameter name in gprs_ns2_nsvc_by_sockaddr_bind()
+ * tlv: Show bug in decoded tlv_parsed for type TLV_TYPE_SINGLE_TV
+ * Fix 'Fix parsing of TLV_TYPE_SINGLE_TV'
+ * ns2: Count transmitted/dropped in each layer implementation
+ * cosmetic: codec/Makefile.am: list sources one file per line
+ * osmo_io: Make name optional, add _set_name() API
+ * socket: Cache errno before calling further functions
+ * gsm0502.h: Document spec number
+ * gsm: Add missing IE definition for GMM Receive N-PDU Number list
+ * exec: osmo_system_nowait2(): Improve logging and error checks
+ * sockaddr_str: Introduce macro OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL
+ * cosmetic: lapdm: Fix typo in comment
+ * tlv: Introduce API msgb_tv32_push()
+ * lapdm: Track fn of primitives in struct lapdm_msg_ctx
+ * gsm: Introduce functions to convert between FN and RFN (Reduced FN)
+ * socket: Add osmo_sock_init flag to enable SCTP ASCONF features
+ * rsl: Introduce new osmocom extension IE RSL_IE_OSMO_ABS_FRAME_NUMBER
+ * lapdm: Append RSL_IE_OSMO_ABS_FRAME_NUMBER to RSLms msgs towards upper layers
+ * Revert "lapdm: Append RSL_IE_OSMO_ABS_FRAME_NUMBER to RSLms msgs towards upper layers"
+ * Revert "rsl: Introduce new osmocom extension IE RSL_IE_OSMO_ABS_FRAME_NUMBER"
+ * lapdm: Update public lapdm_msg_ctx upon CCCH data ind
+ * socket: Avoid aborting socket creation if setsockopt for ASCONF fails
+ * gsm_12_21.h: Introduce packed structs for NM_ATT_IPACC_RLC_CFG{,_2,_3} values
+ * gsm_12_21.h: Introduce packed structs for NM_ATT_IPACC_BSSGP_CFG values
+ * gsm_12_21.h: Introduce packed structs for NM_ATT_IPACC_NS_CFG values
+ * socket: Remove OSMO_SOCK_F_SCTP_ASCONF_SUPPORTED, add osmo_sock_init2_multiaddr2()
+ * socket: Support setsokopt SCTP_INITMSG in osmo_sock_init2_multiaddr2()
+
+ [ Matan Perelman ]
+ * gsm0808_enc_channel_type: Add spare byte
+
+ [ Harald Welte ]
+ * Implement the XOR-2G authentication algorithm
+ * New unit test for XOR-2G authentication
+ * Rename OSMO_AUTH_ALG_XOR to OSMO_AUTH_ALG_XOR_3G
+ * convolutional coding for CSD
+ * Interleaving for CSD
+ * libosmocore.map: Add two missing entries for gsmtap*2() API
+ * Makefile.am: Make libraries depend on .map files
+ * isdn: Add V.110 encoder/decoder
+ * gsm: TS 44.021 modified V.110 frame encoding/decoding support
+ * gsm_08_58: Document IPAC RTP CSD modes in at least a few words
+ * New osmo-gsmtap-logsend utility
+ * gsmtap.h: Define a packet type for encapsulation of GSM RLP frames
+ * rate_ctr: Add rate_ctr_add2() similar to rate_ctr_inc2()
+ * Add osmo_io with initial poll backend
+ * Add osmo_gsm48_si1ro_nch_pos_{encode,decode} functions
+ * cosmetic: Fix spec reference in RSL header file
+ * gsm_08_58.h: Add 'struct rsl_ie_nch_drx_info'
+ * libosmogsm: Support authentication with 256-bit K and/or OP/OPc
+ * libosmogsm: Add OSMO_ASSERT() to ensure correct algorithm
+ * libosmogsm: Ensure MILENAGE + XOR-3G K length is 128 bit
+ * osmo-auc-gen: Convert over to osmo_auth_gen_vec*2 API
+ * libosmogsm: Allow auth API caller to specify RES length
+ * libosmogsm: Factor out the C2 derivation function
+ * libosmogsm: Avoid executing MILENAGE crypto twice (for UMTS and GSM)
+ * libosmogsm: Add support for TUAK authentication algorithm
+ * gsmtap_source_free(): Don't crash if NULL is passed
+
+ [ Daniel Willmann ]
+ * Add libosmocore.map
+ * Add osmo_sockaddr_size() to return the size of the variant used
+ * tests: Add initial osmo_io tests
+ * osmo_io: Avoid read of uninitialized variable
+ * gpsr_ns2_udp: Use osmo_io_fd instead of osmo_fd
+ * osmo_io: Improve handling and documentation of segmentation_cb
+ * osmo_io: Support detecting non-blocking connect()
+ * osmo_io: Consistency - put read/recv callback first in osmo_io_ops
+ * osmo_io: Don't make msg in write_cb const
+ * osmo_io: Remove osmo_iofd_read/write_enable/disable
+ * socket: Ensure fd is not negative in osmo_sock_get_name_buf()
+ * osmo_io: Return early on error in osmo_iofd_register()
+ * osmo_io: Use LOGPIO instead of LOGP
+ * osmo_io: Use bitfield for various boolean flags
+ * osmo_io: Make the test more deterministic between backends
+ * osmo_io: Fix write_enable handling in iofd_txqueue
+ * osmo_io: Remove missing functions from map file
+ * osmo_io: Add osmo_iofd_notify_connected()
+ * osmo_io: Document expectation that segmentation_cb() can modify msgb
+ * osmo_io: Add function to change the maximum length of the tx_queue
+ * cosmetic: Fix doc comment
+ * osmo_io(cosmetic): End in a dot for doxygen AUTO_BRIEF
+ * osmo_io: Fix length calculation in iofd_handle_segmentation()
+ * osmo_io: Ensure correct ownership of msgb when sending
+ * osmo_io: Use MSG_NOSIGNAL to avoid SIGPIPE on write
+ * osmo_io: Add iofd_handle_recv()
+ * osmo_io: Avoid potential double free when sending msgb
+ * osmo_io: Add io_uring backend
+ * ns2: Add VTY option to change the max write queue size for UDP
+ * osmo_io: Change parent of msghdr to iofd (instead of msg)
+ * osmo_io: Use local variable to reference msghdr->msg
+
+ [ Eric ]
+ * fix _thread order
+ * logging: remove log_initialized(void)
+ * gsm48_rest_octets: fix wrong value
+
+ [ Mychaela N. Falconia ]
+ * codec: add osmo_efr_check_sid() function
+ * codec: add SID classification functions per GSM 06.31 & 06.81
+ * codec: add SID preening functions for FR & EFR
+ * codec: add osmo_{fr,efr}_is_any_sid() inline functions
+ * codec: add osmo_gsm611_silence_frame[] datum
+ * codec cosmetic: move old FR ECU code to ecu_fr_old.c
+ * codec: replace GSM-FR ECU with new implementation
+ * coding: fix decoding of EFR triplicated bits
+ * gsm0503_tch_hr_decode(): look at all 8 stealing bits
+ * gsm0503_tch_hr_encode(): accept both TS101318 and RFC5993 payloads
+ * gsm0503_tch_hr_decode2(): new function, emits TS101318 format
+ * libosmocoding.map: export gsm0503_tch_hr_decode2()
+ * coding cosmetic: gsm0503_tch_{fr,hr}_encode(): remove extra spacing
+ * codec: new functions osmo_{fr,efr}_sid_reset()
+ * codec: new function osmo_hr_sid_reset()
+ * coding: gsm0503_tch_{fr,hr}_encode(): add ability to emit BFI
+ * ecu: add is_dtx_pause() method
+
+ [ arehbein ]
+ * core: Check return value of osmo_fd_register()
+ * core: Add function to update osmo_io_ops field for osmo_io_fd
+ * core/osmo_io: Rename variables for readability
+ * core/osmo_io: Fix reception of partial packets
+ * gsm/ipa: Add segmentation callback
+ * Revert "gsm/ipa: Add segmentation callback"
+ * select: Prevent negative index lookup on osmo_fd_lookup.table
+
+ [ Andreas Eversberg ]
+ * ASCI: Add 3GPP TS 44.068 and 44.069 protocol definitions
+ * ASCI: Add IE transcoding according to 3GPP TS 48.008
+ * Added generation of include/osmocom/core/socket_compat.h
+ * ASCI: Add message definition and encoding according to 3GPP TS 48.008
+ * Add support for sending Bter UI frames at lapdm.c
+ * Add support for receiving Bter UI frames at lapdm.c
+ * Add short L3 header to gsm_04_08.h
+ * Fix short L3 header of SI 10 at gsm_04_08.h
+ * ASCI: Add Notification/NCH message to gsm_04_08.h
+ * lapdm: Do not return an error when enqueuing a frame
+ * Add VGCS UPLINK GRANT message structure to gsm_04_08.h
+ * ASCI: Also display group/broadcast call message names
+ * Allow 'configure <cr>' at VTY to enter config mode
+ * ASCI: Add decoding of mobile identity in TALKER INDICATION
+ * ASCI: Add missing check for return value of gsm0808_enc_speech_codec_list2()
+ * ASCI: Add BCC call state definitions
+ * LAPDM: Use correct offset to short header on recevied frame
+ * ASCI: Add definition for TALKER INDICATION and UPLINK RELEASE
+
+ [ Sylvain Munaut ]
+ * gsm: Fix comment for TCH/F4.8 code
+ * gsm: Improve the TCH/H2.4 coding routines
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 13:15:52 +0200
+
+libosmocore (1.8.0) unstable; urgency=medium
+
+ [ Vadim Yanitskiy ]
+ * fix uninitialized err pointer passed to osmo_bssap_le_dec()
+ * gsm0408_test: do not return early in test_bearer_cap()
+ * gsm0408_test: add a testcase for gsm48_decode_bearer_cap()
+ * gsm48_ie: fix coding style: while is not a function
+ * gb: fix uninitialized ptr access in bssgp_encode_rim_pdu()
+ * fsm: add unit tests verifying state timeout s/ms accuracy
+ * fsm: fix state_chg(): pass microseconds to osmo_timer_schedule()
+ * tests/tdef: assert pointer returned by osmo_tdef_get_entry()
+ * gb/gprs_ns: call osmo_timer_del() unconditionally
+ * fsm: osmo_fsm_{event,inst,state}_name(): make *fi pointer const
+ * logging: add a new category DLCSN1 for libosmo-csn1
+ * {gb,sim,usb}: ensure -no-undefined is present in *_la_LDFLAGS
+ * include: use '#pragma once' everywhere
+ * gsm0502: use parentheses in GSM_TDMA_FN_{SUM,SUB} macros
+ * configure.ac: fix 'AM_CONDITIONAL(ENABLE_GNUTLS, false)' listed twice
+ * {gsm,gb}/Makefile.am: drop undefined $GCC_FVISIBILITY_HIDDEN
+ * gsm0502: gsm0502_fn_remap(): use GSM_TDMA_FN_SUB() macro
+ * */Makefile.am: do not mix up AM_CFLAGS with AM_CPPFLAGS
+ * gsm0808: cosmetic: switch is not a function
+ * gsm0808: remove unneeded assignment in enc_speech_codec()
+ * gsm0808: remove redundant assert() in enc_speech_codec()
+ * gsm0808: remove over-defensive assert()s for function parameters
+ * gsm0808: add gsm0808_enc_speech_codec[_list]2()
+ * gsm0808: use new gsm0808_enc_speech_codec[_list]2() API
+ * gsm48_ie: gsm48_decode_freq_list(): make 'cd' argument const
+
+ [ Pau Espin Pedrol ]
+ * iuup: Explicitly mark default case as unexpected with assert
+ * cbsp: avoid potential msgb write overflow in osmo_cbsp_recv_buffered
+ * gsm_23_041.h: Define CBS ETWS Warning Type values
+ * cbsp: Return error if decoding any of the cell id lists fail
+ * tests: Run smscb/gsm0341_test during make check
+ * cbsp: Guard against malformed msgb without l1h,l2h being passed
+ * cbsp: Fix decoding of Fail List
+ * cosmetic: tlv.h: Fix trailing whistespace
+ * tlv.h: Fix TLVP_PRESENT returning a pointer instead of a boolean
+ * gsm: Add BTS feature for Osmux
+ * gsm: rsl: Define new osmocom extension TLV IE to pass Osmux CID
+ * gsm: bts_features: Add missing entries to osmo_bts_features_names
+ * utils.h: protect param with parenthesis in OSMO_BYTES_FOR_BITS()
+ * vty: Allow using hex representations in cmd numeric ranges
+ * socket.h: Reorder sockaddr APIs to have them all together
+ * socket: Introduce API osmo_sockaddr_is_any
+ * gsm: constify several readonly params
+ * ctrl: error if program forgot to initialize the ctr handler before installing cmds
+ * socket.h: Introduce API osmo_sockaddr_netmask_to_prefixlen()
+ * Move src/*.{c,h} to src/core/
+ * src/core/Makefile.am: reformat SOURCES list
+ * Split include/Makefile.am content into subdirs
+ * Makefile.am: Remove unexsiting all_includes variable
+ * Fix all references to config.h
+ * Introduce netns API
+ * Introduce netdev API
+ * Introduce tundev API
+ * configure --enable-libmnl: Add libmnl to libosmocore.pc.in Requires
+ * netdev: Fix compilation building with --disable-libmnl
+ * tun: Fix potential unpaired call to osmo_netns_switch_exit()
+ * gprs_ns2_fr: use osmo_netdev to monitor and operate network device
+ * debian/rules: Fix moved path crc*gen.c
+
+ [ Mychaela Falconia ]
+ * gsm48_ie: fix parsing of Bearer capability IE without octet 3a
+
+ [ Harald Welte ]
+ * sim/class_tables: Add GET IDENTITY, SUSPEND UICC, EXCHANGE CAPABILITIES
+ * allocate VTY port number 4270 for osmo-isdntap
+ * logging.h: Allocate DLM2PA and DLM2UA for libosmo-sigtran
+ * Support building with -Werror=strict-prototypes / -Werror=old-style-definition
+ * Disable -Wstrict-prototypes for logging_vty_add_cmds()
+ * vty/logging.h: Avoid -Werror=pragmas error in C++ code
+ * Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition
+ * Fix typos in copyright statements.
+ * gsmtap.h: Add definitions for various ISDN sub-types
+ * create libosmoisdn sub-library
+ * isdndlc: Fix documentation
+
+ [ Oliver Smith ]
+ * gsm0808_enc_aoip_trasp_addr: add length check
+ * utils/osmo-stat-dummy: check for ENABLE_UTILITIES
+ * d/control: libosmocore-dev: depend on libmnl-dev
+ * gsm_08_08.h: fix typo in GSM0808_DATA_FULL_PREF
+
+ [ Alexander Couzens ]
+ * gprs_ns2: add vty `nse <0-65535> restart sns`
+ * gb: add bssgp2_enc_flush_ll encode FLUSH-LL
+
+ [ Neels Hofmeyr ]
+ * enrich API doc for gsm0808_speech_codec
+ * gsm0408_test: do not print errno in expected output
+ * comments: gsm_08_08.h: AMR cfg: explain in much more detail
+ * osmo_tdef_get(): clarify API doc on val_if_not_present
+
+ [ Max ]
+ * Ignore osmo-ns-dummy
+ * Add function to guess AF_UNSPEC address
+ * Add osmo_sockaddr_strs_to_str()
+ * cosmetic: remove trailing space
+ * cosmetic: make linter happy with LAPD code
+ * LAPD: log unknown format value
+ * LAPD: use bool for T200 reset flags
+ * msgb: expand copy test
+ * doc: correct typo in ticket reference
+ * msgb: introduce extended copy functions
+ * Add define for unset Frame Number
+ * LAPD: move tx_hist code into static functions
+ * osmo-ns-dummy: add ctrl interface
+ * jenkins_arm.sh: disable external tests
+ * vty: fix doc typo
+ * telnet_init_dynif: propagate error
+ * telnet_init_dynif: don't allow negative port
+ * rate_ctr: convert to timerfd
+ * rate_ctr: drop rate estimation code
+ * osmo-stat-dummy: add rate counters and statsd tester
+ * ctrl: add optional port to bind command
+ * ASCI: add VBS/VGCS support to BTS features list
+ * SI: add RR short PD message types
+ * Fixup .gitignore
+ * SI: add missing header
+ * Add SI10 support
+
+ [ neels ]
+ * Revert "Add osmo_sockaddr_strs_to_str()"
+ * Revert "Add function to guess AF_UNSPEC address"
+
+ [ Daniel Willmann ]
+ * use_count: Return if uc is NULL
+
+ [ Keith Whyte ]
+ * Fix LCLS-CONNECT-CONTROL generation
+ * Fix Typo in gsm0808_msgt_names[]
+
+ [ Philipp Maier ]
+ * msgb: assert msgb->lXh to be not NULL
+ * msgb: do not use msgb_l4 instead of msgb_sms
+ * bits: fix typo
+ * uitils: add floored and euclidian modulo functions
+ * gsm0408_test: add unittest for gsm_gsmtime2fn()
+ * gsm_utils: improve gsm_gsmtime2fn()
+
+ [ arehbein ]
+ * gb/vty: Show if NSVC is blocked locally by O&M/vty or by remote
+ * libosmocore: Deprecate APIs telnet_init(_dynip)()
+ * libosmocore: Transition to use of 'telnet_init_default'
+
+ [ Eric ]
+ * bitgen test: fix concat macro
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 11:20:41 +0100
+
+libosmocore (1.7.0) unstable; urgency=medium
+
+ [ Vadim Yanitskiy ]
+ * .gitignore: add utils/osmo-aka-verify binary
+ * tests/vty: fix use of GNU 'missing =' extension in designator
+ * debian/control: minimum version of libtalloc-dev must be >= 2.1.0
+ * tests/tdef: rename the binaries to end with '_test'
+ * tdef: fix wrong path in documentation: tests/vty -> tests/tdef
+ * bitvec_read_field(): indicate errors using errno
+ * bitvec_read_field(): fix incorrect bit-shift issue found by UBSan
+ * bitvec_read_field(): optimize by expanding bytenum_from_bitnum()
+ * tests/testsuite.at: ensure empty stderr for the bitvec_test
+ * VTY: enable talloc introspection for OTC_GLOBAL
+ * VTY: implement 'no log gsmtap [HOSTNAME]' command
+ * contrib/libosmocore.spec.in: mention osmo-config-merge in utils
+ * gsm_7bit_encode_n(): use regular malloc() instead of calloc()
+ * tests/logging: ensure both stream and wqueue modes are tested
+ * tests/logging: also test printing the filename information
+ * logging: fix coding style issues in _output_buf()
+ * logging: fix printing of '\0' when filename printed last
+ * tests/logging: merge both logging_test_{stream,wqueue}.err
+ * core/utils.h: add OSMO_LIKELY / OSMO_UNLIKELY macros
+ * core/utils.h: wrap OSMO_ASSERT() with do { ... } while (0)
+ * core/msgb.h: make use of OSMO_LIKELY / OSMO_UNLIKELY
+ * core/utils.h: make use of OSMO_LIKELY in OSMO_ASSERT
+ * libosmocodec: osmo_hr_check_sid(): simplify the logic
+ * contrib/jenkins_amd64.sh: remove FreeBSD specific quirks
+ * bssmap_le: support additional IEs in Perform Location Request
+ * Use internal <osmocom/core/talloc.h> everywhere
+ * coding: fix comments for detect_afs_sid_{first,update,onset}
+ * coding: cosmetic: move 'dtx_prev' to the scope where it's used
+ * coding: use switch statement in gsm0503_tch_a[fh]s_decode_dtx()
+ * coding: properly handle AFS_SID_UPDATE frames in DTX mode
+ * coding: prevent marking FACCH frames as AMR's special DTX frames
+ * tests/dtx: test detection/decoding of A[FH]S_SID_UPDATE
+ * tests/dtx: test tagging of FACCH/[FH] frames
+ * coding: fix decoding of AHS_SID_UPDATE frames (BER ~50%)
+ * coding: do not reset codec ID on receipt of DTX frames
+ * coding: add gsm0503_detect_a[fh]s_dtx_frame2()
+ * coding: separate gsm0503_tch_a[fh]s_decode_inband()
+
+ [ Eric ]
+ * fix isdigit taking unsigned as input
+ * logging: allow disabling macros using a new define: LIBOSMOCORE_NO_LOGGING
+
+ [ Daniel Willmann ]
+ * bssgp_bvc_fsm: Move log message to the correct place
+ * bssgp_bvc_fsm: Add a hook to notify when a reset was acknowledged
+
+ [ Harald Welte ]
+ * logging: Fix memory leak in case async log write queue overflows
+ * write_queue: Document it that caller is responsible if enqueue fails
+ * gsmtap: Add gsmtap_sendmsg_free() as alternative to gsmtap_sendmsg()
+ * Introduce CRC and FSM for IuUP (user plane) as used in 3G RTP data
+ * tcp_stats: fix compilation on CentOS 7
+ * iuup: Fix signed/unsigned loop counter control flow issue
+ * src/conv.c: Align better with Osmocom coding style
+ * bitvec: Fix -Wsign-compare warnings
+ * utils: Fix -Wsign-compare warnings
+ * log_taget_find() should use enum log_target_type, not int
+ * bits.c: Fix -Wsign-compare warnings
+ * socket, select: Fix -Wsign-compare warnings
+ * msgb: Fix -Wsign-compare warnings
+ * osmo_libusb: Fix NULL check in osmo_usb_removed_cb()
+ * osmo_libusb: Use libusb_get_pollfds() to get initial file descriptors
+ * osmo_libusb: Print log message on libusb initialization error
+ * gsm0808: Test if we properly decode a SRVCC cell identifier list
+ * usb: Match device by VID/PID without path/addr if it is unique
+ * clean-up pkg-config files: Make use of "Requires" as documented
+ * debian/control: libosmocore-dev must depend on libsctp-dev and libusb-1.0-0-dev
+ * libosmo{gb,vty}.pc.in: Add talloc to 'Requires'
+ * libosmocore.spec: Make libosmocore-devel require libsctp
+ * libosmovty: Link libosmovty against libpthread
+ * vty: Support platforms that don't support pthread_getname_np()
+ * vty: Add a 'skip-zero' version of 'show stats' and 'show rate-counters'
+ * stats: Functions with no arguments should specify(void)
+ * stats: Avoid NULL pointer deref in allocation failure paths.
+ * stats: don't try to save unknown stats reporter types
+ * fsm_vty: use unsigned int when left-shifting 31 bits!
+ * update git URLs (git -> https; gitea)
+
+ [ Eric Wild ]
+ * logging: make LIBOSMOCORE_NO_LOGGING work as expected
+
+ [ Oliver Smith ]
+ * treewide: remove FSF address
+ * select_main: don't poll forever during shutdown
+ * Cosmetic: linuxlist.h: fix misleading comment
+
+ [ Philipp Maier ]
+ * stats: fix typo
+ * stat_item: tolerate NULL pointer argument in osmo_stat_item_group_free
+ * select: gather statistics for TCP connections
+ * stats_tcp: use a default batch size of 5 instead of 1
+ * stats_tcp: fix stats item identifier
+ * gsm23003: fix docstring for osmo_plmn_from_bcd()
+ * iuup: do not use illegal characters in state/event names.
+ * logging: log to stderr when logging is not initialized
+ * reader: more meaningful null pointer check in get_sw
+
+ [ Pau Espin Pedrol ]
+ * include/: Adapt some headers to match contrib/struct_endianess.py format
+ * logging: Fix Not enough tailroom msgb_put in _output_buf callers
+ * osmo-release.sh: Use variable containing bumpversion path everywhere
+ * iuup: Fix decoding of 1byte-length subflow size fields
+ * iuup: Submit RNL-STATUS-Initialization.ind upon rx of Init
+ * iuup: Improve CRC checksum error logging
+ * gsm: [ABI BREAK] Support CellId SAI, change CellId CGI-PS id number
+ * gsm: lapd_core: Change log line NOTICE->INFO
+ * gsm0808_test: Add new unit test showing dec error
+ * gsm0808: Fix decoding of IE GSM0808_IE_LCS_CLIENT_TYPE
+ * cosmetic: gsm_08_08.h: Add space between assignment sides
+ * gsm0808: Add missing IEs in bss_att_tlvdef
+ * jenkins: Validate IEs are added to tlv_definition
+ * libosmocore.pc.in: put libsctp in Requires.private
+ * configure: Support libsctp < 1.0.17 without libsctp.pc
+ * rsl: Fix tlv_parse of IPAC_DLCX_IND message
+ * jenkins: Validate gsm 08.58 IEs are added to tlv_definition
+ * gsm: Introduce helper rach_tx_integer_raw2val()
+ * cosmetic: logging.h: fix indentation
+ * gsm_12_21.h: Fix abis_nm_avail_state InTest and Failed values
+ * gsm_12_21.h: Add header description pointing to TS files
+ * coding: Refactor function to avoid gcc false positive warn
+ * osmo-arfcn: Fix false positive in gcc 12.1.0
+ * coding: Use ARRAY_SIZE macro
+ * iuup: Rework API to support RFCI IDs != RFCI index
+ * cosmetic: iuup.h: Fix indentation
+ * iuup: Fix IPTIs_present not set to 0 if no IPTIs received
+ * cbsp: Add enum and value string for Cause
+ * iuup: Add missing state to bitmask for st SMpSDU_Data_Transfer_Ready
+ * iuup: Drop unused events
+ * tests/iuup: Showcase IuUP stack not answering subsequent Init msgs
+ * iuup: Fix Handling of subsequent Initialization msgs
+ * vty: command.c: Add assert
+
+ [ Sylvain Munaut ]
+ * conv: Fix the traceback for tail biting codes
+ * build: Disable libusb and libmnl for embedded builds
+
+ [ Neels Hofmeyr ]
+ * PFCP: add DLPFCP and osmo-upf port numbers
+ * ports.h: add osmo-pfcp-tool ports
+ * add osmo_sockaddr_to_str_c(), osmo_sockaddr_to_str_buf2()
+ * log: socket.c: rather use the osmo_sockaddr_str _FMT
+ * add osmo_quote_str_buf3, osmo_escape_str_buf3
+ * add osmo_sockaddr_from/to_octets()
+ * follow-up to osmo_sockaddr_from/to_octets()
+ * BSSAP: HO Request Ack: add missing Codec List (BSS Supported)
+ * add osmo_sockaddr_set_port()
+ * osmo_time_cc: rate_ctr presence should not affect counting
+ * cosmetic tweak in Makefile.am
+
+ [ Michael Iedema ]
+ * stats: use tcp stat names as provided
+
+ [ Karsten Ohme ]
+ * APDU parsing support for GlobalPlatform
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 17:00:51 +0200
+
+libosmocore (1.6.0) unstable; urgency=medium
+
+ [ Pau Espin Pedrol ]
+ * osmo-release.sh: Check configure.ac dependency versions match those in rpm *.spec.in
+ * osmo-release.sh: Drop repeated DRY_RUN early exit
+ * osmo-release.sh: Check LIBVERSION matches rpm *.spec.in
+ * osmo-release.sh: Properly rearrange var init and sanity checks
+ * tests/gb: Fix printf format errors on ARM 32 bits
+ * Skip RPM checks if no *.spec.in available
+ * gsm_08_08.h: Add extra field elements defined in other sections
+ * cosmetic: tlv_parser: fix typo in func description
+ * cosmetic: Fix trailing whitespace
+ * gsm: Support Sending Last EUTRAN PLMN Id in Handover Required
+ * cosmetic: fix typo in comment
+ * gsm0808: Introduce gsm0808_old_bss_to_new_bss_info_att_tlvdef
+ * osmo_timer_pending: Make arg const
+ * gb: Fix naming and export symbol bssgp_enc_rim_pdu
+ * Revert "gb: Fix naming and export symbol bssgp_enc_rim_pdu"
+ * gb: Fix missing exporting symbol bssgp_encode_rim_pdu
+ * vty: Implement missing public API host_config_file()
+ * vty: Introduce API vty_read_config_filep
+ * range_enc_determine_range(): Don't dereference array on size=0
+ * stat,rate_ctr: Introduce new API to get counter at given index
+ * Use new stat item/ctr getter APIs
+ * stat,rate_ctr: Allow setting group name and use it at report time
+ * ns2: Use NSVC bufid in stats report
+ * ipaccess: Add new osmo extension IPAC_PROTO_EXT_PCU
+ * bts_feature: Introduce feature to speak to PCU
+ * msgb_alloc_headroom: Change size args to be uint16_t
+ * ctrl: Pre-calculate required size before allocating msgb
+ * ctrl: Support recovering from short write
+ * cosmetic: gsm_12_21.h: Fix trailing whitespace
+ * Rename osmo dyn ts enums
+ * bts_feature: Introduce feature to confiure dyn ts as sdcch8
+ * Make gcc 11.1.0 false positivies happy
+ * cosmetic: gb/gprs_ns.c: fix trailing whitespace
+ * gb/bssgp: Abort clearly if backward-compat API is used in wrong way
+ * utils: Fix c++ warn in OSMO_STRBUF_APPEND
+ * cosmetic: Fix missing space in comment
+ * gsm_08_58.h: Extend IPA Power Control Params IEs to pass C/I params
+ * gsm: Mark gsm0858_rsl_ul_meas_enc() pointer param const
+ * cosmetic: gsm: meas_rep.h: Fix typo in comment
+ * logging: Change LLAPD category color to purple-like one
+ * {ctrl,vty}/ports.h: Allocate ports for osmo-hnodeb
+ * configure.ac: Depend on talloc 2.1.0
+ * logging: Fix double lock of log_tgt_mutex
+ * contrib/libosmocore.spec.in: Depends on talloc 2.1.0
+ * osmo-release.sh: Blacklist script file from LIBVERS matches
+
+ [ Oliver Smith ]
+ * osmo-release.sh: support epoch
+ * tests/stats: enable logging in test output
+ * stats: log error when missing stats values
+ * Revert "stats: log error when missing stats values"
+ * stat_item: make next_id argument name consistent
+ * stat_item: add comment with struct overview
+ * stat_item: make value ids item specific
+ * stats_test: restore stat_item_get_next asserts
+ * stats: log error when missing stats values (v2)
+ * stats: have_value is a bool, not int
+ * vty: add "show uptime"
+ * vty: add "show pid"
+ * vty: add "shutdown"
+ * vty: show uptime: use timespecsub
+ * vty: clear screen with ^L
+ * vty/ports: prepare formatting for a long define
+ * vty/ports: move pcap server/client ports
+ * tests: add 'make update_exp' target
+ * tests/stats: show how last item sent may be wrong
+ * stats: send real last value if no new values come
+
+ [ Alexander Couzens ]
+ * gprs_bssgp: add support for SGSN oriented RESET
+ * gprs_bssgp: use BVCI_SIGNALLING/BVCI_PTM instead of 0/1
+ * gprs_ns2: don't OSMO_ASSERT() while freeing NS-VC.
+ * gprs_ns2: rework logging of Rx and Tx NS PDU
+ * gprs_ns2: always use the same method to print NSVCs
+ * gprs_ns2_vty: hide dynamic NSE information when ask for persistant only
+ * gprs_ns2_vty: make the `show ns entities` and `show ns binds` look similiar
+ * gprs_ns2: dump_nsvc: correct indention
+ * gprs_ns2: add vty command `nsvc <nsvci> reset`
+ * gprs_ns2: fix memory leaks when receiving SNS or invalid packets
+ * gprs_ns2: vty: remove a white space in `show binds`
+ * gprs_ns2: nsvc_fsm: reorder notification st_alive_on_enter()
+ * gprs_ns2: sns: ensure the sns->alive state is correct
+ * gprs_ns2: sns: remove the initial SNS NSVC if it's not part
+ * gprs_ns2_vty: print a response to vty `nsvc <nsvci> (block|unblock|reset)
+ * gprs_ns2: fix nsvc block and unblock vty command
+ * gprs_ns2: SNS: allow transition missing transition GPRS_SNS_ST_UNCONFIGURED
+ * gprs_ns2: add functions for SNS add/del/change-weight messages
+ * gprs_ns2: sns: fix del bind()
+ * gprs_ns2: vty: fix removing a bind from a SNS
+ * gprs_ns2: rework id strings of nsvcs
+ * gprs_ns2_sns: move selection of the next bind into own function
+ * gprs_ns2_sns: bss: improve validation of configuration
+ * gprs_ns2_sns: refactor local and remote entries into a struct
+ * gprs_ns2_sns: refactor ns2_clear_ipv46_entries_local to use new elems functions
+ * gprs_ns2_sns: use struct ns2_sns_elems in add/update/remove remote_elems
+ * gprs_ns2_sns: move gss->remote specific check out of add_ip4_elem/add_ip6_elem
+ * gprs_ns2_sns: add check for duplicates to add_ip6_elem()
+ * gprs_ns2_sns: refactor ip4_weight_sum/ip6_weight_sum
+ * gprs_ns2_sns: refactor nss_weight_sum_data -> ip46_weight_sum_data
+ * gprs_ns2_sns: replace ns2_sns_type with address family
+ * gprs_ns2_sns: bss: set gss->family
+ * gprs_ns2: use llist_add_tail to keep order
+ * gprs_ns2: fix missing notify towards the NSE when NSVC become blocked
+ * gprs_ns2_vc_fsm: reset the ALIVE response time when stopping test
+ * gprs_ns2: fix crash when changing the MTU
+ * gprs_ns2: fix check of MTU changes for frame relay
+ * gprs_ns2: correct mtu value in the log line
+ * gprs_ns2: use gprs_ns2_free_bind() to clean up a bind
+ * gprs_ns2: fix wrong format string in Tx Size logline
+ * gprs_ns2_vty: dump_nsvc: change output depending on NSVCI
+ * gprs_ns2: ensure the NSE becomes dead when FR link went down
+ * gprs_ns2_udp: don't start the NSVC fsm for SNS
+ * gprs_ns2_sns: refactor SNS failures into a function
+ * gprs_ns2_sns: free the NSE if the SIZE PDU is not valid
+ * gprs_ns2: add recursive anchor to protect against double free
+ * gprs_ns2: move sns_event into internal.h to direct emit events
+ * gprs_ns2_sns: rework sns clean up
+ * gprs_ns2: use an event to free the nsvscs when using SNS
+ * gprs_ns2: gprs_ns2_free_bind() should remove itself before removing nsvcs
+ * gprs_ns2: don't use llist_for_each when freeing an element
+ * gprs_ns2_sns: implement local change weight procedure
+ * gprs_ns2_sns: implement outbound SNS ADD procedures
+ * gprs_ns2_sns: implement outbound SNS DEL procedures
+ * gprs_ns2: also prevent recursive events when SGSN side cleans up
+ * gprs_ns2: calculate the nse->*_sums before notifing the sns fsm
+ * gprs_ns2: add correct filename/linenr to sns failed log message
+ * gprs_ns2: improve reselection protection
+ * gprs_ns2_sns: ensure the SNS fsm behave correct when no signalling NSVCs are present
+ * gprs_ns2: ensure the incoming NSVC is also the outgoing NSVC
+ * ns2: nsvc: reject UNITDATA when the remote BLOCK'ed
+ * ns2: fix a crash when receiving a SIZE while configured
+ * gprs_ns2: fix NS STATUS validation
+ * gprs_ns2: fix a white space
+ * gprs_ns2: nsvc: react on STATUS PDUs with cause code NSVC UNKNOWN/NSVC BLOCKED
+ * vty: add vty_out_uptime() print the uptime to the vty
+ * ns2: nse: add a uptime/downtime to track the last state change
+ * ns2: nsvc: add a uptime/downtime to track the last state change
+ * ns2: message: BLOCK/BLOCK ACK allow to use a given NSVCI instead of using the nsvc nsvci
+ * ns2: ensure the NSVC is in the correct mode for NSVC UNKNOWN/NSVC BLOCKED cause codes
+ * ns2: fsm: add comment don't answer on a STATUS with a STATUS
+ * ns2: message: allow to pass a foreign NSVCI to STATUS PDU
+ * ns2: correct parse a STATUS PDU which was received over a different NSVC
+ * ns2: improve log line when receving a PDU with wrong NSE
+ * ns2: don't forward an invalid RESET PDU to the FSM
+ * ns2: correct parse a BLOCK PDU which was received over a different NSVC
+ * include: add enum for UTRAN cipher
+
+ [ Harald Welte ]
+ * cosmetic: ssn: some more comments
+ * gprs_ns2: Log all transmitted SNS messages
+ * gprs_ns2_sns: Dispatch inbound SNS-ACK to FSM
+ * gprs_ns2: Remove any references to DNS; we use DLNS in NS2.
+ * gprs_ns2: Pass peer/remote sockaddr argument to ns2_create_vc()
+ * TODO-RELEASE: Request increasing _LAST_OSMOVTY_NODE next release
+ * gprs_ns2_sns: Implement error log in case no binds found for NSE
+ * gprs_ns2_sns: Remove TODO (spec agrees, the correct cause code is used)
+ * gprs_ns2_vty: Clarify VTY help string wording
+ * gprs_ns2_sns: refactor ns2_sns_st_size_onenter()
+ * gprs_ns2_sns: Unify handling of SNS-CONFIG for IPv4 + IPv6
+ * gprs_ns2: Encapsulate setting NSE dialect
+ * gprs_ns2_sns: Split allstate action in generic and BSS-specific part
+ * utils: osmo-aka-verify to verify UMTS AKA (SIM side)
+ * osmo-aka-verify: Fix use case with OP and not OPc
+ * gprs_ns2_sns: Support for SGSN-side IP-SNS
+ * gprs_ns2_sns: Add some more OSMO_ASSERT about BSS role
+ * gprs_ns2_sns: Rename BSS-side states to include 'bss' in name
+ * gprs_ns2: Introduce gprs_ns2_create_nse2() for SGSN side SNS
+ * gprs_ns2_vty: Allow creating NSE in sgsn-role
+ * gprs_ns2_sns: Don't clear remote IP endpoints in SGSN role
+ * gprs_ns2_sns: Verify mandatory IE presence in incoming SNS-SIZE
+ * gprs_ns2_sns: SNS-SIZE contains the actual number of local endpoints
+ * gprs_ns2_sns: Assume the SGSN has a very large number of max. NSVC
+ * gprs_ns2_sns: Implement checks during processing of inbound SNS-SIZE
+ * gprs_ns2_vty: Permit VTY configuration of bind->accept_sns
+ * gprs_ns2: dynamic NS-VC + NSE creation for IP-SNS in SGSN role
+ * gprs_ns2_sns: Allow VTY configuration of default binds for IP-SNS
+ * gprs_ns2: Add comments explaining the nsvc->sns_only field
+ * gprs_ns2_vc_fsm: In IP-SNS/ALIVE mode, initial state is ALIVE/UNBLOCKED
+ * gprs_ns2: Actually start Tns-test after SNS-CONFIG creates NS-VC
+ * gprs_ns2_sns: Compute local endpoints before using them
+ * gprs_ns2_sns: remove code duplication in create_missing_nsvcs()
+ * gprs_ns2_sns: Don't create NS-VCs for binds outside the NSE
+ * gprs_ns2_sns: Fix memory leak when creating ip[46]_local arrays
+ * sim: Obtain card ATR when opening the card
+ * sim: Remove 'printf' from library code
+ * frame_relay: Export osmo_fr_network_free()
+ * ns2: Dump frame relay state to VTY during "show ns"
+ * socket: Introduce osmo_sock_set_dscp() to set socket DSCP value
+ * ns2: Fix setting the DSCP value.
+ * socket: Introduce osmo_sock_set_priority() helper function
+ * socket: reduce code duplication, introduce socket_helper_tail()
+ * socket: QoS support for all our socket init functions
+ * socket: IPv6 support for osmo_sock_set_dscp()
+ * gprs_ns2: Fix yet another DSCP vs. TOS mix-up
+ * gprs_ns2_frgre: Ensure DSCP is sin premitted value range
+ * ns2: migrate from osmo_sock_set_dscp() to OSMO_SOCK_F_DSCP()
+ * gprs_ns: Fix another DSCP vs. TOS mistake in old NS code
+ * ns2: Allow setting the socket priority for a UDP bind
+ * Fix ipa_ccm_make_id_resp_from_req
+ * Fix ipa_ccm_make_id_resp_from_req to work at all
+ * ipa_ccm_make_id_resp: Make it work at all
+ * ns2: change the 'priority' setting name to 'socket-priority'
+ * osmo_sock_*_ofd(): Mark OSMO_FD_WRITE on non-blocking connect()
+ * sim: Add osim_card_{reset,close}() API
+ * osmo-auc-gen: Permit specifying the SQN in hex (0x12345) format
+ * copy base64 implementation from mbedtls
+ * base64: Migrate over to osmocom
+ * base64: reformat using Lindent to conform to our coding style
+ * osmo-auc-gen: Print RFC3310 IMS HTTP-AKA style base64 nonce/res
+ * logging: Change stderr + file target to use non-blocking write
+ * logging: Avoid memcpy from stack to msgb in _file_output()
+ * logging: Attempt a synchronous, non-blocking write first (file, stderr)
+ * rate_ctr: Make it safe to call rate_ctr_init() several times
+
+ [ Daniel Willmann ]
+ * stats: Ensure that each osmo_stat_item only reports once per interval
+ * tlv: Fix length returned by t{l16,16l}v_put
+ * stats_vty: Improve generation of osmo counters
+ * ns2: Ignore NSVC with data_weight 0 for data
+ * Aggregate NSVC stats inside the NSE
+ * ns2: Avoid use-after-free when SGSN-side non-persistent SNS-NSE fails
+ * frame_relay, gprs_ns2_fr: Fix log messages, remove unused struct
+
+ [ Vadim Yanitskiy ]
+ * utils/osmo-aka-verify: fix swapped CK/IK arguments
+ * vty/logging: use consistent quiting in warning messages
+ * vty/logging: ensure consistent '%' prefix for warnings
+ * vty/logging: logp: properly handle library specific sub-systems
+ * protocol/gsm_08_58.h: add RSL_CMOD_SP_{GSM4,GSM5,GSM6}
+ * protocol/gsm_08_58.h: add more 'Channel rate and type' values
+ * protocol/gsm_08_58.h: add asymmetric CSD data rates
+ * gsm/abis_nm: add missing NM_OC_IPAC_* value-string entries
+ * rsl: make rsl_dec_chan_nr() more readable, use RSL_CHAN_NR_MASK
+ * gsm_08_58.h: add Osmocom specific Bm/Lm CBITs for VAMOS
+ * gsm_08_58.h: add mask for Osmocom specific VAMOS C-bits
+ * fsm: cosmetic: fix weird spacing in osmo_fsm_inst_alloc()
+ * stats_vty: also show rate counter group name (if present)
+ * bts_features: add feature for BCCH carrier power reduction mode
+ * fix rsl_chan_nr_str_{buf,c}(): enlarge the buffer size
+ * utils: remove misleading comments for osmo_hexdump[_nospc]_c()
+ * utils: introduce osmo_talloc_replace_string_fmt()
+ * gsm_08_58: extend struct abis_rsl_osmo_temp_ovp_acch_cap
+ * Revert "Prevent GCR encoder/decoder functions from being used directly"
+ * gsm/protocol/gsm_44_004.h: fix missing include of 'endian.h'
+ * gsm/protocol/gsm_04_08.h: add gsm48_meas_res_is_valid()
+ * tests/stats: add VTY transcript tests
+ * stats: use llist_add_tail() in osmo_stats_reporter_alloc()
+ * stats: allow configuring reporter's name in the VTY
+ * stats: cosmetic: print 'stats interval' before the reporters
+ * stats: don't mark reporter as 'disable' beforehand
+ * stats: clarify error messages in cfg_no_stats_reporter_{statsd,log}
+
+ [ Neels Hofmeyr ]
+ * add BTS_FEAT_VAMOS
+ * deprecate osmo_bts_feature_name(), add osmo_bts_features_desc()
+ * add osmo_bts_features_names: short BTS feature strings
+ * RR: add VAMOS channel modes
+ * RR: add missing Extended TSC Set IE
+ * gsm48_mr_cfg_from_gsm0808_sc_cfg(): drop bitmask without effect
+ * fix default_timeout type of osmo_tdef_fsm_inst_state_chg default_timeout
+ * add RSL_IE_OSMO_TRAINING_SEQUENCE
+ * add RSL_CMOD_CRT_OSMO_TCH_VAMOS_Bm, RSL_CMOD_CRT_OSMO_TCH_VAMOS_Lm
+ * fixup for gsm48_chan_mode_to_non_vamos()
+ * osmo_select_shutdown_request(): allow finishing pending writes on SIGTERM
+ * add Kc128 to gsm0808 Create Ciphering Command
+ * gsm0808: add Kc128 to Handover Request
+ * add fixme: enforce 8 byte length of Kc
+ * fix api doc of osmo_identifier_sanitize_buf()
+ * vty 'stats reset': do not reset stat_items
+ * utils: add osmo_str_to_int() and osmo_str_to_int64()
+ * add osmo_stat_item_get_group_by_name_idxname()
+ * CTRL: expose stat_item groups on CTRL
+ * stat_item: cosmetic: s/desc/group_desc in osmo_stat_item_group_alloc()
+ * stats_test: assert counter and stat item val counts separately
+ * refactor stat_item: get rid of FIFO and "skipped" error
+ * refactor stat_item: report only changed values
+ * cosmetic: get rid of 3 deprecation warnings
+ * revisit some calls of strtol(), stroul(), strtoull()
+ * add osmo_time_cc, moved from osmo-bsc
+
+ [ Eric ]
+ * kdf: add key derivation functions
+ * vty: allow flushing
+ * gsmtap: allow 127.0.0.x local listeners
+
+ [ Michael Iedema ]
+ * ns2: use same name in ctr_group as stat_item_group
+
+ [ Philipp Maier ]
+ * control_cmd: fix typo
+ * vty: make function cmd_range_match() public
+ * linuxlist: add macro to get last element of a list
+ * command: fix sourcecode formatting
+ * codec: add missing osmo_amr_type_name function.
+ * gsm0503_coding: use ahs tables when encoding ahs codec id
+ * rsl: add new RSL IE to signal temporary overpower
+ * bts_features: Add new feature to indicate support for temporary overpower
+ * gsm_08_58: fix sourcecode formatting
+ * gsm_08_58: fix sourcecode formatting
+
+ [ Keith ]
+ * GPRS: Add PDP_TYPE_N_IETF_IPv4v6
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 13:08:22 +0100
+
libosmocore (1.5.1) unstable; urgency=medium
[ Pau Espin Pedrol ]
diff --git a/debian/compat b/debian/compat
index ec635144..f599e28b 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-9
+10
diff --git a/debian/control b/debian/control
index b3191328..5fb26cbc 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,8 @@ Source: libosmocore
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Section: libs
Priority: optional
-Build-Depends: debhelper (>= 9),
+# liburing-dev: don't try to install it on debian 10 and ubuntu 20.04
+Build-Depends: debhelper (>= 10),
autotools-dev,
autoconf,
automake,
@@ -14,27 +15,29 @@ Build-Depends: debhelper (>= 9),
doxygen,
libpcsclite-dev,
pkg-config,
- libtalloc-dev,
+ libtalloc-dev (>= 2.1.0),
libsctp-dev,
libusb-1.0-0-dev,
libmnl-dev,
libsystemd-dev,
+ liburing-dev | base-files (<< 11) | ubuntu-keyring (<< 2021),
python3:native
Standards-Version: 3.9.8
-Vcs-Git: git://git.osmocom.org/libosmocore.git
-Vcs-Browser: http://git.osmocom.org/libosmocore/
+Vcs-Git: https://gitea.osmocom.org/osmocom/libosmocore
+Vcs-Browser: https://gitea.osmocom.org/osmocom/libosmocore
Homepage: https://projects.osmocom.org/projects/libosmocore
Package: libosmocore
Section: libs
Architecture: any
Multi-Arch: foreign
-Depends: libosmocodec0 (= ${binary:Version}),
+Depends: libosmocodec4 (= ${binary:Version}),
libosmocoding0 (= ${binary:Version}),
- libosmocore17 (= ${binary:Version}),
- libosmogb12 (= ${binary:Version}),
- libosmogsm16 (= ${binary:Version}),
- libosmovty9 (= ${binary:Version}),
+ libosmocore21 (= ${binary:Version}),
+ libosmogb14 (= ${binary:Version}),
+ libosmogsm20 (= ${binary:Version}),
+ libosmoisdn0 (= ${binary:Version}),
+ libosmovty13 (= ${binary:Version}),
libosmoctrl0 (= ${binary:Version}),
libosmosim2 (= ${binary:Version}),
libosmousb0 (= ${binary:Version}),
@@ -46,7 +49,7 @@ Description: Open Source MObile COMmunications CORE library (metapackage)
least) other programs that are developed in the sphere of Free Software / Open
Source mobile communication.
-Package: libosmocodec0
+Package: libosmocodec4
Section: libs
Architecture: any
Multi-Arch: same
@@ -72,7 +75,7 @@ Package: libosmocodec-doc
Architecture: all
Section: doc
Depends: ${misc:Depends},
- libosmocodec0,
+ libosmocodec4,
libjs-jquery
Description: Documentation for the osmo codec library
This is part of the libosmocore "meta"-library. The libosmocore library
@@ -115,7 +118,7 @@ Description: Documentation for the osmo coding library
.
This package contains the documentation for the libosmocoding library.
-Package: libosmocore17
+Package: libosmocore21
Section: libs
Architecture: any
Multi-Arch: same
@@ -129,19 +132,24 @@ Description: Osmo Core library
(at least) other programs that are developed in the sphere of Free Software /
Open Source mobile communication.
.
- The libosmocore17 library in particular is a collection of common code used in
+ The libosmocore 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},
- libosmocore17,
+ libosmocore21,
libjs-jquery,
libosmocodec-doc,
libosmocoding-doc,
libosmogsm-doc,
- libosmovty-doc
+ libosmoisdn-doc,
+ libosmovty-doc,
+ libosmoctrl-doc,
+ libosmogb-doc,
+ libosmosim-doc,
+ libosmousb-doc
Description: Documentation for the Osmo Core library
This is part of the libosmocore "meta"-library. The libosmocore library
contains various utility functions that were originally developed as part of
@@ -151,7 +159,7 @@ Description: Documentation for the Osmo Core library
.
This package contains the documentation for the libosmocore library.
-Package: libosmogb12
+Package: libosmogb14
Section: libs
Architecture: any
Multi-Arch: same
@@ -172,7 +180,7 @@ Package: libosmogb-doc
Architecture: all
Section: doc
Depends: ${misc:Depends},
- libosmogb12,
+ libosmogb14,
libjs-jquery
Description: Documentation for the Osmo GPRS Gb library
This is part of the libosmocore "meta"-library. The libosmocore library
@@ -183,7 +191,7 @@ Description: Documentation for the Osmo GPRS Gb library
.
This package contains the documentation for the libosmogb library.
-Package: libosmogsm16
+Package: libosmogsm20
Section: libs
Architecture: any
Multi-Arch: same
@@ -207,7 +215,7 @@ Package: libosmogsm-doc
Architecture: all
Section: doc
Depends: ${misc:Depends},
- libosmogsm16,
+ libosmogsm20,
libjs-jquery
Description: Documentation for the Osmo GSM utility library
This is part of the libosmocore "meta"-library. The libosmocore library
@@ -218,7 +226,40 @@ Description: Documentation for the Osmo GSM utility library
.
This package contains the documentation for the libosmogsm library.
-Package: libosmovty9
+Package: libosmoisdn0
+Section: libs
+Architecture: any
+Multi-Arch: same
+Depends: ${shlibs:Depends},
+ ${misc:Depends}
+Pre-Depends: ${misc:Pre-Depends}
+Description: Osmo ISDN utility library
+ This is part of the libosmocore "meta"-library. The libosmocore library
+ contains various utility functions that were originally developed as part of
+ the OpenBSC project, but which are of a more generic nature and thus useful to
+ (at least) other programs that are developed in the sphere of Free Software /
+ Open Source mobile communication.
+ .
+ The libosmoisdn library in particular is a collection of common code used in
+ various ISDN related sub-projects inside the Osmocom family of projects. It
+ includes an I.460 sub-channel multiplex and a generic LAPD core.
+
+Package: libosmoisdn-doc
+Architecture: all
+Section: doc
+Depends: ${misc:Depends},
+ libosmoisdn0,
+ libjs-jquery
+Description: Documentation for the Osmo ISDN utility library
+ This is part of the libosmocore "meta"-library. The libosmocore library
+ contains various utility functions that were originally developed as part of
+ the OpenBSC project, but which are of a more generic nature and thus useful to
+ (at least) other programs that are developed in the sphere of Free Software /
+ Open Source mobile communication.
+ .
+ This package contains the documentation for the libosmoisdn library.
+
+Package: libosmovty13
Section: libs
Architecture: any
Multi-Arch: same
@@ -239,7 +280,7 @@ Package: libosmovty-doc
Architecture: all
Section: doc
Depends: ${misc:Depends},
- libosmovty9,
+ libosmovty13,
libjs-jquery
Description: Documentation for the Osmo VTY library
This is part of the libosmocore "meta"-library. The libosmocore library
@@ -297,6 +338,21 @@ Description: Osmo SIM library
.
The libosmosim library in particular contains routines for SIM card access.
+Package: libosmosim-doc
+Architecture: all
+Section: doc
+Depends: ${misc:Depends},
+ libosmosim2,
+ libjs-jquery
+Description: Documentation for the Osmo SIM library
+ This is part of the libosmocore "meta"-library. The libosmocore library
+ contains various utility functions that were originally developed as part of
+ the OpenBSC project, but which are of a more generic nature and thus useful to
+ (at least) other programs that are developed in the sphere of Free Software /
+ Open Source mobile communication.
+ .
+ This package contains the documentation for the libosmosim library.
+
Package: libosmousb0
Section: libs
Architecture: any
@@ -314,12 +370,30 @@ Description: Osmo USB library
The libosmosub library in particular contains routines for USB device access
via libusb-1.0, integrated into the libosmocore select event loop.
+Package: libosmousb-doc
+Architecture: all
+Section: doc
+Depends: ${misc:Depends},
+ libosmousb0,
+ libjs-jquery
+Description: Documentation for the Osmo USB library
+ This is part of the libosmocore "meta"-library. The libosmocore library
+ contains various utility functions that were originally developed as part of
+ the OpenBSC project, but which are of a more generic nature and thus useful to
+ (at least) other programs that are developed in the sphere of Free Software /
+ Open Source mobile communication.
+ .
+ This package contains the documentation for the libosmousb library.
+
Package: libosmocore-dev
Architecture: any
Multi-Arch: same
Section: libdevel
Depends: libosmocore,
- libtalloc-dev,
+ libtalloc-dev (>= 2.1.0),
+ libsctp-dev,
+ libusb-1.0-0-dev,
+ libmnl-dev,
${misc:Depends}
Description: Development headers for Open Source MObile COMmunications CORE library
The header files provided by this package may be used to develop
diff --git a/debian/copyright b/debian/copyright
index b119569a..9281f03d 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,6 +1,6 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: libosmocore
-Source: git://git.osmocom.org/libosmocore.git
+Source: https://gitea.osmocom.org/osmocom/libosmocore
Files: *
Copyright: 2008 Daniel Willmann <daniel@totalueberwachung.de>
@@ -13,30 +13,30 @@ License: GPL-2+
Files: include/osmocom/core/loggingrb.h
include/osmocom/core/strrb.h
- src/strrb.c
- src/loggingrb.c
+ src/core/strrb.c
+ src/core/loggingrb.c
Copyright: 2012-2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
License: GPL-2+
Files: include/osmocom/core/linuxrbtree.h
- src/rbtree.c
+ src/core/rbtree.c
Copyright: 1999 Andrea Arcangeli <andrea@suse.de>
2002 David Woodhouse <dwmw2@infradead.org>
License: GPL-2+
Files: include/osmocom/core/crc16.h
- src/crc16.c
+ src/core/crc16.c
Copyright: 2005 Ben Gardner <bgardner@wabtec.com>
License: GPL-2
-Files: src/utils.c
+Files: src/core/utils.c
Copyright: 2011 Harald Welte <laforge@gnumonks.org>
2011 Sylvain Munaut <tnt@246tNt.com>
2014 Nils O. Selåsdal <noselasd@fiane.dyndns.org>
License: GPL-2+
Files: src/gsm/gsm48_ie.c
- src/gsm/lapd_core.c
+ src/isdn/lapd_core.c
src/gsm/lapdm.c
Copyright: 2008,2010-2011 Harald Welte <laforge@gnumonks.org>
2009-2011 Andreas Eversberg <jolly@eversberg.eu>
@@ -100,8 +100,8 @@ Copyright: 1997,1998 Kunihiro Ishiguro
License: GPL-2+
Files: include/osmocom/core/stats.h
- src/stat_item.c
- src/stats.c
+ src/core/stat_item.c
+ src/core/stats.c
src/vty/stats_vty.c
tests/stats/stats_test.c
Copyright: 2009-2010 by Harald Welte <laforge@gnumonks.org>
diff --git a/debian/libosmocodec0.install b/debian/libosmocodec4.install
index 2676133e..2676133e 100644
--- a/debian/libosmocodec0.install
+++ b/debian/libosmocodec4.install
diff --git a/debian/libosmocore-utils.install b/debian/libosmocore-utils.install
index 9501bec9..0e411978 100644
--- a/debian/libosmocore-utils.install
+++ b/debian/libosmocore-utils.install
@@ -2,3 +2,4 @@ usr/bin/osmo-arfcn
usr/bin/osmo-auc-gen
usr/bin/osmo-aka-verify
usr/bin/osmo-config-merge
+usr/bin/osmo-gsmtap-logsend
diff --git a/debian/libosmocore.dirs b/debian/libosmocore.dirs
index 94090a39..9c10e3aa 100644
--- a/debian/libosmocore.dirs
+++ b/debian/libosmocore.dirs
@@ -5,4 +5,5 @@ usr/include/osmocom/codec
usr/include/osmocom/core
usr/include/osmocom/crypt
usr/include/osmocom/gsm
+usr/include/osmocom/isdn
usr/include/osmocom/vty
diff --git a/debian/libosmocore17.install b/debian/libosmocore21.install
index b73331b9..b73331b9 100644
--- a/debian/libosmocore17.install
+++ b/debian/libosmocore21.install
diff --git a/debian/libosmogb12.install b/debian/libosmogb14.install
index 4c474255..4c474255 100644
--- a/debian/libosmogb12.install
+++ b/debian/libosmogb14.install
diff --git a/debian/libosmogsm16.install b/debian/libosmogsm20.install
index 5e617298..5e617298 100644
--- a/debian/libosmogsm16.install
+++ b/debian/libosmogsm20.install
diff --git a/debian/libosmoisdn-doc.doc-base b/debian/libosmoisdn-doc.doc-base
new file mode 100644
index 00000000..bca6e1d0
--- /dev/null
+++ b/debian/libosmoisdn-doc.doc-base
@@ -0,0 +1,7 @@
+Document: libosmoisdn-doc
+Title: Documentation for the libosmoisdn library
+Section: Programming
+
+Format: HTML
+Index: /usr/share/doc/libosmocore/isdn/html/index.html
+Files: /usr/share/doc/libosmocore/isdn/html/*.html
diff --git a/debian/libosmoisdn-doc.install b/debian/libosmoisdn-doc.install
new file mode 100644
index 00000000..9c985503
--- /dev/null
+++ b/debian/libosmoisdn-doc.install
@@ -0,0 +1 @@
+usr/share/doc/libosmocore/isdn/
diff --git a/debian/libosmoisdn0.install b/debian/libosmoisdn0.install
new file mode 100644
index 00000000..691a3e0f
--- /dev/null
+++ b/debian/libosmoisdn0.install
@@ -0,0 +1 @@
+usr/lib/*/libosmoisdn*.so.*
diff --git a/debian/libosmosim-doc.doc-base b/debian/libosmosim-doc.doc-base
new file mode 100644
index 00000000..0c2678db
--- /dev/null
+++ b/debian/libosmosim-doc.doc-base
@@ -0,0 +1,7 @@
+Document: libosmosim-doc
+Title: Documentation for the libosmosim library
+Section: Programming
+
+Format: HTML
+Index: /usr/share/doc/libosmocore/sim/html/index.html
+Files: /usr/share/doc/libosmocore/sim/html/*.html
diff --git a/debian/libosmosim-doc.install b/debian/libosmosim-doc.install
new file mode 100644
index 00000000..2f37b409
--- /dev/null
+++ b/debian/libosmosim-doc.install
@@ -0,0 +1 @@
+usr/share/doc/libosmocore/sim/
diff --git a/debian/libosmousb-doc.doc-base b/debian/libosmousb-doc.doc-base
new file mode 100644
index 00000000..6121918b
--- /dev/null
+++ b/debian/libosmousb-doc.doc-base
@@ -0,0 +1,7 @@
+Document: libosmousb-doc
+Title: Documentation for the libosmousb library
+Section: Programming
+
+Format: HTML
+Index: /usr/share/doc/libosmocore/usb/html/index.html
+Files: /usr/share/doc/libosmocore/usb/html/*.html
diff --git a/debian/libosmousb-doc.install b/debian/libosmousb-doc.install
new file mode 100644
index 00000000..081159a9
--- /dev/null
+++ b/debian/libosmousb-doc.install
@@ -0,0 +1 @@
+usr/share/doc/libosmocore/usb/
diff --git a/debian/libosmovty9.install b/debian/libosmovty13.install
index fbf6a5fa..fbf6a5fa 100644
--- a/debian/libosmovty9.install
+++ b/debian/libosmovty13.install
diff --git a/debian/rules b/debian/rules
index f5b71ebb..db97497c 100755
--- a/debian/rules
+++ b/debian/rules
@@ -25,8 +25,20 @@ override_dh_install:
override_dh_auto_test:
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
+# Set --disable-uring for debian 10 and ubuntu 20.04
override_dh_auto_configure:
- dh_auto_configure -- --enable-static --disable-sctp-tests --enable-systemd-logging
+ set -x && \
+ CONFIGURE_FLAGS=" \
+ --disable-sctp-tests \
+ --disable-uring-tests \
+ --enable-static \
+ --enable-systemd-logging \
+ "; \
+ distro_v=$$(. /etc/os-release && echo $$VERSION_ID); \
+ if [ "$$distro_v" = 10 ] || [ "$$distro_v" = 20.04 ]; then \
+ CONFIGURE_FLAGS="$$CONFIGURE_FLAGS --disable-uring"; \
+ fi; \
+ dh_auto_configure -- $$CONFIGURE_FLAGS
override_dh_clean:
dh_clean
@@ -40,16 +52,17 @@ override_dh_clean:
$(RM) include/osmocom/core/crc32gen.h
$(RM) include/osmocom/core/crc64gen.h
$(RM) include/osmocom/core/crc8gen.h
- $(RM) src/crc16gen.c
- $(RM) src/crc32gen.c
- $(RM) src/crc64gen.c
- $(RM) src/crc8gen.c
+ $(RM) src/core/crc16gen.c
+ $(RM) src/core/crc32gen.c
+ $(RM) src/core/crc64gen.c
+ $(RM) src/core/crc8gen.c
$(RM) tests/package.m4
$(RM) tests/testsuite
$(RM) -r doc/codec/
$(RM) -r doc/core/
$(RM) -r doc/ctrl/
$(RM) -r doc/gsm/
+ $(RM) -r doc/isdn/
$(RM) -r doc/gb/
$(RM) -r doc/vty/html/
$(RM) -r doc/vty/latex/
diff --git a/include/Makefile.am b/include/Makefile.am
index f0742d5f..3578a80e 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,218 +1 @@
-BUILT_SOURCES = osmocom/gsm/gsm0503.h
-
-nobase_include_HEADERS = \
- osmocom/codec/ecu.h \
- osmocom/codec/codec.h \
- osmocom/codec/gsm610_bits.h \
- osmocom/core/application.h \
- osmocom/core/backtrace.h \
- osmocom/core/bit16gen.h \
- osmocom/core/bit32gen.h \
- osmocom/core/bit64gen.h \
- osmocom/core/bits.h \
- osmocom/core/bitvec.h \
- osmocom/core/bitcomp.h \
- osmocom/core/byteswap.h \
- osmocom/core/conv.h \
- osmocom/core/counter.h \
- osmocom/core/crc16.h \
- osmocom/core/crc16gen.h \
- osmocom/core/crc32gen.h \
- osmocom/core/crc64gen.h \
- osmocom/core/crc8gen.h \
- osmocom/core/crcgen.h \
- osmocom/core/endian.h \
- osmocom/core/defs.h \
- osmocom/core/exec.h \
- osmocom/core/fsm.h \
- osmocom/core/gsmtap.h \
- osmocom/core/gsmtap_util.h \
- osmocom/core/hash.h \
- osmocom/core/hashtable.h \
- osmocom/core/isdnhdlc.h \
- osmocom/core/it_q.h \
- osmocom/core/linuxlist.h \
- osmocom/core/linuxrbtree.h \
- osmocom/core/log2.h \
- osmocom/core/logging.h \
- osmocom/core/loggingrb.h \
- osmocom/core/stats.h \
- osmocom/core/macaddr.h \
- osmocom/core/msgb.h \
- osmocom/core/panic.h \
- osmocom/core/prbs.h \
- osmocom/core/prim.h \
- osmocom/core/process.h \
- osmocom/core/rate_ctr.h \
- osmocom/core/stat_item.h \
- osmocom/core/select.h \
- osmocom/core/sercomm.h \
- osmocom/core/signal.h \
- osmocom/core/socket.h \
- osmocom/core/statistics.h \
- osmocom/core/strrb.h \
- osmocom/core/talloc.h \
- osmocom/core/tdef.h \
- osmocom/core/thread.h \
- osmocom/core/timer.h \
- osmocom/core/timer_compat.h \
- osmocom/core/utils.h \
- osmocom/core/write_queue.h \
- osmocom/core/sockaddr_str.h \
- osmocom/core/use_count.h \
- osmocom/crypt/auth.h \
- osmocom/crypt/gprs_cipher.h \
- osmocom/ctrl/control_cmd.h \
- osmocom/ctrl/control_if.h \
- osmocom/ctrl/ports.h \
- osmocom/gprs/frame_relay.h \
- osmocom/gprs/bssgp_bvc_fsm.h \
- osmocom/gprs/gprs_bssgp.h \
- osmocom/gprs/gprs_bssgp2.h \
- osmocom/gprs/gprs_bssgp_bss.h \
- osmocom/gprs/gprs_bssgp_rim.h \
- osmocom/gprs/gprs_msgb.h \
- osmocom/gprs/gprs_ns.h \
- osmocom/gprs/gprs_ns_frgre.h \
- osmocom/gprs/gprs_ns2.h \
- osmocom/gprs/gprs_rlc.h \
- osmocom/gprs/protocol/gsm_04_60.h \
- osmocom/gprs/protocol/gsm_08_16.h \
- osmocom/gprs/protocol/gsm_08_18.h \
- osmocom/gprs/protocol/gsm_24_301.h \
- osmocom/gsm/a5.h \
- osmocom/gsm/abis_nm.h \
- osmocom/gsm/apn.h \
- osmocom/gsm/bts_features.h \
- osmocom/gsm/cbsp.h \
- osmocom/gsm/comp128.h \
- osmocom/gsm/comp128v23.h \
- osmocom/gsm/bitvec_gsm.h \
- osmocom/gsm/gan.h \
- osmocom/gsm/gsm0341.h \
- osmocom/gsm/gsm0411_smc.h \
- osmocom/gsm/gsm0411_smr.h \
- osmocom/gsm/gsm0411_utils.h \
- osmocom/gsm/gsm0480.h \
- osmocom/gsm/gsm0502.h \
- osmocom/gsm/gsm0503.h \
- osmocom/coding/gsm0503_tables.h \
- osmocom/coding/gsm0503_parity.h \
- osmocom/coding/gsm0503_mapping.h \
- osmocom/coding/gsm0503_interleaving.h \
- osmocom/coding/gsm0503_coding.h \
- osmocom/coding/gsm0503_amr_dtx.h \
- osmocom/gsm/bsslap.h \
- osmocom/gsm/bssmap_le.h \
- osmocom/gsm/gad.h \
- osmocom/gsm/gsm0808.h \
- osmocom/gsm/gsm0808_lcs.h \
- osmocom/gsm/gsm29205.h \
- osmocom/gsm/gsm0808_utils.h \
- osmocom/gsm/gsm23003.h \
- osmocom/gsm/gsm23236.h \
- osmocom/gsm/gsm29118.h \
- osmocom/gsm/gsm48.h \
- osmocom/gsm/gsm48_arfcn_range_encode.h \
- osmocom/gsm/gsm48_ie.h \
- osmocom/gsm/gsm48_rest_octets.h \
- osmocom/gsm/gsm_utils.h \
- osmocom/gsm/gsup.h \
- osmocom/gsm/gsup_sms.h \
- osmocom/gsm/i460_mux.h \
- osmocom/gsm/ipa.h \
- osmocom/gsm/lapd_core.h \
- osmocom/gsm/lapdm.h \
- osmocom/gsm/meas_rep.h \
- osmocom/gsm/mncc.h \
- osmocom/gsm/prim.h \
- osmocom/gsm/l1sap.h \
- osmocom/gsm/oap.h \
- osmocom/gsm/oap_client.h \
- osmocom/gsm/protocol/gsm_23_032.h \
- osmocom/gsm/protocol/gsm_03_40.h \
- osmocom/gsm/protocol/gsm_03_41.h \
- osmocom/gsm/protocol/gsm_04_08.h \
- osmocom/gsm/protocol/gsm_04_08_gprs.h \
- osmocom/gsm/protocol/gsm_04_11.h \
- osmocom/gsm/protocol/gsm_04_12.h \
- osmocom/gsm/protocol/gsm_04_14.h \
- osmocom/gsm/protocol/gsm_04_80.h \
- osmocom/gsm/protocol/gsm_08_08.h \
- osmocom/gsm/protocol/gsm_08_58.h \
- osmocom/gsm/protocol/gsm_09_02.h \
- osmocom/gsm/protocol/gsm_12_21.h \
- osmocom/gsm/protocol/gsm_23_003.h \
- osmocom/gsm/protocol/gsm_23_041.h \
- osmocom/gsm/protocol/gsm_29_118.h \
- osmocom/gsm/protocol/gsm_44_004.h \
- osmocom/gsm/protocol/gsm_44_318.h \
- osmocom/gsm/protocol/gsm_48_049.h \
- osmocom/gsm/protocol/gsm_48_071.h \
- osmocom/gsm/protocol/gsm_49_031.h \
- osmocom/gsm/protocol/ipaccess.h \
- osmocom/gsm/protocol/smpp34_osmocom.h \
- osmocom/gsm/rsl.h \
- osmocom/gsm/rxlev_stat.h \
- osmocom/gsm/sysinfo.h \
- osmocom/gsm/tlv.h \
- osmocom/sim/class_tables.h \
- osmocom/sim/sim.h
-
-if ENABLE_PLUGIN
-nobase_include_HEADERS += osmocom/core/plugin.h
-endif
-
-if ENABLE_MSGFILE
-nobase_include_HEADERS += osmocom/core/msgfile.h
-endif
-
-if ENABLE_SERIAL
-nobase_include_HEADERS += osmocom/core/serial.h
-endif
-
-
-if ENABLE_VTY
-nobase_include_HEADERS += \
- osmocom/vty/buffer.h \
- osmocom/vty/command.h \
- osmocom/vty/logging.h \
- osmocom/vty/stats.h \
- osmocom/vty/misc.h \
- osmocom/vty/telnet_interface.h \
- osmocom/vty/vector.h \
- osmocom/vty/vty.h \
- osmocom/vty/ports.h \
- osmocom/vty/cpu_sched_vty.h \
- osmocom/vty/tdef_vty.h \
- osmocom/ctrl/control_vty.h
-endif
-
-if ENABLE_LIBUSB
-nobase_include_HEADERS += \
- osmocom/usb/libusb.h
-endif
-
-if ENABLE_LIBMNL
-nobase_include_HEADERS += osmocom/core/mnl.h
-endif
-
-noinst_HEADERS = \
- osmocom/gsm/kasumi.h \
- osmocom/gsm/gea.h \
- osmocom/core/logging_internal.h \
- $(NULL)
-
-osmocom/core/bit%gen.h: osmocom/core/bitXXgen.h.tpl
- $(AM_V_GEN)$(MKDIR_P) $(dir $@)
- $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
-
-osmocom/core/crc%gen.h: osmocom/core/crcXXgen.h.tpl
- $(AM_V_GEN)$(MKDIR_P) $(dir $@)
- $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
-
-osmocom/gsm/gsm0503.h: $(top_srcdir)/utils/conv_gen.py $(top_srcdir)/utils/conv_codes_gsm.py
- $(AM_V_GEN)python3 $(top_srcdir)/utils/conv_gen.py gen_header gsm \
- --target-path $(builddir)/osmocom/gsm
-
-CLEANFILES = osmocom/gsm/gsm0503.h
+SUBDIRS = osmocom
diff --git a/include/osmocom/Makefile.am b/include/osmocom/Makefile.am
new file mode 100644
index 00000000..33538094
--- /dev/null
+++ b/include/osmocom/Makefile.am
@@ -0,0 +1,13 @@
+SUBDIRS = \
+ core \
+ vty \
+ codec \
+ gsm \
+ isdn \
+ crypt \
+ coding \
+ gprs \
+ ctrl \
+ sim \
+ usb \
+ $(NULL)
diff --git a/include/osmocom/codec/Makefile.am b/include/osmocom/codec/Makefile.am
new file mode 100644
index 00000000..63ae4024
--- /dev/null
+++ b/include/osmocom/codec/Makefile.am
@@ -0,0 +1,7 @@
+osmocodec_HEADERS = \
+ ecu.h \
+ codec.h \
+ gsm610_bits.h \
+ $(NULL)
+
+osmocodecdir = $(includedir)/osmocom/codec
diff --git a/include/osmocom/codec/codec.h b/include/osmocom/codec/codec.h
index cbdad75e..c5981f89 100644
--- a/include/osmocom/codec/codec.h
+++ b/include/osmocom/codec/codec.h
@@ -15,6 +15,10 @@
/* TS 101318 Chapter 5.3: 244 bits + 4bit sig */
#define GSM_EFR_BYTES 31
+/* Number of bytes of an GSM_HR RTP payload */
+#define GSM_HR_BYTES_RTP_RFC5993 (GSM_HR_BYTES + 1)
+#define GSM_HR_BYTES_RTP_TS101318 (GSM_HR_BYTES)
+
extern const uint16_t gsm610_bitorder[]; /* FR */
extern const uint16_t gsm620_unvoiced_bitorder[]; /* HR unvoiced */
extern const uint16_t gsm620_voiced_bitorder[]; /* HR voiced */
@@ -29,6 +33,8 @@ extern const uint16_t gsm690_5_9_bitorder[]; /* AMR 5.9 kbits */
extern const uint16_t gsm690_5_15_bitorder[]; /* AMR 5.15 kbits */
extern const uint16_t gsm690_4_75_bitorder[]; /* AMR 4.75 kbits */
+extern const uint8_t osmo_gsm611_silence_frame[GSM_FR_BYTES];
+
extern const struct value_string osmo_amr_type_names[];
enum osmo_amr_type {
@@ -47,6 +53,9 @@ enum osmo_amr_type {
AMR_NO_DATA = 15,
};
+static inline const char *osmo_amr_type_name(enum osmo_amr_type type)
+{ return get_value_string(osmo_amr_type_names, type); }
+
enum osmo_amr_quality {
AMR_BAD = 0,
AMR_GOOD = 1
@@ -78,8 +87,53 @@ static inline bool osmo_amr_is_speech(enum osmo_amr_type ft)
}
}
+/* SID ternary classification per GSM 06.31 & 06.81 section 6.1.1 */
+enum osmo_gsm631_sid_class {
+ OSMO_GSM631_SID_CLASS_SPEECH = 0,
+ OSMO_GSM631_SID_CLASS_INVALID = 1,
+ OSMO_GSM631_SID_CLASS_VALID = 2,
+};
+
bool osmo_fr_check_sid(const uint8_t *rtp_payload, size_t payload_len);
bool osmo_hr_check_sid(const uint8_t *rtp_payload, size_t payload_len);
+bool osmo_efr_check_sid(const uint8_t *rtp_payload, size_t payload_len);
+
+enum osmo_gsm631_sid_class osmo_fr_sid_classify(const uint8_t *rtp_payload);
+enum osmo_gsm631_sid_class osmo_efr_sid_classify(const uint8_t *rtp_payload);
+
+/*! Check if given FR codec frame is any kind of SID, valid or invalid
+ * \param[in] rtp_payload Buffer with RTP payload
+ * \returns true if the frame is an "accepted SID frame" in GSM 06.31
+ * definition, false otherwise.
+ */
+static inline bool osmo_fr_is_any_sid(const uint8_t *rtp_payload)
+{
+ enum osmo_gsm631_sid_class sidc;
+
+ sidc = osmo_fr_sid_classify(rtp_payload);
+ return sidc != OSMO_GSM631_SID_CLASS_SPEECH;
+}
+
+/*! Check if given EFR codec frame is any kind of SID, valid or invalid
+ * \param[in] rtp_payload Buffer with RTP payload
+ * \returns true if the frame is an "accepted SID frame" in GSM 06.81
+ * definition, false otherwise.
+ */
+static inline bool osmo_efr_is_any_sid(const uint8_t *rtp_payload)
+{
+ enum osmo_gsm631_sid_class sidc;
+
+ sidc = osmo_efr_sid_classify(rtp_payload);
+ return sidc != OSMO_GSM631_SID_CLASS_SPEECH;
+}
+
+bool osmo_fr_sid_preen(uint8_t *rtp_payload);
+bool osmo_efr_sid_preen(uint8_t *rtp_payload);
+
+void osmo_fr_sid_reset(uint8_t *rtp_payload);
+void osmo_hr_sid_reset(uint8_t *rtp_payload);
+void osmo_efr_sid_reset(uint8_t *rtp_payload);
+
int osmo_amr_rtp_enc(uint8_t *payload, uint8_t cmr, enum osmo_amr_type ft,
enum osmo_amr_quality bfi);
int osmo_amr_rtp_dec(const uint8_t *payload, int payload_len, uint8_t *cmr,
diff --git a/include/osmocom/codec/ecu.h b/include/osmocom/codec/ecu.h
index 668df367..64928609 100644
--- a/include/osmocom/codec/ecu.h
+++ b/include/osmocom/codec/ecu.h
@@ -6,7 +6,7 @@
#include <osmocom/core/defs.h>
#include <osmocom/codec/codec.h>
-/* ECU state for GSM-FR */
+/* ECU state for GSM-FR - deprecated version only! */
struct osmo_ecu_fr_state {
bool subsequent_lost_frame;
uint8_t frame_backup[GSM_FR_BYTES];
@@ -62,12 +62,16 @@ int osmo_ecu_frame_in(struct osmo_ecu_state *st, bool bfi,
/* generate output data for a substitute/erroneous frame */
int osmo_ecu_frame_out(struct osmo_ecu_state *st, uint8_t *frame_out);
+/* is the stream handled by this ECU currently in a DTX pause? */
+bool osmo_ecu_is_dtx_pause(struct osmo_ecu_state *st);
+
struct osmo_ecu_ops {
struct osmo_ecu_state * (*init)(void *ctx, enum osmo_ecu_codec codec);
void (*destroy)(struct osmo_ecu_state *);
int (*frame_in)(struct osmo_ecu_state *st, bool bfi,
const uint8_t *frame, unsigned int frame_bytes);
int (*frame_out)(struct osmo_ecu_state *st, uint8_t *frame_out);
+ bool (*is_dtx_pause)(struct osmo_ecu_state *st);
};
int osmo_ecu_register(const struct osmo_ecu_ops *ops, enum osmo_ecu_codec codec);
diff --git a/include/osmocom/coding/Makefile.am b/include/osmocom/coding/Makefile.am
new file mode 100644
index 00000000..713b1932
--- /dev/null
+++ b/include/osmocom/coding/Makefile.am
@@ -0,0 +1,10 @@
+osmocoding_HEADERS = \
+ gsm0503_tables.h \
+ gsm0503_parity.h \
+ gsm0503_mapping.h \
+ gsm0503_interleaving.h \
+ gsm0503_coding.h \
+ gsm0503_amr_dtx.h \
+ $(NULL)
+
+osmocodingdir = $(includedir)/osmocom/coding
diff --git a/include/osmocom/coding/gsm0503_amr_dtx.h b/include/osmocom/coding/gsm0503_amr_dtx.h
index f048a6e3..dc22ec7e 100644
--- a/include/osmocom/coding/gsm0503_amr_dtx.h
+++ b/include/osmocom/coding/gsm0503_amr_dtx.h
@@ -34,7 +34,14 @@ static inline const char *gsm0503_amr_dtx_frame_name(enum gsm0503_amr_dtx_frames
return get_value_string(gsm0503_amr_dtx_frame_names, frame);
}
-enum gsm0503_amr_dtx_frames gsm0503_detect_afs_dtx_frame(int *n_errors, int *n_bits_total, const ubit_t *ubits);
-enum gsm0503_amr_dtx_frames gsm0503_detect_ahs_dtx_frame(int *n_errors, int *n_bits_total, const ubit_t *ubits);
+enum gsm0503_amr_dtx_frames gsm0503_detect_afs_dtx_frame(int *n_errors, int *n_bits_total, const ubit_t *ubits)
+ OSMO_DEPRECATED("Use gsm0503_detect_afs_dtx_frame2() instead");
+enum gsm0503_amr_dtx_frames gsm0503_detect_ahs_dtx_frame(int *n_errors, int *n_bits_total, const ubit_t *ubits)
+ OSMO_DEPRECATED("Use gsm0503_detect_ahs_dtx_frame2() instead");
+
+enum gsm0503_amr_dtx_frames gsm0503_detect_afs_dtx_frame2(int *n_errors, int *n_bits_total,
+ int *mode_id, const sbit_t *sbits);
+enum gsm0503_amr_dtx_frames gsm0503_detect_ahs_dtx_frame2(int *n_errors, int *n_bits_total,
+ int *mode_id, const sbit_t *sbits);
/*! @} */
diff --git a/include/osmocom/coding/gsm0503_coding.h b/include/osmocom/coding/gsm0503_coding.h
index 2afa049b..3c51127e 100644
--- a/include/osmocom/coding/gsm0503_coding.h
+++ b/include/osmocom/coding/gsm0503_coding.h
@@ -50,10 +50,13 @@ int gsm0503_tch_fr_decode(uint8_t *tch_data, const sbit_t *bursts, int net_order
int gsm0503_tch_hr_encode(ubit_t *bursts, const uint8_t *tch_data, int len);
int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
+ int *n_errors, int *n_bits_total)
+ OSMO_DEPRECATED("Use gsm0503_tch_hr_decode2() instead");
+int gsm0503_tch_hr_decode2(uint8_t *tch_data, const sbit_t *bursts, int odd,
int *n_errors, int *n_bits_total);
int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
- int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft,
+ int codec_mode_req, const uint8_t *codec, int codecs, uint8_t ft,
uint8_t cmr);
int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts,
int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
@@ -63,7 +66,7 @@ int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts,
uint8_t *cmr, int *n_errors, int *n_bits_total, uint8_t *dtx);
int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
- int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, uint8_t cmr);
+ int codec_mode_req, const uint8_t *codec, int codecs, uint8_t ft, uint8_t cmr);
int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
uint8_t *cmr, int *n_errors, int *n_bits_total);
@@ -86,4 +89,36 @@ int gsm0503_rach_ext_decode_ber(uint16_t *ra, const sbit_t *burst, uint8_t bsic,
int gsm0503_sch_encode(ubit_t *burst, const uint8_t *sb_info);
int gsm0503_sch_decode(uint8_t *sb_info, const sbit_t *burst);
+int gsm0503_tch_fr96_encode(ubit_t *bursts, const ubit_t *data);
+int gsm0503_tch_fr96_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_fr48_encode(ubit_t *bursts, const ubit_t *data);
+int gsm0503_tch_fr48_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_hr48_encode(ubit_t *bursts, const ubit_t *data);
+int gsm0503_tch_hr48_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_fr24_encode(ubit_t *bursts, const ubit_t *data);
+int gsm0503_tch_fr24_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_hr24_encode(ubit_t *bursts, const ubit_t *data);
+int gsm0503_tch_hr24_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_fr144_encode(ubit_t *bursts, const ubit_t *data);
+int gsm0503_tch_fr144_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_fr_facch_encode(ubit_t *bursts, const uint8_t *data);
+int gsm0503_tch_fr_facch_decode(uint8_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_hr_facch_encode(ubit_t *bursts, const uint8_t *data);
+int gsm0503_tch_hr_facch_decode(uint8_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
/*! @} */
diff --git a/include/osmocom/coding/gsm0503_interleaving.h b/include/osmocom/coding/gsm0503_interleaving.h
index 05b5e278..fab4d3d0 100644
--- a/include/osmocom/coding/gsm0503_interleaving.h
+++ b/include/osmocom/coding/gsm0503_interleaving.h
@@ -58,4 +58,7 @@ void gsm0503_mcs8_dl_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2,
void gsm0503_mcs8_dl_interleave(const ubit_t *hc, const ubit_t *c1,
const ubit_t *c2, ubit_t *hi, ubit_t *di);
+void gsm0503_tch_f96_deinterleave(sbit_t *cB, const sbit_t *iB);
+void gsm0503_tch_f96_interleave(const ubit_t *cB, ubit_t *iB);
+
/*! @} */
diff --git a/include/osmocom/core/Makefile.am b/include/osmocom/core/Makefile.am
new file mode 100644
index 00000000..7c29ca10
--- /dev/null
+++ b/include/osmocom/core/Makefile.am
@@ -0,0 +1,103 @@
+osmocore_HEADERS = \
+ application.h \
+ backtrace.h \
+ base64.h \
+ bit16gen.h \
+ bit32gen.h \
+ bit64gen.h \
+ bits.h \
+ bitvec.h \
+ bitcomp.h \
+ byteswap.h \
+ conv.h \
+ counter.h \
+ crc16.h \
+ crc16gen.h \
+ crc32gen.h \
+ crc64gen.h \
+ crc8gen.h \
+ crcgen.h \
+ endian.h \
+ defs.h \
+ exec.h \
+ fsm.h \
+ gsmtap.h \
+ gsmtap_util.h \
+ hash.h \
+ hashtable.h \
+ isdnhdlc.h \
+ it_q.h \
+ linuxlist.h \
+ linuxrbtree.h \
+ log2.h \
+ logging.h \
+ loggingrb.h \
+ stats.h \
+ macaddr.h \
+ msgb.h \
+ netdev.h \
+ netns.h \
+ osmo_io.h \
+ panic.h \
+ prbs.h \
+ prim.h \
+ process.h \
+ rate_ctr.h \
+ stat_item.h \
+ stats_tcp.h \
+ select.h \
+ sercomm.h \
+ signal.h \
+ socket.h \
+ statistics.h \
+ strrb.h \
+ talloc.h \
+ tdef.h \
+ thread.h \
+ timer.h \
+ timer_compat.h \
+ tun.h \
+ utils.h \
+ write_queue.h \
+ sockaddr_str.h \
+ soft_uart.h \
+ time_cc.h \
+ use_count.h \
+ socket_compat.h \
+ $(NULL)
+
+if ENABLE_PLUGIN
+osmocore_HEADERS += plugin.h
+endif
+
+if ENABLE_MSGFILE
+osmocore_HEADERS += msgfile.h
+endif
+
+if ENABLE_SERIAL
+osmocore_HEADERS += serial.h
+endif
+
+if ENABLE_LIBMNL
+osmocore_HEADERS += mnl.h
+endif
+
+osmocoredir = $(includedir)/osmocom/core
+
+noinst_HEADERS = \
+ logging_internal.h \
+ $(NULL)
+
+bit%gen.h: bitXXgen.h.tpl
+ $(AM_V_GEN)$(MKDIR_P) $(dir $@)
+ $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
+
+crc%gen.h: crcXXgen.h.tpl
+ $(AM_V_GEN)$(MKDIR_P) $(dir $@)
+ $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
+
+socket_compat.h: socket_compat.h.tpl
+ $(AM_V_GEN)$(MKDIR_P) $(dir $@)
+ $(AM_V_GEN)sed -e's/XX/$(HAVE_SYS_SOCKET_H)/g' $< > $@
+
+EXTRA_DIST = socket_compat.h.tpl
diff --git a/include/osmocom/core/base64.h b/include/osmocom/core/base64.h
new file mode 100644
index 00000000..a6c97206
--- /dev/null
+++ b/include/osmocom/core/base64.h
@@ -0,0 +1,69 @@
+/**
+ * \file base64.h
+ *
+ * \brief RFC 1521 base64 encoding/decoding
+ *
+ * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *
+ * This file is part of mbed TLS (https://tls.mbed.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#pragma once
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Encode a buffer into base64 format
+ *
+ * \param dst destination buffer
+ * \param dlen size of the destination buffer
+ * \param olen number of bytes written
+ * \param src source buffer
+ * \param slen amount of data to be encoded
+ *
+ * \return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL.
+ * *olen is always updated to reflect the amount
+ * of data that has (or would have) been written.
+ *
+ * \note Call this function with dlen = 0 to obtain the
+ * required buffer size in *olen
+ */
+int osmo_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
+ const unsigned char *src, size_t slen);
+
+/**
+ * \brief Decode a base64-formatted buffer
+ *
+ * \param dst destination buffer (can be NULL for checking size)
+ * \param dlen size of the destination buffer
+ * \param olen number of bytes written
+ * \param src source buffer
+ * \param slen amount of data to be decoded
+ *
+ * \return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or
+ * MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is
+ * not correct. *olen is always updated to reflect the amount
+ * of data that has (or would have) been written.
+ *
+ * \note Call this function with *dst = NULL or dlen = 0 to obtain
+ * the required buffer size in *olen
+ */
+int osmo_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
+ const unsigned char *src, size_t slen);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/include/osmocom/core/bitXXgen.h.tpl b/include/osmocom/core/bitXXgen.h.tpl
index 258fccb6..ab54ba9c 100644
--- a/include/osmocom/core/bitXXgen.h.tpl
+++ b/include/osmocom/core/bitXXgen.h.tpl
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
diff --git a/include/osmocom/core/bitcomp.h b/include/osmocom/core/bitcomp.h
index 5faa5ea4..21679577 100644
--- a/include/osmocom/core/bitcomp.h
+++ b/include/osmocom/core/bitcomp.h
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
diff --git a/include/osmocom/core/bitvec.h b/include/osmocom/core/bitvec.h
index bd107093..1e2fe7b4 100644
--- a/include/osmocom/core/bitvec.h
+++ b/include/osmocom/core/bitvec.h
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -27,7 +23,6 @@
* \file bitvec.h */
#include <stdint.h>
-#include <osmocom/core/talloc.h>
#include <osmocom/core/defs.h>
#include <stdbool.h>
@@ -65,7 +60,7 @@ int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, enum bit_value
int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit);
int bitvec_get_bytes(struct bitvec *bv, uint8_t *bytes, unsigned int count);
int bitvec_set_bytes(struct bitvec *bv, const uint8_t *bytes, unsigned int count);
-struct bitvec *bitvec_alloc(unsigned int size, TALLOC_CTX *bvctx);
+struct bitvec *bitvec_alloc(unsigned int size, void *bvctx);
void bitvec_free(struct bitvec *bv);
int bitvec_unhex(struct bitvec *bv, const char *src);
unsigned int bitvec_pack(const struct bitvec *bv, uint8_t *buffer);
@@ -77,7 +72,7 @@ char bit_value_to_char(enum bit_value v);
void bitvec_to_string_r(const struct bitvec *bv, char *str);
void bitvec_zero(struct bitvec *bv);
unsigned bitvec_rl(const struct bitvec *bv, bool b);
-unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits);
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, unsigned int max_bits);
void bitvec_shiftl(struct bitvec *bv, unsigned int n);
int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits);
unsigned int bitvec_add_array(struct bitvec *bv, const uint32_t *array,
diff --git a/include/osmocom/core/conv.h b/include/osmocom/core/conv.h
index 8b344f4d..84ef0f85 100644
--- a/include/osmocom/core/conv.h
+++ b/include/osmocom/core/conv.h
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*! \defgroup conv Convolutional encoding and decoding routines
@@ -128,6 +124,7 @@ int osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
const sbit_t *input, int n);
int osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
const sbit_t *input);
+int osmo_conv_decode_get_best_end_state(struct osmo_conv_decoder *decoder);
int osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
ubit_t *output, int has_flush, int end_state);
diff --git a/include/osmocom/core/counter.h b/include/osmocom/core/counter.h
index dc627918..7b677cb1 100644
--- a/include/osmocom/core/counter.h
+++ b/include/osmocom/core/counter.h
@@ -31,7 +31,7 @@ static inline void osmo_counter_inc(struct osmo_counter *ctr)
}
/*! Get current value of counter */
-OSMO_DEPRECATED("Implement as osmo_stat_item instead")
+OSMO_DEPRECATED_OUTSIDE("Implement as osmo_stat_item instead")
static inline unsigned long osmo_counter_get(struct osmo_counter *ctr)
{
return ctr->value;
@@ -52,7 +52,7 @@ void osmo_counter_free(struct osmo_counter *ctr)
int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *), void *data);
-int osmo_counters_count();
+int osmo_counters_count(void);
struct osmo_counter *osmo_counter_get_by_name(const char *name);
diff --git a/include/osmocom/core/crcXXgen.h.tpl b/include/osmocom/core/crcXXgen.h.tpl
index 823f21f2..13a3623e 100644
--- a/include/osmocom/core/crcXXgen.h.tpl
+++ b/include/osmocom/core/crcXXgen.h.tpl
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
diff --git a/include/osmocom/core/crcgen.h b/include/osmocom/core/crcgen.h
index 7cfe8699..24d29e74 100644
--- a/include/osmocom/core/crcgen.h
+++ b/include/osmocom/core/crcgen.h
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
diff --git a/include/osmocom/core/endian.h b/include/osmocom/core/endian.h
index 6107b12f..426ab680 100644
--- a/include/osmocom/core/endian.h
+++ b/include/osmocom/core/endian.h
@@ -1,7 +1,7 @@
/*! \file endian.h
*
* GNU and FreeBSD have various ways to express the
- * endianess but none of them is similiar enough. This
+ * endianness but none of them is similar enough. This
* will create two defines that allows to decide on the
* endian. The following will be defined to either 0 or
* 1 at the end of the file.
diff --git a/include/osmocom/core/exec.h b/include/osmocom/core/exec.h
index e63ec114..a4fdcfd3 100644
--- a/include/osmocom/core/exec.h
+++ b/include/osmocom/core/exec.h
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
extern const char *osmo_environment_whitelist[];
diff --git a/include/osmocom/core/fsm.h b/include/osmocom/core/fsm.h
index 77ebe7c1..a1ffd30d 100644
--- a/include/osmocom/core/fsm.h
+++ b/include/osmocom/core/fsm.h
@@ -200,7 +200,7 @@ void osmo_fsm_set_dealloc_ctx(void *ctx);
fmt, ## args)
#define OSMO_T_FMT "%c%u"
-#define OSMO_T_FMT_ARGS(T) ((T) >= 0 ? 'T' : 'X'), ((T) >= 0 ? T : -T)
+#define OSMO_T_FMT_ARGS(T) ((T) >= 0 ? 'T' : 'X'), ((T) >= 0 ? (T) : -(T))
int osmo_fsm_register(struct osmo_fsm *fsm);
void osmo_fsm_unregister(struct osmo_fsm *fsm);
@@ -224,9 +224,9 @@ int osmo_fsm_inst_update_id(struct osmo_fsm_inst *fi, const char *id);
int osmo_fsm_inst_update_id_f(struct osmo_fsm_inst *fi, const char *fmt, ...);
int osmo_fsm_inst_update_id_f_sanitize(struct osmo_fsm_inst *fi, char replace_with, const char *fmt, ...);
-const char *osmo_fsm_event_name(struct osmo_fsm *fsm, uint32_t event);
-const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi);
-const char *osmo_fsm_state_name(struct osmo_fsm *fsm, uint32_t state);
+const char *osmo_fsm_event_name(const struct osmo_fsm *fsm, uint32_t event);
+const char *osmo_fsm_inst_name(const struct osmo_fsm_inst *fi);
+const char *osmo_fsm_state_name(const struct osmo_fsm *fsm, uint32_t state);
/*! return the name of the state the FSM instance is currently in. */
static inline const char *osmo_fsm_inst_state_name(struct osmo_fsm_inst *fi)
diff --git a/include/osmocom/core/gsmtap.h b/include/osmocom/core/gsmtap.h
index 11b2d137..ebb52960 100644
--- a/include/osmocom/core/gsmtap.h
+++ b/include/osmocom/core/gsmtap.h
@@ -49,6 +49,7 @@
#define GSMTAP_TYPE_QC_DIAG 0x11 /* Qualcomm DIAG frame */
#define GSMTAP_TYPE_LTE_NAS 0x12 /* LTE Non-Access Stratum */
#define GSMTAP_TYPE_E1T1 0x13 /* E1/T1 Lines */
+#define GSMTAP_TYPE_GSM_RLP 0x14 /* GSM RLP frames as per 3GPP TS 24.022 */
/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */
@@ -179,6 +180,12 @@
#define GSMTAP_E1T1_RAW 0x03 /* raw/transparent B-channel */
#define GSMTAP_E1T1_TRAU16 0x04 /* 16k TRAU frames; sub-slot 0-3 */
#define GSMTAP_E1T1_TRAU8 0x05 /* 8k TRAU frames; sub-slot 0-7 */
+#define GSMTAP_E1T1_V5EF 0x06 /* V5 Envelope Function */
+#define GSMTAP_E1T1_X75 0x07 /* X.75 B-channel data */
+#define GSMTAP_E1T1_V120 0x08 /* V.120 B-channel data */
+#define GSMTAP_E1T1_V110 0x09 /* V.110 B-channel data */
+#define GSMTAP_E1T1_H221 0x0a /* H.221 B-channel data */
+#define GSMTAP_E1T1_PPP 0x0b /* PPP */
/* flags for the ARFCN */
#define GSMTAP_ARFCN_F_PCS 0x8000
diff --git a/include/osmocom/core/gsmtap_util.h b/include/osmocom/core/gsmtap_util.h
index e5643260..d24ee95f 100644
--- a/include/osmocom/core/gsmtap_util.h
+++ b/include/osmocom/core/gsmtap_util.h
@@ -24,30 +24,29 @@ struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type,
int8_t snr, const uint8_t *data, unsigned int len);
/*! one gsmtap instance */
-struct gsmtap_inst {
- int ofd_wq_mode; /*!< wait queue mode? */
- struct osmo_wqueue wq; /*!< the wait queue */
- struct osmo_fd sink_ofd;/*!< file descriptor */
-};
-
-/*! obtain the file descriptor associated with a gsmtap instance
- * \param[in] gti GSMTAP instance
- * \returns file descriptor of GSMTAP instance */
-static inline int gsmtap_inst_fd(struct gsmtap_inst *gti)
-{
- return gti->wq.bfd.fd;
-}
+struct gsmtap_inst;
+
+int gsmtap_inst_fd(struct gsmtap_inst *gti)
+ OSMO_DEPRECATED("Use gsmtap_inst_fd2() instead");
+
+int gsmtap_inst_fd2(const struct gsmtap_inst *gti);
int gsmtap_source_init_fd(const char *host, uint16_t port);
+int gsmtap_source_init_fd2(const char *local_host, uint16_t local_port, const char *rem_host, uint16_t rem_port);
int gsmtap_source_add_sink_fd(int gsmtap_fd);
struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port,
int ofd_wq_mode);
+struct gsmtap_inst *gsmtap_source_init2(const char *local_host, uint16_t local_port,
+ const char *rem_host, uint16_t rem_port, int ofd_wq_mode);
+
+void gsmtap_source_free(struct gsmtap_inst *gti);
int gsmtap_source_add_sink(struct gsmtap_inst *gti);
int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg);
+int gsmtap_sendmsg_free(struct gsmtap_inst *gti, struct msgb *msg);
int gsmtap_send_ex(struct gsmtap_inst *gti, uint8_t type, uint16_t arfcn, uint8_t ts,
uint8_t chan_type, uint8_t ss, uint32_t fn,
diff --git a/include/osmocom/core/isdnhdlc.h b/include/osmocom/core/isdnhdlc.h
index 56369bfd..c8cfdc3a 100644
--- a/include/osmocom/core/isdnhdlc.h
+++ b/include/osmocom/core/isdnhdlc.h
@@ -20,14 +20,9 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#ifndef __ISDNHDLC_H__
-#define __ISDNHDLC_H__
+#pragma once
#include <stdint.h>
@@ -80,5 +75,3 @@ extern void osmo_isdnhdlc_out_init(struct osmo_isdnhdlc_vars *hdlc, uint32_t fea
extern int osmo_isdnhdlc_encode(struct osmo_isdnhdlc_vars *hdlc, const uint8_t *src,
uint16_t slen, int *count, uint8_t *dst, int dsize);
-
-#endif /* __ISDNHDLC_H__ */
diff --git a/include/osmocom/core/linuxlist.h b/include/osmocom/core/linuxlist.h
index f28f88b9..2fc3fa75 100644
--- a/include/osmocom/core/linuxlist.h
+++ b/include/osmocom/core/linuxlist.h
@@ -238,6 +238,12 @@ static inline void llist_splice_init(struct llist_head *llist,
#define llist_last_entry(ptr, type, member) \
llist_entry((ptr)->prev, type, member)
+/*! Return the last element of the list.
+ * \param head the llist head of the list.
+ * \returns last element of the list, head if the list is empty.
+ */
+#define llist_last(head) (head)->prev
+
/*! Get the first element from a list, or NULL.
* \param ptr the list head to take the element from.
* \param type the type of the struct this is embedded in.
@@ -322,8 +328,7 @@ static inline void llist_splice_init(struct llist_head *llist,
pos = llist_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next))
-/*! Iterate over llist of given type, safe against removal of
- * non-consecutive(!) llist entries.
+/*! Iterate over llist of given type, safe against removal of llist entry.
* \param pos the 'type *' to use as a loop counter.
* \param n another 'type *' to use as temporary storage.
* \param head the head of the list over which to iterate.
diff --git a/include/osmocom/core/linuxrbtree.h b/include/osmocom/core/linuxrbtree.h
index d3f9fd12..e15317ec 100644
--- a/include/osmocom/core/linuxrbtree.h
+++ b/include/osmocom/core/linuxrbtree.h
@@ -12,11 +12,6 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301, USA.
-
linux/include/linux/rbtree.h
To use rbtrees you'll have to implement your own insert and search cores.
diff --git a/include/osmocom/core/logging.h b/include/osmocom/core/logging.h
index 343f9761..82e686f2 100644
--- a/include/osmocom/core/logging.h
+++ b/include/osmocom/core/logging.h
@@ -11,10 +11,16 @@
#include <osmocom/core/defs.h>
#include <osmocom/core/linuxlist.h>
+extern struct log_info *osmo_log_info;
+
#ifndef DEBUG
#define DEBUG
#endif
+#ifdef LIBOSMOCORE_NO_LOGGING
+#undef DEBUG
+#endif
+
#ifdef DEBUG
/*! Log a debug message through the Osmocom logging framework
* \param[in] ss logging subsystem (e.g. \ref DLGLOBAL)
@@ -49,11 +55,19 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
* \param[in] fmt format string
* \param[in] args variable argument list
*/
+#ifndef LIBOSMOCORE_NO_LOGGING
#define LOGPC(ss, level, fmt, args...) \
do { \
+ if (!osmo_log_info) { \
+ logp_stub(__FILE__, __LINE__, 1, fmt, ##args); \
+ break; \
+ } \
if (log_check_level(ss, level)) \
logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args); \
} while(0)
+#else
+#define LOGPC(ss, level, fmt, args...)
+#endif
/*! Log through the Osmocom logging framework with explicit source.
* If caller_file is passed as NULL, __FILE__ and __LINE__ are used
@@ -83,8 +97,16 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
* \param[in] fmt format string
* \param[in] args variable argument list
*/
+#ifndef LIBOSMOCORE_NO_LOGGING
#define LOGPSRCC(ss, level, caller_file, caller_line, cont, fmt, args...) \
do { \
+ if (!osmo_log_info) { \
+ if (caller_file) \
+ logp_stub(caller_file, caller_line, cont, fmt, ##args); \
+ else \
+ logp_stub(__FILE__, __LINE__, cont, fmt, ##args); \
+ break; \
+ } \
if (log_check_level(ss, level)) {\
if (caller_file) \
logp2(ss, level, caller_file, caller_line, cont, fmt, ##args); \
@@ -92,6 +114,9 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
logp2(ss, level, __FILE__, __LINE__, cont, fmt, ##args); \
}\
} while(0)
+#else
+#define LOGPSRCC(ss, level, caller_file, caller_line, cont, fmt, args...)
+#endif
/*! different log levels */
#define LOGL_DEBUG 1 /*!< debugging information */
@@ -124,7 +149,13 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
#define DLBSSGP -21 /*!< Osmocom BSSGP layer */
#define DLNSDATA -22 /*!< Osmocom NS layer data pdus */
#define DLNSSIGNAL -23 /*!< Osmocom NS layer signal pdus */
-#define OSMO_NUM_DLIB 23 /*!< Number of logging sub-systems in libraries */
+#define DLIUUP -24 /*!< Osmocom IuUP layer */
+#define DLPFCP -25 /*!< Osmocom Packet Forwarding Control Protocol */
+#define DLCSN1 -26 /*!< CSN.1 (Concrete Syntax Notation 1) codec */
+#define DLM2PA -27 /*!< Osmocom M2PA (libosmo-sigtran) */
+#define DLM2UA -28 /*!< Reserved for future Osmocom M2UA (libosmo-sigtran) */
+#define DLIO -29 /*!< Osmocom IO sub-system */
+#define OSMO_NUM_DLIB 29 /*!< Number of logging sub-systems in libraries */
/* Colors that can be used in log_info_cat.color */
#define OSMO_LOGCOLOR_NORMAL NULL
@@ -267,7 +298,7 @@ enum log_filename_pos {
/*! structure representing a logging target */
struct log_target {
- struct llist_head entry; /*!< linked list */
+ struct llist_head entry; /*!< linked list */
/*! Internal data for filtering */
int filter_map;
@@ -297,8 +328,11 @@ struct log_target {
union {
struct {
+ /* direct, blocking output via stdio */
FILE *out;
const char *fname;
+ /* indirect output via write_queue and osmo_select_main() */
+ struct osmo_wqueue *wqueue;
} tgt_file;
struct {
@@ -364,6 +398,7 @@ struct log_target {
void logp2(int subsys, unsigned int level, const char *file,
int line, int cont, const char *format, ...)
__attribute__ ((format (printf, 6, 7)));
+void logp_stub(const char *file, int line, int cont, const char *format, ...);
int log_init(const struct log_info *inf, void *talloc_ctx);
void log_fini(void);
int log_check_level(int subsys, unsigned int level);
@@ -374,7 +409,8 @@ int log_set_context(uint8_t ctx, void *value);
/* filter on the targets */
void log_set_all_filter(struct log_target *target, int);
-
+int log_cache_enable(void);
+void log_cache_update(int mapped_subsys, uint8_t enabled, uint8_t level);
void log_set_use_color(struct log_target *target, int);
void log_set_print_extended_timestamp(struct log_target *target, int);
void log_set_print_timestamp(struct log_target *target, int);
@@ -408,12 +444,14 @@ struct log_target *log_target_create_gsmtap(const char *host, uint16_t port,
struct log_target *log_target_create_systemd(bool raw);
void log_target_systemd_set_raw(struct log_target *target, bool raw);
int log_target_file_reopen(struct log_target *tgt);
+int log_target_file_switch_to_stream(struct log_target *tgt);
+int log_target_file_switch_to_wqueue(struct log_target *tgt);
int log_targets_reopen(void);
void log_add_target(struct log_target *target);
void log_del_target(struct log_target *target);
-struct log_target *log_target_find(int type, const char *fname);
+struct log_target *log_target_find(enum log_target_type type, const char *fname);
void log_enable_multithread(void);
diff --git a/include/osmocom/core/loggingrb.h b/include/osmocom/core/loggingrb.h
index a9fb4047..6d501466 100644
--- a/include/osmocom/core/loggingrb.h
+++ b/include/osmocom/core/loggingrb.h
@@ -11,10 +11,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
diff --git a/include/osmocom/core/msgb.h b/include/osmocom/core/msgb.h
index cc76e3ad..b05d8b6b 100644
--- a/include/osmocom/core/msgb.h
+++ b/include/osmocom/core/msgb.h
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
@@ -74,6 +70,8 @@ extern int msgb_resize_area(struct msgb *msg, uint8_t *area,
int old_size, int new_size);
extern struct msgb *msgb_copy(const struct msgb *msg, const char *name);
extern struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *name);
+extern struct msgb *msgb_copy_resize(const struct msgb *msg, uint16_t new_len, const char *name);
+extern struct msgb *msgb_copy_resize_c(const void *ctx, const struct msgb *msg, uint16_t new_len, const char *name);
static int msgb_test_invariant(const struct msgb *msg) __attribute__((pure));
/*! Free all msgbs from a queue built with msgb_enqueue().
@@ -129,13 +127,13 @@ static inline struct msgb *msgb_dequeue_count(struct llist_head *queue,
#endif
/*! obtain L1 header of msgb */
-#define msgb_l1(m) ((void *)(m->l1h))
+#define msgb_l1(m) ((void *)((m)->l1h))
/*! obtain L2 header of msgb */
-#define msgb_l2(m) ((void *)(m->l2h))
+#define msgb_l2(m) ((void *)((m)->l2h))
/*! obtain L3 header of msgb */
-#define msgb_l3(m) ((void *)(m->l3h))
+#define msgb_l3(m) ((void *)((m)->l3h))
/*! obtain L4 header of msgb */
-#define msgb_l4(m) ((void *)(m->l4h))
+#define msgb_l4(m) ((void *)((m)->l4h))
/*! obtain SMS header of msgb */
#define msgb_sms(m) msgb_l4(m)
@@ -148,6 +146,7 @@ static inline struct msgb *msgb_dequeue_count(struct llist_head *queue,
*/
static inline unsigned int msgb_l1len(const struct msgb *msgb)
{
+ OSMO_ASSERT(msgb->l1h);
return msgb->tail - (uint8_t *)msgb_l1(msgb);
}
@@ -160,6 +159,7 @@ static inline unsigned int msgb_l1len(const struct msgb *msgb)
*/
static inline unsigned int msgb_l2len(const struct msgb *msgb)
{
+ OSMO_ASSERT(msgb->l2h);
return msgb->tail - (uint8_t *)msgb_l2(msgb);
}
@@ -172,6 +172,7 @@ static inline unsigned int msgb_l2len(const struct msgb *msgb)
*/
static inline unsigned int msgb_l3len(const struct msgb *msgb)
{
+ OSMO_ASSERT(msgb->l3h);
return msgb->tail - (uint8_t *)msgb_l3(msgb);
}
@@ -184,7 +185,8 @@ static inline unsigned int msgb_l3len(const struct msgb *msgb)
*/
static inline unsigned int msgb_l4len(const struct msgb *msgb)
{
- return msgb->tail - (uint8_t *)msgb_sms(msgb);
+ OSMO_ASSERT(msgb->l4h);
+ return msgb->tail - (uint8_t *)msgb_l4(msgb);
}
/*! determine the length of the header
@@ -238,7 +240,7 @@ static inline int msgb_headroom(const struct msgb *msgb)
static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
{
unsigned char *tmp = msgb->tail;
- if (msgb_tailroom(msgb) < (int) len)
+ if (OSMO_UNLIKELY(msgb_tailroom(msgb) < (int) len))
MSGB_ABORT(msgb, "Not enough tailroom msgb_put"
" (allocated %u, head at %u, len %u, tailroom %u < want tailroom %u)\n",
msgb->data_len - sizeof(struct msgb),
@@ -286,7 +288,7 @@ static inline void msgb_put_u32(struct msgb *msgb, uint32_t word)
*/
static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len)
{
- if (msgb_length(msgb) < len)
+ if (OSMO_UNLIKELY(msgb_length(msgb) < len))
MSGB_ABORT(msgb, "msgb too small to get %u (len %u)\n",
len, msgb_length(msgb));
msgb->tail -= len;
@@ -338,7 +340,7 @@ static inline uint32_t msgb_get_u32(struct msgb *msgb)
*/
static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len)
{
- if (msgb_headroom(msgb) < (int) len)
+ if (OSMO_UNLIKELY(msgb_headroom(msgb) < (int) len))
MSGB_ABORT(msgb, "Not enough headroom msgb_push"
" (allocated %u, head at %u < want headroom %u, len %u, tailroom %u)\n",
msgb->data_len - sizeof(struct msgb),
@@ -401,7 +403,7 @@ static inline unsigned char *msgb_push_tl(struct msgb *msgb, uint8_t tag)
*/
static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len)
{
- if (msgb_length(msgb) < len)
+ if (OSMO_UNLIKELY(msgb_length(msgb) < len))
MSGB_ABORT(msgb, "msgb too small to pull %u (len %u)\n",
len, msgb_length(msgb));
msgb->len -= len;
@@ -440,7 +442,7 @@ static inline unsigned char *msgb_pull_to_l2(struct msgb *msg)
/*! remove uint8 from front of message
* \param[in] msgb message buffer
- * \returns 8bit value taken from end of msgb
+ * \returns 8bit value taken from the front of msgb
*/
static inline uint8_t msgb_pull_u8(struct msgb *msgb)
{
@@ -450,7 +452,7 @@ static inline uint8_t msgb_pull_u8(struct msgb *msgb)
/*! remove uint16 from front of message
* \param[in] msgb message buffer
- * \returns 16bit value taken from end of msgb
+ * \returns 16bit value taken from the front of msgb
*/
static inline uint16_t msgb_pull_u16(struct msgb *msgb)
{
@@ -460,7 +462,7 @@ static inline uint16_t msgb_pull_u16(struct msgb *msgb)
/*! remove uint32 from front of message
* \param[in] msgb message buffer
- * \returns 32bit value taken from end of msgb
+ * \returns 32bit value taken from the front of msgb
*/
static inline uint32_t msgb_pull_u32(struct msgb *msgb)
{
@@ -492,9 +494,9 @@ static inline void msgb_reserve(struct msgb *msg, int len)
*/
static inline int msgb_trim(struct msgb *msg, int len)
{
- if (len < 0)
+ if (OSMO_UNLIKELY(len < 0))
MSGB_ABORT(msg, "Negative length is not allowed\n");
- if (len > msg->data_len)
+ if (OSMO_UNLIKELY(len > msg->data_len))
return -1;
msg->len = len;
@@ -524,13 +526,13 @@ static inline int msgb_l3trim(struct msgb *msg, int l3len)
* followed by \ref msgb_reserve in order to create a new \ref msgb with
* user-specified amount of headroom.
*/
-static inline struct msgb *msgb_alloc_headroom_c(const void *ctx, int size, int headroom,
+static inline struct msgb *msgb_alloc_headroom_c(const void *ctx, uint16_t size, uint16_t headroom,
const char *name)
{
- osmo_static_assert(size >= headroom, headroom_bigger);
+ OSMO_ASSERT(size >= headroom);
struct msgb *msg = msgb_alloc_c(ctx, size, name);
- if (msg)
+ if (OSMO_LIKELY(msg))
msgb_reserve(msg, headroom);
return msg;
}
@@ -546,13 +548,13 @@ static inline struct msgb *msgb_alloc_headroom_c(const void *ctx, int size, int
* followed by \ref msgb_reserve in order to create a new \ref msgb with
* user-specified amount of headroom.
*/
-static inline struct msgb *msgb_alloc_headroom(int size, int headroom,
+static inline struct msgb *msgb_alloc_headroom(uint16_t size, uint16_t headroom,
const char *name)
{
- osmo_static_assert(size >= headroom, headroom_bigger);
+ OSMO_ASSERT(size >= headroom);
struct msgb *msg = msgb_alloc(size, name);
- if (msg)
+ if (OSMO_LIKELY(msg))
msgb_reserve(msg, headroom);
return msg;
}
diff --git a/include/osmocom/core/msgfile.h b/include/osmocom/core/msgfile.h
index 800b4311..cfa95905 100644
--- a/include/osmocom/core/msgfile.h
+++ b/include/osmocom/core/msgfile.h
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
diff --git a/include/osmocom/core/netdev.h b/include/osmocom/core/netdev.h
new file mode 100644
index 00000000..3238ec33
--- /dev/null
+++ b/include/osmocom/core/netdev.h
@@ -0,0 +1,49 @@
+/*! \file netdev.h
+ * network device (interface) convenience functions. */
+
+#pragma once
+#if (!EMBEDDED)
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+
+struct osmo_netdev;
+
+typedef int (*osmo_netdev_ifupdown_ind_cb_t)(struct osmo_netdev *netdev, bool ifupdown);
+typedef int (*osmo_netdev_dev_name_chg_cb_t)(struct osmo_netdev *netdev, const char *new_dev_name);
+typedef int (*osmo_netdev_mtu_chg_cb_t)(struct osmo_netdev *netdev, unsigned int new_mtu);
+
+struct osmo_netdev *osmo_netdev_alloc(void *ctx, const char *name);
+void osmo_netdev_free(struct osmo_netdev *netdev);
+
+int osmo_netdev_register(struct osmo_netdev *netdev);
+int osmo_netdev_unregister(struct osmo_netdev *netdev);
+bool osmo_netdev_is_registered(struct osmo_netdev *netdev);
+
+const char *osmo_netdev_get_name(const struct osmo_netdev *netdev);
+
+void osmo_netdev_set_priv_data(struct osmo_netdev *netdev, void *priv_data);
+void *osmo_netdev_get_priv_data(struct osmo_netdev *netdev);
+
+int osmo_netdev_set_ifindex(struct osmo_netdev *netdev, unsigned int ifindex);
+unsigned int osmo_netdev_get_ifindex(const struct osmo_netdev *netdev);
+
+const char *osmo_netdev_get_dev_name(const struct osmo_netdev *netdev);
+
+int osmo_netdev_set_netns_name(struct osmo_netdev *netdev, const char *netns);
+const char *osmo_netdev_get_netns_name(const struct osmo_netdev *netdev);
+
+void osmo_netdev_set_ifupdown_ind_cb(struct osmo_netdev *netdev, osmo_netdev_ifupdown_ind_cb_t ifupdown_ind_cb);
+void osmo_netdev_set_dev_name_chg_cb(struct osmo_netdev *netdev, osmo_netdev_dev_name_chg_cb_t dev_name_chg_cb);
+void osmo_netdev_set_mtu_chg_cb(struct osmo_netdev *netdev, osmo_netdev_mtu_chg_cb_t mtu_chg_cb);
+
+int osmo_netdev_add_addr(struct osmo_netdev *netdev, const struct osmo_sockaddr *addr, uint8_t prefixlen);
+int osmo_netdev_add_route(struct osmo_netdev *netdev, const struct osmo_sockaddr *dst_addr,
+ uint8_t dst_prefixlen, const struct osmo_sockaddr *gw_addr);
+int osmo_netdev_ifupdown(struct osmo_netdev *netdev, bool ifupdown);
+
+#endif /* (!EMBEDDED) */
+/*! @} */
diff --git a/include/osmocom/core/netns.h b/include/osmocom/core/netns.h
new file mode 100644
index 00000000..5bbf2242
--- /dev/null
+++ b/include/osmocom/core/netns.h
@@ -0,0 +1,24 @@
+/*! \file netns.h
+ * Network namespace convenience functions. */
+
+#pragma once
+#if (!EMBEDDED)
+
+#if defined(__linux__)
+
+#include <signal.h>
+
+struct osmo_netns_switch_state {
+ sigset_t prev_sigmask;
+ int prev_nsfd;
+};
+
+int osmo_netns_open_fd(const char *name);
+int osmo_netns_switch_enter(int nsfd, struct osmo_netns_switch_state *state);
+int osmo_netns_switch_exit(struct osmo_netns_switch_state *state);
+
+
+#endif /* defined(__linux__) */
+
+#endif /* (!EMBEDDED) */
+/*! @} */
diff --git a/include/osmocom/core/osmo_io.h b/include/osmocom/core/osmo_io.h
new file mode 100644
index 00000000..8c931ddd
--- /dev/null
+++ b/include/osmocom/core/osmo_io.h
@@ -0,0 +1,207 @@
+/*! \file osmo_io.h
+ * io(_uring) abstraction osmo fd compatibility
+ */
+
+#pragma once
+
+#include <sys/socket.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/utils.h>
+
+/*! \defgroup osmo_io Osmocom I/O interface
+ * @{
+ *
+ * osmo_io is the new (2023) interface for performing asynchronous I/O.
+ * osmo_io encapsulates asynchronous, non-blocking I/O to sockets or other file descriptors
+ * with a submission/completion model.
+ *
+ * For writes, the API user submits write requests, and receives
+ * completion call-backs once the write completes.
+ *
+ * For reads, the API user specifies the size (and headroom) for message buffers, and osmo_io
+ * internally allocates msgb's accordingly. Whenever data arrives at the socket/file descriptor,
+ * osmo_io reads the data into such a msgb and hands it to a read-completion call-back provided
+ * by the API user.
+ *
+ * A given socket/file descriptor is represented by struct osmo_io_fd. osmo_io_fd are named,
+ * i.e. the API user can provide a meaningful name describing the purpose (such as protocol/interface or the
+ * name of the remote peer). This allows osmo_io to log any related [error] messages using this name as
+ * context.
+ *
+ * When implementing some SOCK_STREAM / SOCK_SEQPACKET based client/server transports (such as those on top
+ * of TCP or SCTP), you are most likely better off using the osmo_stream_cli / osmo_stream_srv abstractions
+ * provided by libosmo-netif. They in turn can be used in an osmo_io mode, see the respective documentation.
+ *
+ * If you use osmo_io_fd directly, the life-cycle usually will look as follows:
+ *
+ * 1. open some socket and bind and/or connect it
+ * 2. Allocate an osmo_io_fd using osmo_iofd_setup(), configuring the mode and specifying the call-backs
+ * 3. Registering it with osmo_iofd_register(), which enables reading
+ * 4. Handle inbound data via {read,recvfrom,recvmsg} call-backs; write to it using
+ * osmo_iofd_{write,sendto_sendmsg}_msg()
+ * 5. Eventually un-register it using osmo_iofd_unregister(). Afterwards, you can re-cycle the iofd by
+ * calling osmo_iofd_register() with a new file-descriptor, or free it using osmo_iofd_free().
+ *
+ * \file osmo_io.h */
+
+/*! log macro used for logging information related to the osmo_io_fd.
+ * \param[in] iofd osmo_io_fd about which we're logging
+ * \param[in] level log-level (LOGL_DEBUG, LOGL_INFO, LOGL_NOTICE, LOGL_ERROR, LOGL_FATAL)
+ * \param[in] fmt printf-style format string
+ * \param[in] args arguments to the format string
+ */
+#define LOGPIO(iofd, level, fmt, args...) \
+ LOGP(DLIO, level, "iofd(%s)" fmt, iofd->name, ## args)
+
+struct osmo_io_fd;
+
+/*! The _mode_ of an osmo_io_fd determines if read/write, recvfrom/sendmsg or recvmsg/sendmsg semantics are
+ * used. */
+enum osmo_io_fd_mode {
+ /*! use read() / write() semantics with read_cb/write_cb in osmo_io_ops */
+ OSMO_IO_FD_MODE_READ_WRITE,
+ /*! use recvfrom() / sendto() semantics with recvfrom_cb/sendto_cb in osmo_io_ops */
+ OSMO_IO_FD_MODE_RECVFROM_SENDTO,
+ /*! emulate recvmsg() / sendmsg() semantics with recvmsg_cb/sendto_cb in osmo_io_ops */
+ OSMO_IO_FD_MODE_RECVMSG_SENDMSG,
+};
+
+/*! The back-end used by osmo_io. There can be multiple different back-ends available on a given system;
+ * only one of it is used for all I/O performed via osmo_io in one given process. */
+enum osmo_io_backend {
+ /*! classic back-end using poll(2) and direct read/write/recvfrom/sendto/recvmsg/sendmsg syscalls */
+ OSMO_IO_BACKEND_POLL,
+ /*! back-end using io_uring to perform efficient I/O and reduce syscall overhead */
+ OSMO_IO_BACKEND_IO_URING,
+};
+
+extern const struct value_string osmo_io_backend_names[];
+/*! return the string name of an osmo_io_backend */
+static inline const char *osmo_io_backend_name(enum osmo_io_backend val)
+{ return get_value_string(osmo_io_backend_names, val); }
+
+extern const struct value_string osmo_iofd_mode_names[];
+/*! return the string name of an osmo_io_mode */
+static inline const char *osmo_iofd_mode_name(enum osmo_io_fd_mode val)
+{ return get_value_string(osmo_iofd_mode_names, val); }
+
+/*! I/O operations (call-back functions) related to an osmo_io_fd */
+struct osmo_io_ops {
+ /* mode OSMO_IO_FD_MODE_READ_WRITE: */
+ struct {
+ /*! completion call-back function when something was read from fd. Only valid in
+ * OSMO_IO_FD_MODE_READ_WRITE.
+ * \param[in] iofd osmo_io_fd for which read() has completed.
+ * \param[in] res return value of the read() call, or -errno in case of error.
+ * \param[in] msg message buffer containing the read data. Ownership is transferred to the
+ * call-back, and it must make sure to msgb_free() it eventually! */
+ void (*read_cb)(struct osmo_io_fd *iofd, int res, struct msgb *msg);
+ /*! completion call-back function when write issued via osmo_iofd_write_msgb() has completed
+ * on fd. Only valid in OSMO_IO_FD_MODE_READ_WRITE.
+ * \param[in] iofd on which a write() has completed.
+ * \param[in] res return value of the write() call, or -errno in case of error.
+ * \param[in] msg message buffer whose write has completed. Ownership is *not* transferred to the
+ * call-back; it is automatically freed after the call-back terminates! */
+ void (*write_cb)(struct osmo_io_fd *iofd, int res,
+ struct msgb *msg);
+ /*! optional call-back function to segment the data at message boundaries. This is useful when
+ * message boundaries are to be preserved over a SOCK_STREAM transport socket like TCP. Can
+ * be NULL for any application not requiring de-segmentation of received data.
+ *
+ * The call-back needs to return the size of the next message. If it returns
+ * -EAGAIN or a value larger than msgb_length() (message is incomplete)
+ * osmo_io will wait for more data to be read. Other negative values
+ * cause the msg to be discarded.
+ * If a full message was received (segmentation_cb() returns a value <= msgb_length())
+ * the msgb will be trimmed to size by osmo_io and forwarded to the read call-back. Any
+ * parsing done to the msgb by segmentation_cb() will be preserved for the read_cb()
+ * (e.g. setting lxh or msgb->cb). */
+ int (*segmentation_cb)(struct msgb *msg);
+ };
+
+ /* mode OSMO_IO_FD_MODE_RECVFROM_SENDTO: */
+ struct {
+ /*! completion call-back function when recvfrom(2) has completed.
+ * Only valid in OSMO_IO_FD_MODE_RECVFROM_SENDTO.
+ * \param[in] iofd osmo_io_fd for which recvfrom() has completed.
+ * \param[in] res return value of the recvfrom() call, or -errno in case of error.
+ * \param[in] msg message buffer containing the read data. Ownership is transferred to the
+ * call-back, and it must make sure to msgb_free() it eventually!
+ * \param[in] saddr socket-address of sender from which data was received. */
+ void (*recvfrom_cb)(struct osmo_io_fd *iofd, int res,
+ struct msgb *msg,
+ const struct osmo_sockaddr *saddr);
+ /*! completion call-back function when sendto() issued via osmo_iofd_sendto_msgb() has
+ * completed on fd. Only valid in OSMO_IO_FD_MODE_RECVFROM_SENDTO.
+ * \param[in] iofd on which a sendto() has completed.
+ * \param[in] res return value of the sendto() call, or -errno in case of error.
+ * \param[in] msg message buffer whose write has completed. Ownership is *not* transferred to the
+ * call-back; it is automatically freed after the call-back terminates!
+ * \param[in] daddr socket-address of destination to which data was sent. */
+ void (*sendto_cb)(struct osmo_io_fd *iofd, int res,
+ struct msgb *msg,
+ const struct osmo_sockaddr *daddr);
+ };
+
+ /* mode OSMO_IO_FD_MODE_RECVMSG_SENDMSG: */
+ struct {
+ /*! completion call-back function when recvmsg(2) has completed.
+ * Only valid in OSMO_IO_FD_MODE_RECVMSG_SENDMSG.
+ * \param[in] iofd osmo_io_fd for which recvmsg() has completed.
+ * \param[in] res return value of the recvmsg() call, or -errno in case of error.
+ * \param[in] msg message buffer containing the read data. Ownership is transferred to the
+ * call-back, and it must make sure to msgb_free() it eventually!
+ * \param[in] msgh msghdr containing metadata related to the recvmsg call. Only valid until
+ * call-back ends. */
+ void (*recvmsg_cb)(struct osmo_io_fd *iofd, int res,
+ struct msgb *msg, const struct msghdr *msgh);
+ /*! completion call-back function when sendmsg() issued via osmo_iofd_sendmsg_msgb() has
+ * completed on fd. Only valid in Only valid in OSMO_IO_FD_MODE_RECVMSG_SENDMSG.
+ * \param[in] iofd on which a sendmsg() has completed.
+ * \param[in] res return value of the sendmsg() call, or -errno in case of error.
+ * \param[in] msg message buffer whose write has completed. Ownership is *not* transferred to the
+ * call-back; it is automatically freed after the call-back terminates! */
+ void (*sendmsg_cb)(struct osmo_io_fd *iofd, int res, struct msgb *msg);
+ };
+};
+
+void osmo_iofd_init(void);
+
+struct osmo_io_fd *osmo_iofd_setup(const void *ctx, int fd, const char *name,
+ enum osmo_io_fd_mode mode, const struct osmo_io_ops *ioops, void *data);
+int osmo_iofd_set_cmsg_size(struct osmo_io_fd *iofd, size_t cmsg_size);
+int osmo_iofd_register(struct osmo_io_fd *iofd, int fd);
+int osmo_iofd_unregister(struct osmo_io_fd *iofd);
+unsigned int osmo_iofd_txqueue_len(struct osmo_io_fd *iofd);
+void osmo_iofd_txqueue_clear(struct osmo_io_fd *iofd);
+int osmo_iofd_close(struct osmo_io_fd *iofd);
+void osmo_iofd_free(struct osmo_io_fd *iofd);
+
+void osmo_iofd_notify_connected(struct osmo_io_fd *iofd);
+
+int osmo_iofd_write_msgb(struct osmo_io_fd *iofd, struct msgb *msg);
+int osmo_iofd_sendto_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendto_flags,
+ const struct osmo_sockaddr *dest);
+int osmo_iofd_sendmsg_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendmsg_flags,
+ const struct msghdr *msgh);
+
+void osmo_iofd_set_alloc_info(struct osmo_io_fd *iofd, unsigned int size, unsigned int headroom);
+void osmo_iofd_set_txqueue_max_length(struct osmo_io_fd *iofd, unsigned int size);
+void *osmo_iofd_get_data(const struct osmo_io_fd *iofd);
+void osmo_iofd_set_data(struct osmo_io_fd *iofd, void *data);
+
+unsigned int osmo_iofd_get_priv_nr(const struct osmo_io_fd *iofd);
+void osmo_iofd_set_priv_nr(struct osmo_io_fd *iofd, unsigned int priv_nr);
+
+int osmo_iofd_get_fd(const struct osmo_io_fd *iofd);
+const char *osmo_iofd_get_name(const struct osmo_io_fd *iofd);
+void osmo_iofd_set_name(struct osmo_io_fd *iofd, const char *name);
+
+int osmo_iofd_set_ioops(struct osmo_io_fd *iofd, const struct osmo_io_ops *ioops);
+void osmo_iofd_get_ioops(struct osmo_io_fd *iofd, struct osmo_io_ops *ioops);
+
+/*! @} */
diff --git a/include/osmocom/core/prim.h b/include/osmocom/core/prim.h
index 99eabff0..8e6b436d 100644
--- a/include/osmocom/core/prim.h
+++ b/include/osmocom/core/prim.h
@@ -30,7 +30,11 @@ enum osmo_prim_operation {
PRIM_OP_CONFIRM, /*!< confirm */
};
-extern const struct value_string osmo_prim_op_names[5];
+extern const struct value_string osmo_prim_op_names[];
+static inline const char *osmo_prim_operation_name(enum osmo_prim_operation val)
+{
+ return get_value_string(osmo_prim_op_names, val);
+}
/*!< The upper 8 byte of the technology, the lower 24 bits for the SAP */
#define _SAP_GSM_SHIFT 24
diff --git a/include/osmocom/core/rate_ctr.h b/include/osmocom/core/rate_ctr.h
index 1669ce49..0a3eb2ce 100644
--- a/include/osmocom/core/rate_ctr.h
+++ b/include/osmocom/core/rate_ctr.h
@@ -61,7 +61,9 @@ struct rate_ctr_group {
const struct rate_ctr_group_desc *desc;
/*! The index of this ctr_group within its class */
unsigned int idx;
- /*! Actual counter structures below */
+ /*! Optional string-based identifier to be used instead of index at report time */
+ char *name;
+ /*! Actual counter structures below. Don't access it directly, use APIs below! */
struct rate_ctr ctr[0];
};
@@ -73,6 +75,9 @@ static inline void rate_ctr_group_upd_idx(struct rate_ctr_group *grp, unsigned i
{
grp->idx = idx;
}
+void rate_ctr_group_set_name(struct rate_ctr_group *grp, const char *name);
+
+struct rate_ctr *rate_ctr_group_get_ctr(struct rate_ctr_group *grp, unsigned int idx);
void rate_ctr_group_free(struct rate_ctr_group *grp);
@@ -81,6 +86,15 @@ void rate_ctr_group_free(struct rate_ctr_group *grp);
* \param inc quantity to increment \a ctr by */
void rate_ctr_add(struct rate_ctr *ctr, int inc);
+/*! Increment the counter by \a inc
+ * \param ctrg \ref rate_ctr_group of counter
+ * \param idx index into \a ctrg counter group
+ * \param inc quantity to increment \a ctr by */
+static inline void rate_ctr_add2(struct rate_ctr_group *ctrg, unsigned int idx, int inc)
+{
+ rate_ctr_add(rate_ctr_group_get_ctr(ctrg, idx), inc);
+}
+
/*! Increment the counter by 1
* \param ctr \ref rate_ctr to increment */
static inline void rate_ctr_inc(struct rate_ctr *ctr)
@@ -93,7 +107,7 @@ static inline void rate_ctr_inc(struct rate_ctr *ctr)
* \param idx index into \a ctrg counter group */
static inline void rate_ctr_inc2(struct rate_ctr_group *ctrg, unsigned int idx)
{
- rate_ctr_inc(&ctrg->ctr[idx]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctrg, idx));
}
diff --git a/include/osmocom/core/select.h b/include/osmocom/core/select.h
index b4101998..fc148512 100644
--- a/include/osmocom/core/select.h
+++ b/include/osmocom/core/select.h
@@ -105,5 +105,8 @@ struct osmo_signalfd {
struct osmo_signalfd *
osmo_signalfd_setup(void *ctx, sigset_t set, osmo_signalfd_cb *cb, void *data);
+void osmo_select_shutdown_request(void);
+int osmo_select_shutdown_requested(void);
+bool osmo_select_shutdown_done(void);
/*! @} */
diff --git a/include/osmocom/core/sercomm.h b/include/osmocom/core/sercomm.h
index 072f4d9c..38e6271c 100644
--- a/include/osmocom/core/sercomm.h
+++ b/include/osmocom/core/sercomm.h
@@ -2,8 +2,7 @@
* Osmocom Sercomm HDLC (de)multiplex.
*/
-#ifndef _SERCOMM_H
-#define _SERCOMM_H
+#pragma once
#include <osmocom/core/msgb.h>
@@ -110,5 +109,3 @@ static inline struct msgb *osmo_sercomm_alloc_msgb(unsigned int len)
}
/*! @} */
-
-#endif /* _SERCOMM_H */
diff --git a/include/osmocom/core/serial.h b/include/osmocom/core/serial.h
index 443275f2..0ac29681 100644
--- a/include/osmocom/core/serial.h
+++ b/include/osmocom/core/serial.h
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*! \defgroup serial Utility functions to deal with serial ports
diff --git a/include/osmocom/core/sockaddr_str.h b/include/osmocom/core/sockaddr_str.h
index d96b7434..8ec514c7 100644
--- a/include/osmocom/core/sockaddr_str.h
+++ b/include/osmocom/core/sockaddr_str.h
@@ -20,10 +20,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -38,6 +34,7 @@ struct in6_addr;
struct sockaddr_storage;
struct sockaddr_in;
struct sockaddr_in6;
+struct osmo_sockaddr;
/*! \defgroup sockaddr_str IP address/port utilities.
* @{
@@ -63,11 +60,16 @@ struct osmo_sockaddr_str {
* printf("got " OSMO_SOCKADDR_STR_FMT "\n", OSMO_SOCKADDR_STR_FMT_ARGS(my_sockaddr_str));
*/
#define OSMO_SOCKADDR_STR_FMT "%s%s%s:%u"
+#define OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL(R) \
+ ((R)->af == AF_INET6) ? "[" : "", \
+ (R)->ip, \
+ ((R)->af == AF_INET6) ? "]" : "", \
+ (R)->port
#define OSMO_SOCKADDR_STR_FMT_ARGS(R) \
- ((R) && (R)->af == AF_INET6)? "[" : "", \
- (R)? (R)->ip : "NULL", \
- ((R) && (R)->af == AF_INET6)? "]" : "", \
- (R)? (R)->port : 0
+ ((R) && (R)->af == AF_INET6) ? "[" : "", \
+ (R) ? (R)->ip : "NULL", \
+ ((R) && (R)->af == AF_INET6) ? "]" : "", \
+ (R) ? (R)->port : 0
bool osmo_sockaddr_str_is_set(const struct osmo_sockaddr_str *sockaddr_str);
bool osmo_sockaddr_str_is_nonzero(const struct osmo_sockaddr_str *sockaddr_str);
@@ -83,6 +85,7 @@ int osmo_sockaddr_str_from_32h(struct osmo_sockaddr_str *sockaddr_str, uint32_t
int osmo_sockaddr_str_from_sockaddr_in(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_in *src);
int osmo_sockaddr_str_from_sockaddr_in6(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_in6 *src);
int osmo_sockaddr_str_from_sockaddr(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_storage *src);
+int osmo_sockaddr_str_from_osa(struct osmo_sockaddr_str *sockaddr_str, const struct osmo_sockaddr *src);
int osmo_sockaddr_str_to_in_addr(const struct osmo_sockaddr_str *sockaddr_str, struct in_addr *dst);
int osmo_sockaddr_str_to_in6_addr(const struct osmo_sockaddr_str *sockaddr_str, struct in6_addr *dst);
@@ -91,6 +94,7 @@ int osmo_sockaddr_str_to_32h(const struct osmo_sockaddr_str *sockaddr_str, uint3
int osmo_sockaddr_str_to_sockaddr_in(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_in *dst);
int osmo_sockaddr_str_to_sockaddr_in6(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_in6 *dst);
int osmo_sockaddr_str_to_sockaddr(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_storage *dst);
+int osmo_sockaddr_str_to_osa(const struct osmo_sockaddr_str *sockaddr_str, struct osmo_sockaddr *dst);
int osmo_sockaddr_str_from_32n(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port)
OSMO_DEPRECATED("osmo_sockaddr_str_from_32n() actually uses *host* byte order. Use osmo_sockaddr_str_from_32h() instead");
diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h
index a053391d..ea73cda8 100644
--- a/include/osmocom/core/socket.h
+++ b/include/osmocom/core/socket.h
@@ -14,12 +14,26 @@
#include <arpa/inet.h>
+#include <osmocom/core/defs.h>
+
+/*! maximum number of local or remote addresses supported by an osmo_sock instance */
+#define OSMO_SOCK_MAX_ADDRS 32
+
/*! maximum length of a socket name ("r=1.2.3.4:123<->l=5.6.7.8:987") */
#define OSMO_SOCK_NAME_MAXLEN (2 + INET6_ADDRSTRLEN + 1 + 5 + 3 + 2 + INET6_ADDRSTRLEN + 1 + 5 + 1)
+/*! maximum length of a multi-address socket peer (endpoint) name: (5.6.7.8|::9):987
+ * char '(' + OSMO_STREAM_MAX_ADDRS - 1 addr separators + chars "):" + port buffer + char '\0'
+ */
+#define OSMO_SOCK_MULTIADDR_PEER_STR_MAXLEN (INET6_ADDRSTRLEN * OSMO_SOCK_MAX_ADDRS + INET6_ADDRSTRLEN + 2 + 6 + 1)
+/*! maximum length of a multia-address socket name ("r=(::2|1.2.3.4):123<->l=(5.6.7.8|::9):987") */
+#define OSMO_SOCK_MULTIADDR_NAME_MAXLEN (OSMO_SOCK_MULTIADDR_PEER_STR_MAXLEN + 7)
+
+
struct sockaddr_in;
struct sockaddr;
struct osmo_fd;
+struct sctp_paddrinfo;
struct osmo_sockaddr {
union {
@@ -30,6 +44,58 @@ struct osmo_sockaddr {
} u;
};
+int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen);
+int osmo_sockaddr_is_any(const struct osmo_sockaddr *addr);
+
+/*! Return the size of the variant used in the address
+ * NOTE: This does not return the size of the in{,6}_addr, but rather the size of the
+ * surrounding sockaddr_in{,6}.
+ * \param[in] addr the osmo_sockaddr to get the size of
+ * \return the size of the struct variant being used. If the value in sa_family is unsupported it will return
+ * the size of struct osmo_sockaddr. Returns 0 if addr is NULL. This way it can simply be a wrapper for sendto()
+ * which can be called with NULL/0 for dest_addr / addrlen (and then behaves like a send() call).
+ */
+static inline socklen_t osmo_sockaddr_size(const struct osmo_sockaddr *addr)
+{
+ if (!addr)
+ return 0;
+
+ switch (addr->u.sa.sa_family) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ default:
+ return sizeof(struct osmo_sockaddr);
+ }
+}
+
+unsigned int osmo_sockaddr_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
+ const struct sockaddr *sa);
+size_t osmo_sockaddr_in_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
+ const struct sockaddr_in *sin);
+
+const char *osmo_sockaddr_ntop(const struct sockaddr *sa, char *dst);
+uint16_t osmo_sockaddr_port(const struct sockaddr *sa);
+void osmo_sockaddr_set_port(struct sockaddr *sa, uint16_t port);
+
+int osmo_sockaddr_local_ip(struct osmo_sockaddr *local_ip,
+ const struct osmo_sockaddr *remote_ip);
+int osmo_sockaddr_cmp(const struct osmo_sockaddr *a,
+ const struct osmo_sockaddr *b);
+
+int osmo_sockaddr_to_octets(uint8_t *dst, size_t dst_maxlen, const struct osmo_sockaddr *os);
+int osmo_sockaddr_from_octets(struct osmo_sockaddr *os, const void *src, size_t src_len);
+int osmo_sockaddr_from_str_and_uint(struct osmo_sockaddr *osa_out, const char *ipstr, uint16_t port);
+
+int osmo_sockaddr_netmask_to_prefixlen(const struct osmo_sockaddr *addr);
+
+const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *sockaddr);
+char *osmo_sockaddr_to_str_buf(char *buf, size_t buf_len,
+ const struct osmo_sockaddr *sockaddr);
+int osmo_sockaddr_to_str_buf2(char *buf, size_t buf_len, const struct osmo_sockaddr *sockaddr);
+char *osmo_sockaddr_to_str_c(void *ctx, const struct osmo_sockaddr *sockaddr);
+
/* flags for osmo_sock_init. */
/*! connect the socket to a remote peer */
#define OSMO_SOCK_F_CONNECT (1 << 0)
@@ -53,9 +119,6 @@ struct osmo_sockaddr {
#define GET_OSMO_SOCK_F_PRIO(f) (((f) >> 16) & 0xff)
-/*! maximum number of local or remote addresses supported by an osmo_sock instance */
-#define OSMO_SOCK_MAX_ADDRS 32
-
int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
const char *host, uint16_t port, unsigned int flags);
@@ -63,9 +126,43 @@ int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
const char *local_host, uint16_t local_port,
const char *remote_host, uint16_t remote_port, unsigned int flags);
+struct osmo_sock_init2_multiaddr_pars {
+ union {
+ struct {
+ uint8_t version; /* set to 0 */
+ struct {
+ bool set;
+ bool abort_on_failure;
+ uint32_t value;
+ } sockopt_auth_supported;
+ struct {
+ bool set;
+ bool abort_on_failure;
+ uint32_t value;
+ } sockopt_asconf_supported;
+ struct {
+ bool set;
+ bool abort_on_failure;
+ bool num_ostreams_present;
+ bool max_instreams_present;
+ bool max_attempts_present;
+ bool max_init_timeo_present;
+ uint16_t num_ostreams_value;
+ uint16_t max_instreams_value;
+ uint16_t max_attempts_value;
+ uint16_t max_init_timeo_value;
+ } sockopt_initmsg;
+ } sctp;
+ };
+};
int osmo_sock_init2_multiaddr(uint16_t family, uint16_t type, uint8_t proto,
const char **local_hosts, size_t local_hosts_cnt, uint16_t local_port,
- const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port, unsigned int flags);
+ const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port, unsigned int flags)
+ OSMO_DEPRECATED_OUTSIDE("Use osmo_sock_init2_multiaddr2() instead");
+int osmo_sock_init2_multiaddr2(uint16_t family, uint16_t type, uint8_t proto,
+ const char **local_hosts, size_t local_hosts_cnt, uint16_t local_port,
+ const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port,
+ unsigned int flags, struct osmo_sock_init2_multiaddr_pars *pars);
int osmo_sock_init_osa(uint16_t type, uint8_t proto,
const struct osmo_sockaddr *local,
@@ -87,16 +184,6 @@ int osmo_sock_init_osa_ofd(struct osmo_fd *ofd, int type, int proto,
int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
uint8_t proto, unsigned int flags);
-int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen);
-
-unsigned int osmo_sockaddr_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
- const struct sockaddr *sa);
-size_t osmo_sockaddr_in_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
- const struct sockaddr_in *sin);
-
-const char *osmo_sockaddr_ntop(const struct sockaddr *sa, char *dst);
-uint16_t osmo_sockaddr_port(const struct sockaddr *sa);
-
int osmo_sock_unix_init(uint16_t type, uint8_t proto,
const char *socket_path, unsigned int flags);
@@ -113,6 +200,14 @@ int osmo_sock_get_local_ip_port(int fd, char *port, size_t len);
int osmo_sock_get_remote_ip(int fd, char *host, size_t len);
int osmo_sock_get_remote_ip_port(int fd, char *port, size_t len);
+int osmo_sock_multiaddr_get_ip_and_port(int fd, int ip_proto, char *ip, size_t *ip_cnt, size_t ip_len,
+ char *port, size_t port_len, bool local);
+int osmo_multiaddr_ip_and_port_snprintf(char *str, size_t str_len,
+ const char *ip, size_t ip_cnt, size_t ip_len,
+ const char *portbuf);
+int osmo_sock_multiaddr_get_name_buf(char *str, size_t str_len, int fd, int sk_proto);
+int osmo_sock_multiaddr_add_local_addr(int sfd, const char **addrs, size_t addrs_cnt);
+int osmo_sock_multiaddr_del_local_addr(int sfd, const char **addrs, size_t addrs_cnt);
int osmo_sock_mcast_loop_set(int fd, bool enable);
int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl);
@@ -122,17 +217,10 @@ int osmo_sock_mcast_subscribe(int fd, const char *grp_addr);
int osmo_sock_local_ip(char *local_ip, const char *remote_ip);
-int osmo_sockaddr_local_ip(struct osmo_sockaddr *local_ip,
- const struct osmo_sockaddr *remote_ip);
-int osmo_sockaddr_cmp(const struct osmo_sockaddr *a,
- const struct osmo_sockaddr *b);
-
-const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *sockaddr);
-char *osmo_sockaddr_to_str_buf(char *buf, size_t buf_len,
- const struct osmo_sockaddr *sockaddr);
-
int osmo_sock_set_dscp(int fd, uint8_t dscp);
int osmo_sock_set_priority(int fd, int prio);
+int osmo_sock_sctp_get_peer_addr_info(int fd, struct sctp_paddrinfo *pinfo, size_t *pinfo_cnt);
+
#endif /* (!EMBEDDED) */
/*! @} */
diff --git a/include/osmocom/core/socket_compat.h.tpl b/include/osmocom/core/socket_compat.h.tpl
new file mode 100644
index 00000000..43bee9ee
--- /dev/null
+++ b/include/osmocom/core/socket_compat.h.tpl
@@ -0,0 +1,10 @@
+#define HAVE_STRUCT_SOCKADDR_STORAGE XX
+
+#if HAVE_STRUCT_SOCKADDR_STORAGE
+ #include <sys/socket.h>
+#else
+struct sockaddr_storage {
+ unsigned short ss_family;
+ char __data[128 - sizeof(unsigned short)];
+};
+#endif
diff --git a/include/osmocom/core/soft_uart.h b/include/osmocom/core/soft_uart.h
new file mode 100644
index 00000000..afc6ad6d
--- /dev/null
+++ b/include/osmocom/core/soft_uart.h
@@ -0,0 +1,149 @@
+#pragma once
+
+/*! \file soft_uart.h
+ * Software UART implementation. */
+/*
+ * (C) 2022 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/msgb.h>
+
+/*! Parity mode.
+ * https://en.wikipedia.org/wiki/Parity_bit */
+enum osmo_soft_uart_parity_mode {
+ OSMO_SUART_PARITY_NONE, /*!< No parity bit */
+ OSMO_SUART_PARITY_EVEN, /*!< Even parity */
+ OSMO_SUART_PARITY_ODD, /*!< Odd parity */
+ OSMO_SUART_PARITY_MARK, /*!< Always 1 */
+ OSMO_SUART_PARITY_SPACE, /*!< Always 0 */
+ _OSMO_SUART_PARITY_NUM
+};
+
+/*! Flags passed to the application. */
+enum osmo_soft_uart_flags {
+ OSMO_SUART_F_FRAMING_ERROR = (1 << 0), /*!< Framing error occurred */
+ OSMO_SUART_F_PARITY_ERROR = (1 << 1), /*!< Parity error occurred */
+ OSMO_SUART_F_BREAK = (1 << 2), /*!< Break condition (not implemented) */
+};
+
+/*! Modem status "line" flags.
+ * https://en.wikipedia.org/wiki/RS-232#Data_and_control_signals */
+enum osmo_soft_uart_status {
+ OSMO_SUART_STATUS_F_DTR = (1 << 0), /*!< Data Terminal Ready */
+ OSMO_SUART_STATUS_F_DCD = (1 << 1), /*!< Data Carrier Detect */
+ OSMO_SUART_STATUS_F_DSR = (1 << 2), /*!< Data Set Ready */
+ OSMO_SUART_STATUS_F_RI = (1 << 3), /*!< Ring Indicator */
+ OSMO_SUART_STATUS_F_RTS_RTR = (1 << 4), /*!< Request To Send or Ready To Receive */
+ OSMO_SUART_STATUS_F_CTS = (1 << 5), /*!< Clear To Send */
+};
+
+/*! Flow control mode.
+ * https://en.wikipedia.org/wiki/Flow_control_(data)#Hardware_flow_control */
+enum osmo_soft_uart_flow_ctrl_mode {
+ /*! No flow control */
+ OSMO_SUART_FLOW_CTRL_NONE,
+ /*! DTR/DSR flow control: Tx if DSR is active and drop DTR if cannot Rx anymore. */
+ OSMO_SUART_FLOW_CTRL_DTR_DSR,
+ /*! RTS/CTS flow control: Tx if CTS is active and drop RTS if cannot Rx anymore.
+ * The technically correct name would be RTR/CTS, because the RTS signal actually
+ * indicates readiness to *receive* data (Ready To Receive), and not really used
+ * to request a transmission (Request To Send) nowadays. Alternatively, the RTS
+ * signal can be interpreted as "Request To Send to me". */
+ OSMO_SUART_FLOW_CTRL_RTS_CTS,
+};
+
+/*! Configuration for a soft-UART. */
+struct osmo_soft_uart_cfg {
+ /*! Number of data bits (typically 5, 6, 7 or 8). */
+ uint8_t num_data_bits;
+ /*! Number of stop bits (typically 1 or 2). */
+ uint8_t num_stop_bits;
+ /*! Parity mode (none, even, odd, space, mark). */
+ enum osmo_soft_uart_parity_mode parity_mode;
+ /*! Size of the receive buffer; UART will buffer up to that number
+ * of characters before calling the receive call-back. */
+ unsigned int rx_buf_size;
+ /*! Receive timeout; UART will flush the receive buffer via the receive call-back
+ * after indicated number of milliseconds, even if it is not full yet. */
+ unsigned int rx_timeout_ms;
+
+ /*! Opaque application-private data; passed to call-backs. */
+ void *priv;
+
+ /*! Receive call-back of the application.
+ *
+ * Called if at least one of the following conditions is met:
+ * a) rx_buf_size characters were received (Rx buffer is full);
+ * b) rx_timeout_ms expired and Rx buffer is not empty;
+ * c) a parity or framing error is occurred.
+ *
+ * \param[in] priv opaque application-private data.
+ * \param[in] rx_data msgb holding the received data.
+ * Must be free()ed by the application.
+ * \param[in] flags bit-mask of OSMO_SUART_F_*. */
+ void (*rx_cb)(void *priv, struct msgb *rx_data, unsigned int flags);
+
+ /*! Transmit call-back of the application.
+ *
+ * The implementation is expected to provide at most tx_data->data_len
+ * characters (the actual amount is determined by the number of requested
+ * bits and the effective UART configuration).
+ *
+ * \param[in] priv opaque application-private data.
+ * \param[inout] tx_data msgb for writing to be transmitted data. */
+ void (*tx_cb)(void *priv, struct msgb *tx_data);
+
+ /*! Modem status line change call-back.
+ * \param[in] priv opaque application-private data.
+ * \param[in] status updated status; bit-mask of OSMO_SUART_STATUS_F_*. */
+ void (*status_change_cb)(void *priv, unsigned int status);
+
+ /*! "Hardware" flow control mode. */
+ enum osmo_soft_uart_flow_ctrl_mode flow_ctrl_mode;
+};
+
+extern const struct osmo_soft_uart_cfg osmo_soft_uart_default_cfg;
+
+struct osmo_soft_uart;
+
+struct osmo_soft_uart *osmo_soft_uart_alloc(void *ctx, const char *name,
+ const struct osmo_soft_uart_cfg *cfg);
+void osmo_soft_uart_free(struct osmo_soft_uart *suart);
+int osmo_soft_uart_configure(struct osmo_soft_uart *suart, const struct osmo_soft_uart_cfg *cfg);
+
+const char *osmo_soft_uart_get_name(const struct osmo_soft_uart *suart);
+void osmo_soft_uart_set_name(struct osmo_soft_uart *suart, const char *name);
+
+int osmo_soft_uart_set_rx(struct osmo_soft_uart *suart, bool enable);
+int osmo_soft_uart_set_tx(struct osmo_soft_uart *suart, bool enable);
+
+int osmo_soft_uart_rx_ubits(struct osmo_soft_uart *suart, const ubit_t *ubits, size_t n_ubits);
+int osmo_soft_uart_tx_ubits(struct osmo_soft_uart *suart, ubit_t *ubits, size_t n_ubits);
+
+unsigned int osmo_soft_uart_get_status(const struct osmo_soft_uart *suart);
+int osmo_soft_uart_set_status(struct osmo_soft_uart *suart, unsigned int status);
+void osmo_soft_uart_set_status_line(struct osmo_soft_uart *suart,
+ enum osmo_soft_uart_status line,
+ bool active);
+
+void osmo_soft_uart_flush_rx(struct osmo_soft_uart *suart);
diff --git a/include/osmocom/core/stat_item.h b/include/osmocom/core/stat_item.h
index 29e35ef6..7f6857c0 100644
--- a/include/osmocom/core/stat_item.h
+++ b/include/osmocom/core/stat_item.h
@@ -14,32 +14,16 @@ struct osmo_stat_item_desc;
#define OSMO_STAT_ITEM_NOVALUE_ID 0
#define OSMO_STAT_ITEM_NO_UNIT NULL
-/*! Individual entry in value FIFO */
-struct osmo_stat_item_value {
- int32_t id; /*!< identifier of value */
- int32_t value; /*!< actual value */
-};
-
-/*! data we keep for each actual item */
-struct osmo_stat_item {
- /*! back-reference to the item description */
- const struct osmo_stat_item_desc *desc;
- /* internal use by stats API (stats.c): the id of the next value to
- * be read from the FIFO. If accessing osmo_stat_item directly, without
- * the stats API, store this value elsewhere. */
- int32_t stats_next_id;
- /*! the index of the last value written to the FIFO */
- int16_t last_offs;
- /*! value FIFO */
- struct osmo_stat_item_value values[0];
-};
+/*! data we keep for each actual item. Access via API functions only.
+ * (This struct was made opaque after libosmocore 1.5.1)*/
+struct osmo_stat_item;
/*! Statistics item description */
struct osmo_stat_item_desc {
const char *name; /*!< name of the item */
const char *description;/*!< description of the item */
const char *unit; /*!< unit of a value */
- unsigned int num_values;/*!< number of values to store in FIFO */
+ unsigned int num_values;/*!< DEPRECATED, this value is ignored after libosmocore version 1.5.1 */
int32_t default_value; /*!< default value */
};
@@ -65,13 +49,15 @@ struct osmo_stat_item_group {
const struct osmo_stat_item_group_desc *desc;
/*! The index of this value group within its class */
unsigned int idx;
+ /*! Optional string-based identifier to be used instead of index at report time */
+ char *name;
/*! Actual counter structures below */
struct osmo_stat_item *items[0];
};
struct osmo_stat_item_group *osmo_stat_item_group_alloc(
void *ctx,
- const struct osmo_stat_item_group_desc *desc,
+ const struct osmo_stat_item_group_desc *group_desc,
unsigned int idx);
static inline void osmo_stat_item_group_udp_idx(
@@ -79,7 +65,8 @@ static inline void osmo_stat_item_group_udp_idx(
{
grp->idx = idx;
}
-
+struct osmo_stat_item *osmo_stat_item_group_get_item(struct osmo_stat_item_group *grp, unsigned int idx);
+void osmo_stat_item_group_set_name(struct osmo_stat_item_group *statg, const char *name);
void osmo_stat_item_group_free(struct osmo_stat_item_group *statg);
void osmo_stat_item_inc(struct osmo_stat_item *item, int32_t value);
@@ -90,19 +77,14 @@ int osmo_stat_item_init(void *tall_ctx);
struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx(
const char *name, const unsigned int idx);
+struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idxname(const char *group_name, const char *idx_name);
const struct osmo_stat_item *osmo_stat_item_get_by_name(
const struct osmo_stat_item_group *statg, const char *name);
-int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *next_id, int32_t *value);
-
-/*! Get the last (freshest) value */
-static int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item);
+int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item);
-int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *next_id);
-
-int osmo_stat_item_discard_all(int32_t *next_id)
- OSMO_DEPRECATED("Use osmo_stat_item_discard with item-specific next_id instead");
+void osmo_stat_item_flush(struct osmo_stat_item *item);
typedef int (*osmo_stat_item_handler_t)(
struct osmo_stat_item_group *, struct osmo_stat_item *, void *);
@@ -114,12 +96,20 @@ int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg,
int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data);
-static inline int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item)
-{
- return item->values[item->last_offs].value;
-}
-
void osmo_stat_item_reset(struct osmo_stat_item *item);
void osmo_stat_item_group_reset(struct osmo_stat_item_group *statg);
+const struct osmo_stat_item_desc *osmo_stat_item_get_desc(struct osmo_stat_item *item);
+
+/* DEPRECATION: up until libosmocore 1.5.1, this API also defined
+ * - struct osmo_stat_item_value
+ * - osmo_stat_item_get_next()
+ * - osmo_stat_item_discard()
+ * - osmo_stat_item_discard_all()
+ * Despite our principle of never breaking API compatibility, we have decided to remove these, because there are no
+ * known users. These items were never practically used/usable outside of libosmocore since the generic stats reporter
+ * (stats.c) was introduced.
+ * We also decided to make struct osmo_stat_item opaque to allow future changes of the struct without API breakage.
+ */
+
/*! @} */
diff --git a/include/osmocom/core/stats.h b/include/osmocom/core/stats.h
index b9edac2a..a034a616 100644
--- a/include/osmocom/core/stats.h
+++ b/include/osmocom/core/stats.h
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -108,10 +104,11 @@ struct osmo_stats_config {
int interval;
};
+extern struct llist_head osmo_stats_reporter_list;
extern struct osmo_stats_config *osmo_stats_config;
void osmo_stats_init(void *ctx);
-int osmo_stats_report();
+int osmo_stats_report(void);
int osmo_stats_set_interval(int interval);
diff --git a/include/osmocom/core/stats_tcp.h b/include/osmocom/core/stats_tcp.h
new file mode 100644
index 00000000..9bc7111a
--- /dev/null
+++ b/include/osmocom/core/stats_tcp.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#define TCP_STATS_DEFAULT_INTERVAL 0 /* secs */
+#define TCP_STATS_DEFAULT_BATCH_SIZE 5 /* sockets per interval */
+
+struct osmo_tcp_stats_config {
+ /* poll interval in seconds, use osmo_stats_tcp_set_interval() to manipulate this value */
+ int interval;
+ /* specify how many sockets are processed when the interval timer expires */
+ int batch_size;
+};
+extern struct osmo_tcp_stats_config *osmo_tcp_stats_config;
+
+int osmo_stats_tcp_osmo_fd_register(const struct osmo_fd *fd, const char *name);
+int osmo_stats_tcp_osmo_fd_unregister(const struct osmo_fd *fd);
+int osmo_stats_tcp_set_interval(int interval);
diff --git a/include/osmocom/core/strrb.h b/include/osmocom/core/strrb.h
index b87239da..92d6a2f2 100644
--- a/include/osmocom/core/strrb.h
+++ b/include/osmocom/core/strrb.h
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -30,8 +26,6 @@
#include <stdbool.h>
#include <stdint.h>
-#include <osmocom/core/talloc.h>
-
/*! A structure representing an osmocom string ringbuffer */
#define RB_MAX_MESSAGE_SIZE 240
@@ -42,7 +36,7 @@ struct osmo_strrb {
char **buffer; /*!< storage for messages */
};
-struct osmo_strrb *osmo_strrb_create(TALLOC_CTX * ctx, size_t rb_size);
+struct osmo_strrb *osmo_strrb_create(void *talloc_ctx, size_t rb_size);
bool osmo_strrb_is_empty(const struct osmo_strrb *rb);
const char *osmo_strrb_get_nth(const struct osmo_strrb *rb,
unsigned int string_index);
diff --git a/include/osmocom/core/tdef.h b/include/osmocom/core/tdef.h
index d9d26751..402d0102 100644
--- a/include/osmocom/core/tdef.h
+++ b/include/osmocom/core/tdef.h
@@ -121,11 +121,13 @@ struct osmo_tdef_state_timeout {
const struct osmo_tdef_state_timeout *osmo_tdef_get_state_timeout(uint32_t state,
const struct osmo_tdef_state_timeout *timeouts_array);
-/*! Call osmo_fsm_inst_state_chg() or osmo_fsm_inst_state_chg_keep_timer(), depending on the timeouts_array, tdefs and
- * default_timeout.
+/*! Call osmo_fsm_inst_state_chg[_ms]() or osmo_fsm_inst_state_chg_keep_timer[_ms](),
+ * depending on the timeouts_array, tdefs and default_timeout.
*
- * A T timer configured in sub-second precision is rounded up to the next full second. A timer in unit =
- * OSMO_TDEF_CUSTOM is applied as if the unit is in seconds (i.e. this macro does not make sense for custom units!).
+ * A timer defined with sub-millisecond precision (e.g OSMO_TDEF_US) is rounded up to the next full millisecond.
+ * A timer value defined in units higher than millisecond (e.g. OSMO_TDEF_S, OSMO_TDEF_M) is converted to milliseconds.
+ * A timer in unit = OSMO_TDEF_CUSTOM is applied as if the unit is in seconds (i.e. this macro does not make sense
+ * for custom units!).
*
* See osmo_tdef_get_state_timeout() and osmo_tdef_get().
*
@@ -153,9 +155,10 @@ const struct osmo_tdef_state_timeout *osmo_tdef_get_state_timeout(uint32_t state
* \param[in] state State number to transition to.
* \param[in] timeouts_array Array of struct osmo_tdef_state_timeout[32] to look up state in.
* \param[in] tdefs Array of struct osmo_tdef (last entry zero initialized) to look up T in.
- * \param[in] default_timeout If a T is set in timeouts_array, but no timeout value is configured for T, then use this
- * default timeout value as fallback, or pass -1 to abort the program.
- * \return Return value from osmo_fsm_inst_state_chg() or osmo_fsm_inst_state_chg_keep_timer().
+ * \param[in] default_timeout If a T is set in timeouts_array, but no timeout value is configured for T,
+ * then use this default timeout value (in seconds) as fallback,
+ * or pass a negative number to abort the program.
+ * \return Return value from osmo_fsm_inst_state_chg[_ms]() or osmo_fsm_inst_state_chg_keep_timer[_ms]().
*/
#define osmo_tdef_fsm_inst_state_chg(fi, state, timeouts_array, tdefs, default_timeout) \
_osmo_tdef_fsm_inst_state_chg(fi, state, timeouts_array, tdefs, default_timeout, \
diff --git a/include/osmocom/core/thread.h b/include/osmocom/core/thread.h
index 40b54366..d857268d 100644
--- a/include/osmocom/core/thread.h
+++ b/include/osmocom/core/thread.h
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \defgroup thread Osmocom thread helpers
diff --git a/include/osmocom/core/time_cc.h b/include/osmocom/core/time_cc.h
new file mode 100644
index 00000000..36fdee46
--- /dev/null
+++ b/include/osmocom/core/time_cc.h
@@ -0,0 +1,187 @@
+/*! \file time_cc.h
+ * Report the cumulative counter of time for which a flag is true as rate counter.
+ */
+/* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * 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>
+
+#include <osmocom/core/timer.h>
+
+/*! \defgroup time_cc Cumulative counter of time as rate counter.
+ * @{
+ * \file time_cc.h
+ */
+
+struct osmo_tdef;
+struct rate_ctr;
+
+/*! Configuration for osmo_time_cc.
+ * Report the cumulative counter of time for which a flag is true as rate counter.
+ * For example, for each second that the flag is true, increment a rate counter.
+ *
+ * The flag to be monitored is reported by osmo_time_cc_set_flag().
+ *
+ * The granularity defines how much time one rate counter increment represents:
+ * the default configuration is gran_usec = 1000000, i.e. one rate counter increment represents one second.
+ *
+ * Reporting as rate counter is configurable by round_threshold_usec and forget_sum_usec, examples:
+ *
+ * round_threshold_usec:
+ * - To get "ceil()" behavior, set round_threshold_usec = 1. This increments the rate counter for each gran_usec period
+ * where the flag was seen true, even if it was true for only a very short fraction of a gran_usec period.
+ * - To get "round()" behavior, set round_threshold_usec = half of gran_usec. The rate counter increments when the flag
+ * has been true for 0.5 of a gran_usec (and then again at 1.5 * gran_usec) of 'true' flag. round_threshold_usec = 0
+ * is a special value that means to use half of gran_usec.
+ * - To get "floor()" behavior, set round_threshold_usec >= gran_usec. The rate counter increments when reaching full
+ * gran_usec periods of the flag being true.
+ *
+ * forget_sum_usec:
+ * This is a tradeoff between the accuracy of the reported rate counter and making sure that the events reported are not
+ * irrelevantly long ago.
+ * - To keep sub-granularity-period surplus time forever, set forget_sum_usec = 0.
+ * - To keep surplus time for up to a minute, set forget_sum_usec = 60000000 (60 seconds).
+ * - To get rid of "leftover" time (almost) immediately after the flag goes false, set forget_sum_usec = 1.
+ * - If gran_usec is set to one second and forget_sum_usec is set to one minute, the reported rate counter has a
+ * possible inaccuracy of 1/60th, but makes sure that no timings older than a minute affect the current reports.
+ *
+ * Reporting modes in detail:
+ *
+ * The rate_ctr increments when the cumulative counter passes round_threshold_usec (default: half of gran_usec).
+ *
+ * sum ^
+ * | ________
+ * | /
+ * | /
+ * | /
+ * 3*gran --+--------------------------------------+
+ * | /:
+ * | / :
+ * | - - - - - - - - - - - - - - - - - / :
+ * | /. :
+ * | / . :
+ * 2*gran --+--------------------------------+ . :
+ * | /: . :
+ * | / : . :
+ * | - - - - - - - - - -_________/ : . :
+ * | / . : . :
+ * | / . : . :
+ * 1*gran --+-----------------+ . : . :
+ * | /: . : . :
+ * | / : . : . :
+ * | - - - - - - -/ : . : . :
+ * | /. : . : . :
+ * | ....-------' . : . : . :
+ * 0 +------------------------------------------------------------------------> elapsed time
+ * . : . : . :
+ * _ _ _______ ____________
+ * flag: __| |_| |____| . : |_______|. : . : |__________
+ * f t f t f t . : f t. : . : f
+ * round_threshold_usec : . : . : . :
+ * = 1 usec: 0 1 . :2 . :3 . :4 = "ceil()"
+ * = 0 == gran_usec/2: 0 1 : 2 : 3 : = "round()"
+ * >= gran_usec: 0 1 2 3 = "floor()"
+ *
+ */
+struct osmo_time_cc_cfg {
+ /*! Granularity in microseconds: nr of microseconds that one rate_ctr increment represents. A typical value is
+ * gran_usec = 1000000, meaning one rate counter increment represents one second. When zero, use 1000000. */
+ uint64_t gran_usec;
+ /*! Nr of microseconds above n * gran_usec at which to trigger a counter increment. When zero, use half a
+ * gran_usec. */
+ uint64_t round_threshold_usec;
+ /*! Forget counted sub-gran time after the flag was false for this long. */
+ uint64_t forget_sum_usec;
+ /*! Rate counter to report to, or NULL to not use it. */
+ struct rate_ctr *rate_ctr;
+
+ /*! Update gran_usec from this T timer value, or zero to not use any T timer. */
+ int T_gran;
+ /*! Update round_threshold_usec from this T timer value, or zero to not use any T timer. */
+ int T_round_threshold;
+ /*! Update forget_sum_usec from this T timer value, or zero to not use any T timer. */
+ int T_forget_sum;
+ /*! Look up T_gran and T_forget_sum in this list of timers, or NULL to not use any T timers. */
+ struct osmo_tdef *T_defs;
+};
+
+/*! Report the cumulative counter of time for which a flag is true as rate counter.
+ * See also osmo_time_cc_cfg for details on configuring.
+ *
+ * Usage:
+ *
+ * struct my_obj {
+ * struct osmo_time_cc flag_cc;
+ * };
+ *
+ * void my_obj_init(struct my_obj *my_obj)
+ * {
+ * osmo_time_cc_init(&my_obj->flag_cc);
+ * my_obj->flag_cc.cfg = (struct osmo_time_cc_cfg){
+ * .gran_usec = 1000000,
+ * .forget_sum_usec = 60000000,
+ * .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, MY_CTR_IDX),
+ * };
+ * // optional: set initial flag state, default is 'false':
+ * // osmo_time_cc_set_flag(&my_obj->flag_cc, false);
+ * }
+ *
+ * void my_obj_event(struct my_obj *my_obj, bool flag)
+ * {
+ * osmo_time_cc_set_flag(&my_obj->flag_cc, flag);
+ * }
+ *
+ * void my_obj_destruct(struct my_obj *my_obj)
+ * {
+ * osmo_time_cc_cleanup(&my_obj->flag_cc);
+ * }
+ */
+struct osmo_time_cc {
+ struct osmo_time_cc_cfg cfg;
+
+ bool flag_state;
+
+ /*! Overall cumulative sum. Does not get reset for the entire lifetime of an osmo_time_cc.
+ * (Informational only, not used by the osmo_time_cc implementation.) */
+ uint64_t total_sum;
+
+ struct osmo_timer_list timer;
+
+ /*! CLOCK_MONOTONIC reading in microseconds, at the time when the osmo_time_cc instance started counting. */
+ uint64_t start_time;
+ /*! CLOCK_MONOTONIC reading in microseconds, at the time when the osmo_time_cc last evaluated the flag state and
+ * possibly added to the cumulated sum. */
+ uint64_t last_counted_time;
+
+ /*! Internal cumulative counter of time that flag_state was true. It may get reset to zero regularly, depending
+ * on cfg.forget_sum_usec. This is the basis for incrementing cfg.rate_ctr. */
+ uint64_t sum;
+ /*! The amount of time that already reported cfg.rate_ctr increments account for. This may be ahead of or behind
+ * 'sum', depending on cfg.round_threshold_usec. */
+ uint64_t reported_sum;
+};
+
+void osmo_time_cc_init(struct osmo_time_cc *tc);
+void osmo_time_cc_set_flag(struct osmo_time_cc *tc, bool flag);
+void osmo_time_cc_cleanup(struct osmo_time_cc *tc);
+
+/*! @} */
diff --git a/include/osmocom/core/timer.h b/include/osmocom/core/timer.h
index 61d20e2d..16338dad 100644
--- a/include/osmocom/core/timer.h
+++ b/include/osmocom/core/timer.h
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \defgroup timer Osmocom timers
diff --git a/include/osmocom/core/timer_compat.h b/include/osmocom/core/timer_compat.h
index 916f5684..fd52ae36 100644
--- a/include/osmocom/core/timer_compat.h
+++ b/include/osmocom/core/timer_compat.h
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \defgroup timer Osmocom timers
diff --git a/include/osmocom/core/tun.h b/include/osmocom/core/tun.h
new file mode 100644
index 00000000..86bd8df0
--- /dev/null
+++ b/include/osmocom/core/tun.h
@@ -0,0 +1,43 @@
+/*! \file tun.h
+ * tunnel network device convenience functions. */
+
+#pragma once
+#if (!EMBEDDED)
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/netdev.h>
+
+struct osmo_tundev;
+
+/* callback user gets ownership of the msgb and is expected to free it. */
+typedef int (*osmo_tundev_data_ind_cb_t)(struct osmo_tundev *tun, struct msgb *msg);
+
+struct osmo_tundev *osmo_tundev_alloc(void *ctx, const char *name);
+void osmo_tundev_free(struct osmo_tundev *tundev);
+int osmo_tundev_open(struct osmo_tundev *tundev);
+int osmo_tundev_close(struct osmo_tundev *tundev);
+bool osmo_tundev_is_open(struct osmo_tundev *tundev);
+
+void osmo_tundev_set_priv_data(struct osmo_tundev *tundev, void *priv_data);
+void *osmo_tundev_get_priv_data(struct osmo_tundev *tundev);
+
+void osmo_tundev_set_data_ind_cb(struct osmo_tundev *tundev, osmo_tundev_data_ind_cb_t data_ind_cb);
+
+const char *osmo_tundev_get_name(const struct osmo_tundev *tundev);
+
+int osmo_tundev_set_dev_name(struct osmo_tundev *tundev, const char *dev_name);
+const char *osmo_tundev_get_dev_name(const struct osmo_tundev *tundev);
+
+int osmo_tundev_set_netns_name(struct osmo_tundev *tundev, const char *netns);
+const char *osmo_tundev_get_netns_name(const struct osmo_tundev *tundev);
+
+struct osmo_netdev *osmo_tundev_get_netdev(struct osmo_tundev *tundev);
+
+int osmo_tundev_send(struct osmo_tundev *tundev, struct msgb *msg);
+
+#endif /* (!EMBEDDED) */
+/*! @} */
diff --git a/include/osmocom/core/use_count.h b/include/osmocom/core/use_count.h
index cc5493c7..532d8b14 100644
--- a/include/osmocom/core/use_count.h
+++ b/include/osmocom/core/use_count.h
@@ -19,10 +19,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h
index c9d5560d..92bea59f 100644
--- a/include/osmocom/core/utils.h
+++ b/include/osmocom/core/utils.h
@@ -33,14 +33,23 @@
/*! Make a value_string entry from an enum value name */
#define OSMO_VALUE_STRING(x) { x, #x }
/*! Number of bytes necessary to store given BITS */
-#define OSMO_BYTES_FOR_BITS(BITS) ((BITS + 8 - 1) / 8)
+#define OSMO_BYTES_FOR_BITS(BITS) (((BITS) + 7) / 8)
/*! Copy a C-string into a sized buffer using sizeof to detect buffer's size */
#define OSMO_STRLCPY_ARRAY(array, src) osmo_strlcpy(array, src, sizeof(array))
+/*! Branch prediction optimizations */
+#if defined(__GNUC__)
+#define OSMO_LIKELY(exp) __builtin_expect(!!(exp), 1)
+#define OSMO_UNLIKELY(exp) __builtin_expect(!!(exp), 0)
+#else
+#define OSMO_LIKELY(exp) exp
+#define OSMO_UNLIKELY(exp) exp
+#endif
+
/*! A mapping between human-readable string and numeric value */
struct value_string {
- int value; /*!< numeric value */
+ uint32_t value; /*!< numeric value */
const char *str; /*!< human-readable string */
};
@@ -57,7 +66,7 @@ uint8_t osmo_char2bcd(char c);
int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibble, int end_nibble, bool allow_hex);
int osmo_str2bcd(uint8_t *dst, size_t dst_size, const char *digits, int start_nibble, int end_nibble, bool allow_hex);
-int osmo_hexparse(const char *str, uint8_t *b, int max_len);
+int osmo_hexparse(const char *str, uint8_t *b, unsigned int max_len);
char *osmo_ubit_dump_buf(char *buf, size_t buf_len, const uint8_t *bits, unsigned int len);
char *osmo_ubit_dump(const uint8_t *bits, unsigned int len);
@@ -102,9 +111,11 @@ do { \
* the predicate evaluates to false (0).
*/
#define OSMO_ASSERT(exp) \
- if (!(exp)) { \
+do { \
+ if (OSMO_UNLIKELY(!(exp))) { \
osmo_panic("Assert failed %s %s:%d\n", #exp, __FILE__, __LINE__); \
- }
+ } \
+} while (0); /* some code invokes OSMO_ASSERT() without the semicolon */
/*! duplicate a string using talloc and release its prior content (if any)
* \param[in] ctx Talloc context to use for allocation
@@ -117,6 +128,8 @@ static inline void osmo_talloc_replace_string(void *ctx, char **dst, const char
*dst = talloc_strdup(ctx, newstr);
}
+void osmo_talloc_replace_string_fmt(void *ctx, char **dst, const char *fmt, ...);
+
/*! Append to a string and re-/allocate if necessary.
* \param[in] ctx Talloc context to use for initial allocation.
* \param[in,out] dest char* to re-/allocate and append to.
@@ -155,10 +168,12 @@ size_t osmo_quote_cstr_buf(char *buf, size_t bufsize, const char *str, int in_le
char *osmo_quote_cstr_c(void *ctx, const char *str, int in_len);
const char *osmo_escape_str(const char *str, int len);
+int osmo_escape_str_buf3(char *buf, size_t bufsize, const char *str, int in_len);
char *osmo_escape_str_buf2(char *buf, size_t bufsize, const char *str, int in_len);
const char *osmo_escape_str_buf(const char *str, int in_len, char *buf, size_t bufsize);
char *osmo_escape_str_c(const void *ctx, const char *str, int in_len);
const char *osmo_quote_str(const char *str, int in_len);
+int osmo_quote_str_buf3(char *buf, size_t bufsize, const char *str, int in_len);
char *osmo_quote_str_buf2(char *buf, size_t bufsize, const char *str, int in_len);
const char *osmo_quote_str_buf(const char *str, int in_len, char *buf, size_t bufsize);
char *osmo_quote_str_c(const void *ctx, const char *str, int in_len);
@@ -167,6 +182,18 @@ int osmo_print_n(char *buf, size_t bufsize, const char *str, size_t n);
uint32_t osmo_isqrt32(uint32_t x);
+/*! Floored Modulo (See also: Daan Leijen, Division and Modulus for Computer Scientists).
+ * \param[in] x dividend.
+ * \param[in] y divisor.
+ * \returns remainder of x divided by y. */
+#define OSMO_MOD_FLR(x, y) (((x) > 0 && (y) < 0) || ((x) < 0 && (y) > 0) ? (x) % (y) + (y) : (x) % (y))
+
+/*! Euclidean Modulo (See also: Daan Leijen, Division and Modulus for Computer Scientists).
+ * \param[in] x dividend.
+ * \param[in] y divisor.
+ * \returns remainder of x divided by y. */
+#define OSMO_MOD_EUC(x, y) ((x) % (y) < 0 ? (y) > 0 ? (x) % (y) + (y) : (x) % (y) - (y) : (x) % (y))
+
char osmo_luhn(const char* in, int in_len);
/*! State for OSMO_STRBUF_APPEND() and OSMO_STRBUF_PRINTF(). See there for examples. */
@@ -222,9 +249,9 @@ struct osmo_strbuf {
#define OSMO_STRBUF_APPEND(STRBUF, func, args...) do { \
if (!(STRBUF).pos) \
(STRBUF).pos = (STRBUF).buf; \
- size_t _sb_remain = (STRBUF).buf ? (STRBUF).len - ((STRBUF).pos - (STRBUF).buf) : 0; \
+ size_t _sb_remain = OSMO_STRBUF_REMAIN(STRBUF); \
int _sb_l = func((STRBUF).pos, _sb_remain, ##args); \
- if (_sb_l < 0 || _sb_l > _sb_remain) \
+ if (_sb_l < 0 || (size_t)_sb_l > _sb_remain) \
(STRBUF).pos = (STRBUF).buf + (STRBUF).len; \
else if ((STRBUF).pos) \
(STRBUF).pos += _sb_l; \
@@ -254,6 +281,38 @@ struct osmo_strbuf {
#define OSMO_STRBUF_PRINTF(STRBUF, fmt, args...) \
OSMO_STRBUF_APPEND(STRBUF, snprintf, fmt, ##args)
+/*! Get remaining space for characters and terminating nul in the given struct osmo_strbuf.
+ * \param[in] sb the string buffer to get the remaining space for.
+ * \returns remaining space in the given struct osmo_strbuf. */
+static inline size_t _osmo_strbuf_remain(const struct osmo_strbuf *sb)
+{
+ if (OSMO_UNLIKELY(sb == NULL || sb->buf == NULL))
+ return 0;
+ if (sb->pos == NULL)
+ return sb->len;
+ return sb->len - (sb->pos - sb->buf);
+}
+
+/*! Return remaining space for characters and terminating nul in the given struct osmo_strbuf. */
+#define OSMO_STRBUF_REMAIN(STRBUF) \
+ _osmo_strbuf_remain(&(STRBUF))
+
+/*! Get number of actual characters (without terminating nul) in the given struct osmo_strbuf.
+ * \param[in] sb the string buffer to get the number of characters for.
+ * \returns number of actual characters (without terminating nul). */
+static inline size_t _osmo_strbuf_char_count(const struct osmo_strbuf *sb)
+{
+ if (OSMO_UNLIKELY(sb == NULL || sb->buf == NULL))
+ return 0;
+ if (sb->pos == NULL || sb->pos <= sb->buf)
+ return 0;
+ return OSMO_MIN((size_t)(sb->pos - sb->buf), sb->len - 1);
+}
+
+/*! Return number of actual characters contained in struct osmo_strbuf (without terminating nul). */
+#define OSMO_STRBUF_CHAR_COUNT(STRBUF) \
+ _osmo_strbuf_char_count(&(STRBUF))
+
/*! Like OSMO_STRBUF_APPEND(), but for function signatures that return the char* buffer instead of a length.
* When using this function, the final STRBUF.chars_needed may not reflect the actual number of characters needed, since
* that number cannot be obtained from this kind of function signature.
@@ -265,7 +324,7 @@ struct osmo_strbuf {
#define OSMO_STRBUF_APPEND_NOLEN(STRBUF, func, args...) do { \
if (!(STRBUF).pos) \
(STRBUF).pos = (STRBUF).buf; \
- size_t _sb_remain = (STRBUF).buf ? (STRBUF).len - ((STRBUF).pos - (STRBUF).buf) : 0; \
+ size_t _sb_remain = OSMO_STRBUF_REMAIN(STRBUF); \
if (_sb_remain) { \
func((STRBUF).pos, _sb_remain, ##args); \
} \
@@ -277,12 +336,25 @@ struct osmo_strbuf {
(STRBUF).chars_needed += _sb_l; \
} while(0)
+void osmo_strbuf_drop_tail(struct osmo_strbuf *sb, size_t n_chars);
+/* Convenience macro. struct osmo_strbuf are typically static to a function scope. Avoid having to type '&', same as
+ * with all the other OSMO_STRBUF_* API. */
+#define OSMO_STRBUF_DROP_TAIL(STRBUF, N_CHARS) osmo_strbuf_drop_tail(&(STRBUF), N_CHARS)
+
+void osmo_strbuf_added_tail(struct osmo_strbuf *sb, size_t n_chars);
+/* Convenience macro. struct osmo_strbuf are typically static to a function scope. Avoid having to type '&', same as
+ * with all the other OSMO_STRBUF_* API. */
+#define OSMO_STRBUF_ADDED_TAIL(STRBUF, N_CHARS) osmo_strbuf_added_tail(&(STRBUF), N_CHARS)
+
bool osmo_str_startswith(const char *str, const char *startswith_str);
int osmo_float_str_to_int(int64_t *val, const char *str, unsigned int precision);
int osmo_int_to_float_str_buf(char *buf, size_t buflen, int64_t val, unsigned int precision);
char *osmo_int_to_float_str_c(void *ctx, int64_t val, unsigned int precision);
+int osmo_str_to_int64(int64_t *result, const char *str, int base, int64_t min_val, int64_t max_val);
+int osmo_str_to_int(int *result, const char *str, int base, int min_val, int max_val);
+
/*! Translate a buffer function to a talloc context function.
* This is the full function body of a char *foo_name_c(void *ctx, val...) function, implemented by an
* int foo_name_buf(buf, buflen, val...) function:
@@ -318,7 +390,7 @@ char *osmo_int_to_float_str_c(void *ctx, int64_t val, unsigned int precision);
_needed = FUNC_BUF(_str, _len, ## FUNC_BUF_ARGS); \
if (_needed < 0) \
goto OSMO_NAME_C_on_error; \
- if (_needed < _len) \
+ if ((unsigned int) _needed < _len) \
return _str; \
_len = _needed + 1; \
if (_str) \
diff --git a/include/osmocom/core/write_queue.h b/include/osmocom/core/write_queue.h
index 75d5d8fb..fe762829 100644
--- a/include/osmocom/core/write_queue.h
+++ b/include/osmocom/core/write_queue.h
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -54,6 +50,7 @@ void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length);
void osmo_wqueue_clear(struct osmo_wqueue *queue);
int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data);
int osmo_wqueue_enqueue_quiet(struct osmo_wqueue *queue, struct msgb *data);
+size_t osmo_wqueue_set_maxlen(struct osmo_wqueue *queue, unsigned int len);
int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what);
/*! @} */
diff --git a/include/osmocom/crypt/Makefile.am b/include/osmocom/crypt/Makefile.am
new file mode 100644
index 00000000..fa391bb3
--- /dev/null
+++ b/include/osmocom/crypt/Makefile.am
@@ -0,0 +1,8 @@
+osmocrypt_HEADERS = \
+ auth.h \
+ gprs_cipher.h \
+ kdf.h \
+ utran_cipher.h \
+ $(NULL)
+
+osmocryptdir = $(includedir)/osmocom/crypt
diff --git a/include/osmocom/crypt/auth.h b/include/osmocom/crypt/auth.h
index c653b616..1499ef85 100644
--- a/include/osmocom/crypt/auth.h
+++ b/include/osmocom/crypt/auth.h
@@ -30,12 +30,41 @@ enum osmo_auth_algo {
OSMO_AUTH_ALG_COMP128v1,
OSMO_AUTH_ALG_COMP128v2,
OSMO_AUTH_ALG_COMP128v3,
- OSMO_AUTH_ALG_XOR,
+ OSMO_AUTH_ALG_XOR_3G,
OSMO_AUTH_ALG_MILENAGE,
+ OSMO_AUTH_ALG_XOR_2G,
+ OSMO_AUTH_ALG_TUAK,
_OSMO_AUTH_ALG_NUM,
};
+/* Backwards-compatibility. We used to call XOR-3G just "XOR" which became ambiguous when
+ * we started to add XOR-2G support. */
+#define OSMO_AUTH_ALG_XOR OSMO_AUTH_ALG_XOR_3G
/*! permanent (secret) subscriber auth data */
+struct osmo_sub_auth_data2 {
+ enum osmo_sub_auth_type type;
+ enum osmo_auth_algo algo;
+ union {
+ struct {
+ /* See 3GPP TS 33.102 Section 9.3.7 Length of authentication parameters */
+ uint8_t opc[32]; /*!< operator invariant value */
+ uint8_t opc_len; /*!< OPc length (in bytes): 16 or 32 */
+ uint8_t k[32]; /*!< secret key of the subscriber */
+ uint8_t k_len; /*!< K length (in bytes): 16 or 32 */
+ uint8_t amf[2];
+ uint64_t sqn; /*!< sequence number (in: prev sqn; out: used sqn) */
+ int opc_is_op; /*!< is the OPC field OPC (0) or OP (1) ? */
+ unsigned int ind_bitlen; /*!< nr of bits not in SEQ, only SQN */
+ unsigned int ind; /*!< which IND slot to use an SQN from */
+ uint64_t sqn_ms; /*!< sqn from AUTS (output value only) */
+ } umts;
+ struct {
+ uint8_t ki[OSMO_A5_MAX_KEY_LEN_BYTES]; /*!< secret key */
+ } gsm;
+ } u;
+};
+
+/* deprecated older structure without support for 32-byte K/OP[c] */
struct osmo_sub_auth_data {
enum osmo_sub_auth_type type;
enum osmo_auth_algo algo;
@@ -63,7 +92,7 @@ struct osmo_auth_vector {
uint8_t ck[OSMO_A5_MAX_KEY_LEN_BYTES]; /*!< ciphering key */
uint8_t ik[OSMO_A5_MAX_KEY_LEN_BYTES]; /*!< integrity key */
uint8_t res[16]; /*!< authentication result */
- uint8_t res_len; /*!< length (in bytes) of res */
+ uint8_t res_len; /*!< length (in bytes) of res: 4..16 bytes */
uint8_t kc[8]; /*!< Kc for GSM encryption (A5) */
uint8_t sres[4]; /*!< authentication result for GSM */
uint32_t auth_types; /*!< bitmask of OSMO_AUTH_TYPE_* */
@@ -78,22 +107,32 @@ struct osmo_auth_impl {
/*! callback for generate authentication vectors */
int (*gen_vec)(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *_rand);
- /* callback for generationg auth vectors + re-sync */
+ /*! callback for generating auth vectors + re-sync */
int (*gen_vec_auts)(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *auts, const uint8_t *rand_auts,
const uint8_t *_rand);
};
int osmo_auth_gen_vec(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud, const uint8_t *_rand);
+ struct osmo_sub_auth_data *aud, const uint8_t *_rand)
+ OSMO_DEPRECATED_OUTSIDE("Use osmo_auth_gen_vec2 instead");
+
+int osmo_auth_gen_vec2(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud, const uint8_t *_rand);
int osmo_auth_gen_vec_auts(struct osmo_auth_vector *vec,
struct osmo_sub_auth_data *aud,
const uint8_t *auts, const uint8_t *rand_auts,
+ const uint8_t *_rand)
+ OSMO_DEPRECATED_OUTSIDE("Use osmo_auth_gen_vec_auts2 instead");
+
+int osmo_auth_gen_vec_auts2(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *auts, const uint8_t *rand_auts,
const uint8_t *_rand);
int osmo_auth_register(struct osmo_auth_impl *impl);
@@ -106,5 +145,6 @@ const char *osmo_auth_alg_name(enum osmo_auth_algo alg);
enum osmo_auth_algo osmo_auth_alg_parse(const char *name);
void osmo_auth_c3(uint8_t kc[], const uint8_t ck[], const uint8_t ik[]);
+void osmo_auth_c2(uint8_t sres[4], const uint8_t *res, size_t res_len, uint8_t sres_deriv_func);
/* @} */
diff --git a/include/osmocom/crypt/kdf.h b/include/osmocom/crypt/kdf.h
new file mode 100644
index 00000000..4a3b3b2e
--- /dev/null
+++ b/include/osmocom/crypt/kdf.h
@@ -0,0 +1,21 @@
+#pragma once
+
+/*! \defgroup kdf key derivation functions
+ * @{
+ * \file kdf.h */
+
+#include <stdint.h>
+
+void osmo_kdf_kc128(const uint8_t* ck, const uint8_t* ik, uint8_t* kc128);
+
+void osmo_kdf_kasme(const uint8_t *ck, const uint8_t *ik, const uint8_t* plmn_id,
+ const uint8_t *sqn, const uint8_t *ak, uint8_t *kasme);
+
+void osmo_kdf_enb(const uint8_t *kasme, uint32_t ul_count, uint8_t *kenb);
+
+void osmo_kdf_nh(const uint8_t *kasme, const uint8_t *sync_input, uint8_t *nh);
+
+void osmo_kdf_nas(uint8_t algo_type, uint8_t algo_id, const uint8_t *kasme, uint8_t *knas);
+
+
+/* @} */
diff --git a/include/osmocom/crypt/utran_cipher.h b/include/osmocom/crypt/utran_cipher.h
new file mode 100644
index 00000000..7dab3bc9
--- /dev/null
+++ b/include/osmocom/crypt/utran_cipher.h
@@ -0,0 +1,19 @@
+/*! \file utran_cipher.h */
+
+#pragma once
+
+/* 3GPP TS 25.413 § 9.2.1.11 */
+enum osmo_utran_integrity_algo {
+ OSMO_UTRAN_UIA1 = 0,
+ OSMO_UTRAN_UIA2 = 1,
+ _OSMO_UTRAN_UIA_NUM
+};
+
+/* 3GPP TS 25.413 § 9.2.1.12 */
+enum osmo_utran_encryption_algo {
+ OSMO_UTRAN_UEA0 = 0,
+ OSMO_UTRAN_UEA1 = 1,
+ OSMO_UTRAN_UEA2 = 2,
+ _OSMO_UTRAN_UEA_NUM
+};
+
diff --git a/include/osmocom/ctrl/Makefile.am b/include/osmocom/ctrl/Makefile.am
new file mode 100644
index 00000000..416e3ab4
--- /dev/null
+++ b/include/osmocom/ctrl/Makefile.am
@@ -0,0 +1,13 @@
+osmoctrl_HEADERS = \
+ control_cmd.h \
+ control_if.h \
+ ports.h \
+ $(NULL)
+
+if ENABLE_VTY
+osmoctrl_HEADERS += \
+ control_vty.h \
+ $(NULL)
+endif
+
+osmoctrldir = $(includedir)/osmocom/ctrl
diff --git a/include/osmocom/ctrl/control_cmd.h b/include/osmocom/ctrl/control_cmd.h
index 276a7def..e4f78fa2 100644
--- a/include/osmocom/ctrl/control_cmd.h
+++ b/include/osmocom/ctrl/control_cmd.h
@@ -24,6 +24,7 @@ enum ctrl_node_type {
CTRL_NODE_TS, /* TS specific (net.btsN.trxM.tsI.) */
CTRL_NODE_FSM, /* Finite State Machine (description) */
CTRL_NODE_FSM_INST, /* Finite State Machine (instance) */
+ CTRL_NODE_LCHAN, /* LCHAN specific (net.btsN.trxM.tsI.lchanL) */
_LAST_CTRL_NODE
};
@@ -123,6 +124,7 @@ int ctrl_cmd_def_send(struct ctrl_cmd_def *cd);
int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data);
int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd);
+/* ctrl_cmd_send is also declared in control_if.h, but legacy openbsc.git expects it here */
int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd);
int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd);
struct ctrl_cmd *ctrl_cmd_parse3(void *ctx, struct msgb *msg, bool *parse_failed);
@@ -234,9 +236,7 @@ static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \
* \param[in] cmdname symbol name of the command related function
* \param[in] cmdstr string name exposed on CTRL
* \param[in] dtype name of outer struct of user data
- * \param[in] element name of field within \a dtype
- * \param[in] min minimum permitted integer value
- * \param[in] max maximum permitted integer value */
+ * \param[in] element name of field within \a dtype */
#define CTRL_CMD_DEFINE_STRING(cmdname, cmdstr, dtype, element) \
CTRL_HELPER_GET_STRING(cmdname, dtype, element) \
CTRL_HELPER_SET_STRING(cmdname, dtype, element) \
diff --git a/include/osmocom/ctrl/control_if.h b/include/osmocom/ctrl/control_if.h
index b73296f6..e262d9e2 100644
--- a/include/osmocom/ctrl/control_if.h
+++ b/include/osmocom/ctrl/control_if.h
@@ -28,7 +28,8 @@ struct ctrl_handle {
};
-int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd);
+int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd) OSMO_DEPRECATED("Use ctrl_cmd_send2() instead.");
+int ctrl_cmd_send2(struct ctrl_connection *ccon, struct ctrl_cmd *cmd);
int ctrl_cmd_send_trap(struct ctrl_handle *ctrl, const char *name, char *value);
struct ctrl_handle *ctrl_handle_alloc(void *ctx, void *data, ctrl_cmd_lookup lookup);
struct ctrl_handle *ctrl_handle_alloc2(void *ctx, void *data,
@@ -36,19 +37,20 @@ struct ctrl_handle *ctrl_handle_alloc2(void *ctx, void *data,
unsigned int node_count);
struct ctrl_handle *ctrl_interface_setup(void *data, uint16_t port,
ctrl_cmd_lookup lookup);
+struct ctrl_handle *ctrl_interface_setup2(void *data, uint16_t default_port, ctrl_cmd_lookup lookup,
+ unsigned int node_count);
struct ctrl_handle *ctrl_interface_setup_dynip(void *data,
const char *bind_addr,
uint16_t port,
- ctrl_cmd_lookup lookup);
+ ctrl_cmd_lookup lookup) OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE;
struct ctrl_handle *ctrl_interface_setup_dynip2(void *data,
const char *bind_addr,
uint16_t port,
ctrl_cmd_lookup lookup,
- unsigned int node_count);
+ unsigned int node_count) OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE;
struct ctrl_connection *osmo_ctrl_conn_alloc(void *ctx, void *data);
int ctrl_cmd_handle(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, void *data);
struct ctrl_cmd *ctrl_cmd_exec_from_string(struct ctrl_handle *ch, const char *cmdstr);
int ctrl_lookup_register(ctrl_cmd_lookup lookup);
-
int ctrl_handle_msg(struct ctrl_handle *ctrl, struct ctrl_connection *ccon, struct msgb *msg);
diff --git a/include/osmocom/ctrl/control_vty.h b/include/osmocom/ctrl/control_vty.h
index af9ee991..a6cd012e 100644
--- a/include/osmocom/ctrl/control_vty.h
+++ b/include/osmocom/ctrl/control_vty.h
@@ -2,6 +2,8 @@
#pragma once
+#include <stdint.h>
+
/* Add the 'ctrl' section to VTY, containing the 'bind' command. */
int ctrl_vty_init(void *ctx);
@@ -9,3 +11,6 @@ int ctrl_vty_init(void *ctx);
* This should be fed to ctrl_interface_setup() once the configuration has been
* read. */
const char *ctrl_vty_get_bind_addr(void);
+
+/* Returns configured port passed to the 'line ctrl'/'bind' command or default_port. */
+uint16_t ctrl_vty_get_bind_port(uint16_t default_port);
diff --git a/include/osmocom/ctrl/ports.h b/include/osmocom/ctrl/ports.h
index 91206dcc..b4bceef3 100644
--- a/include/osmocom/ctrl/ports.h
+++ b/include/osmocom/ctrl/ports.h
@@ -28,4 +28,10 @@
/* 4266 used by D-GSM mDNS */
#define OSMO_CTRL_PORT_MGW 4267
#define OSMO_CTRL_PORT_SMLC 4272
+/* 4273 used by VTY interface */
+#define OSMO_CTRL_PORT_HNODEB 4274
+/* 4275: OSMO_VTY_PORT_UPF */
+#define OSMO_CTRL_PORT_UPF 4276
+/* 4277: OSMO_VTY_PORT_PFCP_TOOL */
+#define OSMO_CTRL_PORT_PFCP_TOOL 4278
/* When adding/changing port numbers, keep docs and wiki in sync. See above. */
diff --git a/include/osmocom/gprs/Makefile.am b/include/osmocom/gprs/Makefile.am
new file mode 100644
index 00000000..289be578
--- /dev/null
+++ b/include/osmocom/gprs/Makefile.am
@@ -0,0 +1,17 @@
+SUBDIRS = protocol
+
+osmogprs_HEADERS = \
+ frame_relay.h \
+ bssgp_bvc_fsm.h \
+ gprs_bssgp.h \
+ gprs_bssgp2.h \
+ gprs_bssgp_bss.h \
+ gprs_bssgp_rim.h \
+ gprs_msgb.h \
+ gprs_ns.h \
+ gprs_ns_frgre.h \
+ gprs_ns2.h \
+ gprs_rlc.h \
+ $(NULL)
+
+osmogprsdir = $(includedir)/osmocom/gprs
diff --git a/include/osmocom/gprs/bssgp_bvc_fsm.h b/include/osmocom/gprs/bssgp_bvc_fsm.h
index 446dda84..9d3a6205 100644
--- a/include/osmocom/gprs/bssgp_bvc_fsm.h
+++ b/include/osmocom/gprs/bssgp_bvc_fsm.h
@@ -40,6 +40,8 @@ struct bssgp_bvc_fsm_ops {
void (*reset_notification)(uint16_t nsei, uint16_t bvci, const struct gprs_ra_id *ra_id,
uint16_t cell_id, uint8_t cause, void *priv);
void (*rx_fc_bvc)(uint16_t nsei, uint16_t bvci, const struct bssgp2_flow_ctrl *fc, void *priv);
+ void (*reset_ack_notification)(uint16_t nsei, uint16_t bvci, const struct gprs_ra_id *ra_id,
+ uint16_t cell_id, uint8_t cause, void *priv);
};
struct osmo_fsm_inst *
diff --git a/include/osmocom/gprs/gprs_bssgp.h b/include/osmocom/gprs/gprs_bssgp.h
index 09f640dc..6c043327 100644
--- a/include/osmocom/gprs/gprs_bssgp.h
+++ b/include/osmocom/gprs/gprs_bssgp.h
@@ -235,7 +235,7 @@ 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_flush_all_queues(void);
void bssgp_fc_flush_queue(struct bssgp_flow_control *fc);
/* gprs_bssgp_vty.c */
diff --git a/include/osmocom/gprs/gprs_bssgp2.h b/include/osmocom/gprs/gprs_bssgp2.h
index 3b5b6e82..53e76e3a 100644
--- a/include/osmocom/gprs/gprs_bssgp2.h
+++ b/include/osmocom/gprs/gprs_bssgp2.h
@@ -59,6 +59,8 @@ struct msgb *bssgp2_enc_bvc_reset(uint16_t bvci, enum gprs_bssgp_cause cause,
struct msgb *bssgp2_enc_bvc_reset_ack(uint16_t bvci, const struct gprs_ra_id *ra_id, uint16_t cell_id,
const uint8_t *feat_bm, const uint8_t *ext_feat_bm);
+struct msgb *bssgp2_enc_flush_ll(uint32_t tlli, uint16_t old_bvci,
+ const uint16_t *new_bvci, const uint16_t *nsei);
struct msgb *bssgp2_enc_status(uint8_t cause, const uint16_t *bvci, const struct msgb *orig_msg, uint16_t max_pdu_len);
diff --git a/include/osmocom/gprs/gprs_bssgp_rim.h b/include/osmocom/gprs/gprs_bssgp_rim.h
index 5f397c98..10ea58bd 100644
--- a/include/osmocom/gprs/gprs_bssgp_rim.h
+++ b/include/osmocom/gprs/gprs_bssgp_rim.h
@@ -69,6 +69,7 @@ struct bssgp_rim_routing_info {
char *bssgp_rim_ri_name_buf(char *buf, size_t buf_len, const struct bssgp_rim_routing_info *ri);
const char *bssgp_rim_ri_name(const struct bssgp_rim_routing_info *ri);
int bssgp_parse_rim_ri(struct bssgp_rim_routing_info *ri, const uint8_t *buf, unsigned int len);
+int bssgp_parse_rim_ra(struct bssgp_rim_routing_info *ri, const uint8_t *buf, unsigned int len, uint8_t discr);
int bssgp_create_rim_ri(uint8_t *buf, const struct bssgp_rim_routing_info *ri);
/* 3GPP TS 48.018, table 11.3.63.1.1: RAN-INFORMATION-REQUEST Application Container coding for NACC */
@@ -270,3 +271,4 @@ int bssgp_parse_rim_pdu(struct bssgp_ran_information_pdu *pdu, const struct msgb
struct msgb *bssgp_encode_rim_pdu(const struct bssgp_ran_information_pdu *pdu);
int bssgp_tx_rim(const struct bssgp_ran_information_pdu *pdu, uint16_t nsei);
+int bssgp_tx_rim_encoded(struct msgb *msg, uint16_t nsei);
diff --git a/include/osmocom/gprs/gprs_ns2.h b/include/osmocom/gprs/gprs_ns2.h
index a9c144cc..7c7e2211 100644
--- a/include/osmocom/gprs/gprs_ns2.h
+++ b/include/osmocom/gprs/gprs_ns2.h
@@ -249,7 +249,7 @@ int gprs_ns2_ip_bind_set_dscp(struct gprs_ns2_vc_bind *bind, int dscp);
int gprs_ns2_ip_bind_set_priority(struct gprs_ns2_vc_bind *bind, uint8_t priority);
struct gprs_ns2_vc *gprs_ns2_nsvc_by_sockaddr_bind(
struct gprs_ns2_vc_bind *bind,
- const struct osmo_sockaddr *saddr);
+ const struct osmo_sockaddr *rem_addr);
int gprs_ns2_frgre_bind(struct gprs_ns2_inst *nsi,
const char *name,
diff --git a/include/osmocom/gprs/gprs_rlc.h b/include/osmocom/gprs/gprs_rlc.h
index b74f9e40..060424ae 100644
--- a/include/osmocom/gprs/gprs_rlc.h
+++ b/include/osmocom/gprs/gprs_rlc.h
@@ -1,54 +1,4 @@
-/*! \file gprs_rlc.h */
-
#pragma once
-#include <stdint.h>
-
-/*! Structure for CPS coding and puncturing scheme (TS 04.60 10.4.8a) */
-struct egprs_cps {
- uint8_t bits;
- uint8_t mcs;
- uint8_t p[2];
-};
-
-/*! CPS puncturing table selection (TS 04.60 10.4.8a) */
-enum egprs_cps_punc {
- EGPRS_CPS_P1,
- EGPRS_CPS_P2,
- EGPRS_CPS_P3,
- EGPRS_CPS_NONE = -1,
-};
-
-/*! EGPRS header types (TS 04.60 10.0a.2) */
-enum egprs_hdr_type {
- EGPRS_HDR_TYPE1,
- EGPRS_HDR_TYPE2,
- EGPRS_HDR_TYPE3,
-};
-
-enum osmo_gprs_cs {
- OSMO_GPRS_CS_NONE,
- OSMO_GPRS_CS1,
- OSMO_GPRS_CS2,
- OSMO_GPRS_CS3,
- OSMO_GPRS_CS4,
- OSMO_GPRS_MCS1,
- OSMO_GPRS_MCS2,
- OSMO_GPRS_MCS3,
- OSMO_GPRS_MCS4,
- OSMO_GPRS_MCS5,
- OSMO_GPRS_MCS6,
- OSMO_GPRS_MCS7,
- OSMO_GPRS_MCS8,
- OSMO_GPRS_MCS9,
- _NUM_OSMO_GPRS_CS
-};
-
-int egprs_get_cps(struct egprs_cps *cps, uint8_t type, uint8_t bits);
-
-int osmo_gprs_ul_block_size_bits(enum osmo_gprs_cs cs);
-int osmo_gprs_dl_block_size_bits(enum osmo_gprs_cs cs);
-int osmo_gprs_ul_block_size_bytes(enum osmo_gprs_cs cs);
-int osmo_gprs_dl_block_size_bytes(enum osmo_gprs_cs cs);
-enum osmo_gprs_cs osmo_gprs_ul_cs_by_block_bytes(uint8_t block_size);
-enum osmo_gprs_cs osmo_gprs_dl_cs_by_block_bytes(uint8_t block_size);
+#pragma message "Header osmocom/gprs/gprs_rlc.h is deprecated, include osmocom/gsm/protocol/gsm_44_060.h instead"
+#include <osmocom/gsm/protocol/gsm_44_060.h>
diff --git a/include/osmocom/gprs/protocol/Makefile.am b/include/osmocom/gprs/protocol/Makefile.am
new file mode 100644
index 00000000..e69527d0
--- /dev/null
+++ b/include/osmocom/gprs/protocol/Makefile.am
@@ -0,0 +1,8 @@
+osmogprsproto_HEADERS = \
+ gsm_04_60.h \
+ gsm_08_16.h \
+ gsm_08_18.h \
+ gsm_24_301.h \
+ $(NULL)
+
+osmogprsprotodir = $(includedir)/osmocom/gprs/protocol
diff --git a/include/osmocom/gprs/protocol/gsm_04_60.h b/include/osmocom/gprs/protocol/gsm_04_60.h
index ed63fe00..c2c11a7d 100644
--- a/include/osmocom/gprs/protocol/gsm_04_60.h
+++ b/include/osmocom/gprs/protocol/gsm_04_60.h
@@ -1,201 +1,4 @@
-/*! \file gsm_04_60.h
- * General Packet Radio Service (GPRS).
- * Radio Link Control / Medium Access Control (RLC/MAC) protocol
- * 3GPP TS 04.60 version 8.27.0 Release 1999
- */
-
#pragma once
-#include <stdint.h>
-#include <osmocom/core/endian.h>
-
-/* TS 04.60 10.3a.4.1.1 */
-struct gprs_rlc_ul_header_egprs_1 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t r:1,
- si:1,
- cv:4,
- tfi_hi:2;
- uint8_t tfi_lo:3,
- bsn1_hi:5;
- uint8_t bsn1_lo:6,
- bsn2_hi:2;
- uint8_t bsn2_lo:8;
- uint8_t cps:5,
- rsb:1,
- pi:1,
- spare_hi:1;
- uint8_t spare_lo:6,
- dummy:2;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t tfi_hi:2, cv:4, si:1, r:1;
- uint8_t bsn1_hi:5, tfi_lo:3;
- uint8_t bsn2_hi:2, bsn1_lo:6;
- uint8_t bsn2_lo:8;
- uint8_t spare_hi:1, pi:1, rsb:1, cps:5;
- uint8_t dummy:2, spare_lo:6;
-#endif
-} __attribute__ ((packed));
-
-/* TS 04.60 10.3a.4.2.1 */
-struct gprs_rlc_ul_header_egprs_2 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t r:1,
- si:1,
- cv:4,
- tfi_hi:2;
- uint8_t tfi_lo:3,
- bsn1_hi:5;
- uint8_t bsn1_lo:6,
- cps_hi:2;
- uint8_t cps_lo:1,
- rsb:1,
- pi:1,
- spare_hi:5;
- uint8_t spare_lo:5,
- dummy:3;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t tfi_hi:2, cv:4, si:1, r:1;
- uint8_t bsn1_hi:5, tfi_lo:3;
- uint8_t cps_hi:2, bsn1_lo:6;
- uint8_t spare_hi:5, pi:1, rsb:1, cps_lo:1;
- uint8_t dummy:3, spare_lo:5;
-#endif
-} __attribute__ ((packed));
-
-/* TS 04.60 10.3a.4.3.1 */
-struct gprs_rlc_ul_header_egprs_3 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t r:1,
- si:1,
- cv:4,
- tfi_hi:2;
- uint8_t tfi_lo:3,
- bsn1_hi:5;
- uint8_t bsn1_lo:6,
- cps_hi:2;
- uint8_t cps_lo:2,
- spb:2,
- rsb:1,
- pi:1,
- spare:1,
- dummy:1;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t tfi_hi:2, cv:4, si:1, r:1;
- uint8_t bsn1_hi:5, tfi_lo:3;
- uint8_t cps_hi:2, bsn1_lo:6;
- uint8_t dummy:1, spare:1, pi:1, rsb:1, spb:2, cps_lo:2;
-#endif
-} __attribute__ ((packed));
-
-struct gprs_rlc_dl_header_egprs_1 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t usf:3,
- es_p:2,
- rrbp:2,
- tfi_hi:1;
- uint8_t tfi_lo:4,
- pr:2,
- bsn1_hi:2;
- uint8_t bsn1_mid:8;
- uint8_t bsn1_lo:1,
- bsn2_hi:7;
- uint8_t bsn2_lo:3,
- cps:5;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
- uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
- uint8_t bsn1_mid:8;
- uint8_t bsn2_hi:7, bsn1_lo:1;
- uint8_t cps:5, bsn2_lo:3;
-#endif
-} __attribute__ ((packed));
-
-struct gprs_rlc_dl_header_egprs_2 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t usf:3,
- es_p:2,
- rrbp:2,
- tfi_hi:1;
- uint8_t tfi_lo:4,
- pr:2,
- bsn1_hi:2;
- uint8_t bsn1_mid:8;
- uint8_t bsn1_lo:1,
- cps:3,
- dummy:4;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
- uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
- uint8_t bsn1_mid:8;
- uint8_t dummy:4, cps:3, bsn1_lo:1;
-#endif
-} __attribute__ ((packed));
-
-struct gprs_rlc_dl_header_egprs_3 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t usf:3,
- es_p:2,
- rrbp:2,
- tfi_hi:1;
- uint8_t tfi_lo:4,
- pr:2,
- bsn1_hi:2;
- uint8_t bsn1_mid:8;
- uint8_t bsn1_lo:1,
- cps:4,
- spb:2,
- dummy:1;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
- uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
- uint8_t bsn1_mid:8;
- uint8_t dummy:1, spb:2, cps:4, bsn1_lo:1;
-#endif
-} __attribute__ ((packed));
-
-/* TS 03.60 Chapter 6.3.3.1: Network Mode of Operation */
-enum osmo_gprs_nmo {
- GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */
- GPRS_NMO_II = 1, /* all paging on CCCH */
- GPRS_NMO_III = 2, /* no paging coordination */
-};
-
-/* TS 44.060 12.24 */
-struct osmo_gprs_cell_options {
- enum osmo_gprs_nmo nmo;
- /* T3168: wait for packet uplink assignment message */
- uint32_t t3168; /* in milliseconds */
- /* T3192: wait for release of the TBF after reception of the final block */
- uint32_t t3192; /* in milliseconds */
- uint32_t drx_timer_max;/* in seconds */
- uint32_t bs_cv_max;
- uint8_t supports_egprs_11bit_rach;
- bool ctrl_ack_type_use_block; /* use PACKET CONTROL ACKNOWLEDGMENT */
-
- uint8_t ext_info_present;
- struct {
- uint8_t egprs_supported;
- uint8_t use_egprs_p_ch_req;
- uint8_t bep_period;
- uint8_t pfc_supported;
- uint8_t dtm_supported;
- uint8_t bss_paging_coordination;
- bool ccn_active;
- } ext_info;
-};
-
-/* TS 04.60 Table 12.9.2 */
-struct osmo_gprs_power_ctrl_pars {
- uint8_t alpha;
- uint8_t t_avg_w;
- uint8_t t_avg_t;
- uint8_t pc_meas_chan;
- uint8_t n_avg_i;
-};
+#pragma message "Header osmocom/gprs/protocol/gsm_04_60.h is deprecated, include osmocom/gsm/protocol/gsm_44_060.h instead"
+#include <osmocom/gsm/protocol/gsm_44_060.h>
diff --git a/include/osmocom/gprs/protocol/gsm_08_18.h b/include/osmocom/gprs/protocol/gsm_08_18.h
index 0d002b57..1152eb6c 100644
--- a/include/osmocom/gprs/protocol/gsm_08_18.h
+++ b/include/osmocom/gprs/protocol/gsm_08_18.h
@@ -362,13 +362,12 @@ enum bssgp_rim_pdu_type {
/* RIM PDU Indications
* 3GPP TS 48.018, section 11.3.65.0 */
struct bssgp_rim_pdu_ind {
-#if OSMO_IS_BIG_ENDIAN
- uint8_t reserved:4,
- pdu_type_ext:3,
- ack_requested:1;
-#elif OSMO_IS_LITTLE_ENDIAN
+#if OSMO_IS_LITTLE_ENDIAN
uint8_t ack_requested:1,
pdu_type_ext:3,
reserved:4;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t reserved:4, pdu_type_ext:3, ack_requested:1;
#endif
} __attribute__ ((packed));
diff --git a/include/osmocom/gsm/Makefile.am b/include/osmocom/gsm/Makefile.am
new file mode 100644
index 00000000..5678a51e
--- /dev/null
+++ b/include/osmocom/gsm/Makefile.am
@@ -0,0 +1,69 @@
+SUBDIRS = protocol
+
+BUILT_SOURCES = gsm0503.h
+
+osmogsm_HEADERS = \
+ a5.h \
+ abis_nm.h \
+ apn.h \
+ bts_features.h \
+ cbsp.h \
+ comp128.h \
+ comp128v23.h \
+ bitvec_gsm.h \
+ gan.h \
+ gsm0341.h \
+ gsm0411_smc.h \
+ gsm0411_smr.h \
+ gsm0411_utils.h \
+ gsm0480.h \
+ gsm0502.h \
+ gsm0503.h \
+ bsslap.h \
+ bssmap_le.h \
+ gad.h \
+ gsm0808.h \
+ gsm0808_lcs.h \
+ gsm29205.h \
+ gsm0808_utils.h \
+ gsm23003.h \
+ gsm23236.h \
+ gsm29118.h \
+ gsm44021.h \
+ gsm48.h \
+ gsm48_arfcn_range_encode.h \
+ gsm48_ie.h \
+ gsm48_rest_octets.h \
+ gsm_utils.h \
+ gsup.h \
+ gsup_sms.h \
+ i460_mux.h \
+ ipa.h \
+ iuup.h \
+ lapd_core.h \
+ lapdm.h \
+ meas_rep.h \
+ mncc.h \
+ prim.h \
+ l1sap.h \
+ oap.h \
+ oap_client.h \
+ rlp.h \
+ rsl.h \
+ rxlev_stat.h \
+ sysinfo.h \
+ tlv.h \
+ $(NULL)
+
+osmogsmdir = $(includedir)/osmocom/gsm
+
+noinst_HEADERS = \
+ kasumi.h \
+ gea.h \
+ $(NULL)
+
+gsm0503.h: $(top_srcdir)/utils/conv_gen.py $(top_srcdir)/utils/conv_codes_gsm.py
+ $(AM_V_GEN)python3 $(top_srcdir)/utils/conv_gen.py gen_header gsm \
+ --target-path $(builddir)/
+
+CLEANFILES = gsm0503.h
diff --git a/include/osmocom/gsm/a5.h b/include/osmocom/gsm/a5.h
index fa63246d..1b26842f 100644
--- a/include/osmocom/gsm/a5.h
+++ b/include/osmocom/gsm/a5.h
@@ -12,10 +12,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
diff --git a/include/osmocom/gsm/bsslap.h b/include/osmocom/gsm/bsslap.h
index b2174b3a..56f2e6c8 100644
--- a/include/osmocom/gsm/bsslap.h
+++ b/include/osmocom/gsm/bsslap.h
@@ -21,10 +21,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
diff --git a/include/osmocom/gsm/bssmap_le.h b/include/osmocom/gsm/bssmap_le.h
index 1c750c8a..113d4bd1 100644
--- a/include/osmocom/gsm/bssmap_le.h
+++ b/include/osmocom/gsm/bssmap_le.h
@@ -21,10 +21,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
diff --git a/include/osmocom/gsm/bts_features.h b/include/osmocom/gsm/bts_features.h
index 93d35f3a..cf1db4ac 100644
--- a/include/osmocom/gsm/bts_features.h
+++ b/include/osmocom/gsm/bts_features.h
@@ -7,7 +7,7 @@
/* N. B: always add new features to the end of the list (right before _NUM_BTS_FEAT) to avoid breaking compatibility
with BTS compiled against earlier version of this header. Also make sure that the description strings
- osmo_bts_features_descs[] in gsm_data.c are also updated accordingly! */
+ osmo_bts_features_{descs,names}[] in bts_features.c are also updated accordingly! */
enum osmo_bts_features {
BTS_FEAT_HSCSD,
BTS_FEAT_GPRS,
@@ -29,6 +29,13 @@ enum osmo_bts_features {
BTS_FEAT_ACCH_REP,
BTS_FEAT_CCN, /* Is CCN supported by the cell? TS 44.060 sec 8.8.2 */
BTS_FEAT_VAMOS, /* Is the BTS VAMOS capable? */
+ BTS_FEAT_ABIS_OSMO_PCU, /* BTS supports forwarding data to PCUIF over IPA OML multiplex */
+ BTS_FEAT_BCCH_POWER_RED,
+ BTS_FEAT_DYN_TS_SDCCH8, /* Osmo Dynamic TS supports configured as SDCCH8 */
+ BTS_FEAT_ACCH_TEMP_OVP, /* FACCH/SACCH Temporary overpower */
+ BTS_FEAT_OSMUX, /* Osmux (Osmocom RTP muxing) support */
+ BTS_FEAT_VBS, /* Voice Broadcast Service support, 3GPP TS 43.069 */
+ BTS_FEAT_VGCS, /* Voice Group Call Service support, 3GPP TS 44.068 */
_NUM_BTS_FEAT
};
diff --git a/include/osmocom/gsm/cbsp.h b/include/osmocom/gsm/cbsp.h
index d456ce2f..efa4ce6f 100644
--- a/include/osmocom/gsm/cbsp.h
+++ b/include/osmocom/gsm/cbsp.h
@@ -24,10 +24,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Decoded 8.2.3 Message Content */
@@ -75,7 +71,7 @@ struct osmo_cbsp_fail_ent {
struct llist_head list; /* entry in a fail_list below */
enum CELL_IDENT id_discr;
union gsm0808_cell_id_u cell_id;
- uint8_t cause;
+ uint8_t cause; /* enum osmo_cbsp_cause */
};
@@ -245,6 +241,30 @@ struct osmo_cbsp_error_ind {
enum cbsp_channel_ind *channel_ind;
};
+/* 8.2.13 Cause */
+enum osmo_cbsp_cause {
+ OSMO_CBSP_CAUSE_PARAM_NOT_RECOGNISED = 0,
+ OSMO_CBSP_CAUSE_PARAM_VALUE_INVALID,
+ OSMO_CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED,
+ OSMO_CBSP_CAUSE_CELL_ID_NOT_VALID,
+ OSMO_CBSP_CAUSE_UNRECOGNISED_MESSAGE,
+ OSMO_CBSP_CAUSE_MISSING_MANDATORY_ELEMENT,
+ OSMO_CBSP_CAUSE_BSC_CAPACITY_EXCEEDED,
+ OSMO_CBSP_CAUSE_CELL_MEMORY_EXCEEDED,
+ OSMO_CBSP_CAUSE_BSC_MEMORY_EXCEEDED,
+ OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_SUPPORTED,
+ OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_OPERATIONAL,
+ OSMO_CBSP_CAUSE_INCOMPATIBLE_DRX_PARAM,
+ OSMO_CBSP_CAUSE_EXT_CHAN_NOT_SUPPORTED,
+ OSMO_CBSP_CAUSE_MSG_REF_ALREADY_USED,
+ OSMO_CBSP_CAUSE_UNSPECIFIED_ERROR,
+ OSMO_CBSP_CAUSE_LAI_OR_LAC_NOT_VALID,
+};
+extern const struct value_string osmo_cbsp_cause_names[];
+static inline const char *osmo_cbsp_cause_name(enum osmo_cbsp_cause cause)
+{
+ return get_value_string(osmo_cbsp_cause_names, cause);
+}
/* decoded CBSP message */
struct osmo_cbsp_decoded {
@@ -283,7 +303,7 @@ struct osmo_cbsp_decoded {
} u;
};
-extern const __thread char *osmo_cbsp_errstr;
+extern __thread const char *osmo_cbsp_errstr;
struct msgb *osmo_cbsp_msgb_alloc(void *ctx, const char *name);
struct msgb *osmo_cbsp_encode(void *ctx, const struct osmo_cbsp_decoded *in);
@@ -292,3 +312,4 @@ void osmo_cbsp_init_struct(struct osmo_cbsp_decoded *cbsp, enum cbsp_msg_type ms
struct osmo_cbsp_decoded *osmo_cbsp_decoded_alloc(void *ctx, enum cbsp_msg_type msg_type);
int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb **tmp_msg);
+int osmo_cbsp_segmentation_cb(struct msgb *msg);
diff --git a/include/osmocom/gsm/gsm0502.h b/include/osmocom/gsm/gsm0502.h
index 1be2cc39..0ac13873 100644
--- a/include/osmocom/gsm/gsm0502.h
+++ b/include/osmocom/gsm/gsm0502.h
@@ -1,4 +1,7 @@
-/*! \file gsm0502.h */
+/*! \defgroup gsm0502 GSM 05.02 / 3GPP TS 45.002
+ * @{
+ * \file gsm0502.h
+ */
#pragma once
@@ -16,10 +19,10 @@
/*! Return the sum of two specified TDMA frame numbers (summation) */
#define GSM_TDMA_FN_SUM(a, b) \
- ((a + b) % GSM_TDMA_HYPERFRAME)
+ (((a) + (b)) % GSM_TDMA_HYPERFRAME)
/*! Return the difference of two specified TDMA frame numbers (subtraction) */
#define GSM_TDMA_FN_SUB(a, b) \
- ((a + GSM_TDMA_HYPERFRAME - b) % GSM_TDMA_HYPERFRAME)
+ (((a) + GSM_TDMA_HYPERFRAME - (b)) % GSM_TDMA_HYPERFRAME)
/*! Return the *minimum* difference of two specified TDMA frame numbers (distance) */
#define GSM_TDMA_FN_DIFF(a, b) \
OSMO_MIN(GSM_TDMA_FN_SUB(a, b), GSM_TDMA_FN_SUB(b, a))
@@ -31,9 +34,58 @@
#define GSM_TDMA_FN_DEC(fn) \
((fn) = GSM_TDMA_FN_SUB((fn), 1))
+/* 5.2.3.1 Normal burst for GMSK (1 bit per symbol) */
+#define GSM_NBITS_NB_GMSK_TAIL 3
+#define GSM_NBITS_NB_GMSK_PAYLOAD (2 * 58)
+#define GSM_NBITS_NB_GMSK_TRAIN_SEQ 26
+#define GSM_NBITS_NB_GMSK_BURST 148 /* without guard period */
+
+/* 5.2.3.3 Normal burst for 8-PSK (3 bits per symbol) */
+#define GSM_NBITS_NB_8PSK_TAIL (GSM_NBITS_NB_GMSK_TAIL * 3)
+#define GSM_NBITS_NB_8PSK_PAYLOAD (GSM_NBITS_NB_GMSK_PAYLOAD * 3)
+#define GSM_NBITS_NB_8PSK_TRAIN_SEQ (GSM_NBITS_NB_GMSK_TRAIN_SEQ * 3)
+#define GSM_NBITS_NB_8PSK_BURST (GSM_NBITS_NB_GMSK_BURST * 3)
+
+/* 5.2.5 Synchronization burst (also GMSK) */
+#define GSM_NBITS_SB_GMSK_TAIL GSM_NBITS_NB_GMSK_TAIL
+#define GSM_NBITS_SB_GMSK_PAYLOAD (2 * 39)
+#define GSM_NBITS_SB_GMSK_ETRAIN_SEQ 64
+#define GSM_NBITS_SB_GMSK_BURST GSM_NBITS_NB_GMSK_BURST
+
+/* 5.2.6 Dummy burst (also GMSK) */
+#define GSM_NBITS_DB_GMSK_TAIL GSM_NBITS_NB_GMSK_TAIL
+#define GSM_NBITS_DB_GMSK_MIXED 142
+#define GSM_NBITS_DB_GMSK_BURST GSM_NBITS_NB_GMSK_BURST
+
+/* 5.2.7 Access burst (also GMSK) */
+#define GSM_NBITS_AB_GMSK_ETAIL 8
+#define GSM_NBITS_AB_GMSK_SYNCH_SEQ 41
+#define GSM_NBITS_AB_GMSK_PAYLOAD 36
+#define GSM_NBITS_AB_GMSK_TAIL GSM_NBITS_NB_GMSK_TAIL
+#define GSM_NBITS_AB_GMSK_BURST GSM_NBITS_NB_GMSK_BURST
+
+/*! Compare the given TDMA FNs, taking the wrapping into account.
+ * \param[in] fn1 First TDMA Fn value to compare.
+ * \param[in] fn2 Second TDMA Fn value to compare.
+ * \returns similarly to memcmp(), -1 if fn1 goes before fn2;
+ * 0 if fn1 equals fn2;
+ * 1 if fn1 goes after fn2. */
+static inline int gsm0502_fncmp(uint32_t fn1, uint32_t fn2)
+{
+ const uint32_t thresh = GSM_TDMA_HYPERFRAME / 2;
+
+ if (fn1 == fn2)
+ return 0;
+ if ((fn1 < fn2 && (fn2 - fn1) < thresh) ||
+ (fn1 > fn2 && (fn1 - fn2) > thresh))
+ return -1;
+
+ return 1;
+}
+
/* Table 5 Clause 7 TS 05.02 */
static inline unsigned int
-gsm0502_get_n_pag_blocks(struct gsm48_control_channel_descr *chan_desc)
+gsm0502_get_n_pag_blocks(const struct gsm48_control_channel_descr *chan_desc)
{
if (chan_desc->ccch_conf == RSL_BCCH_CCCH_CONF_1_C)
return 3 - chan_desc->bs_ag_blks_res;
@@ -58,7 +110,7 @@ gsm0502_get_paging_group(uint64_t imsi, unsigned int bs_cc_chans,
}
unsigned int
-gsm0502_calc_paging_group(struct gsm48_control_channel_descr *chan_desc, uint64_t imsi);
+gsm0502_calc_paging_group(const struct gsm48_control_channel_descr *chan_desc, uint64_t imsi);
enum gsm0502_fn_remap_channel {
FN_REMAP_TCH_F,
@@ -75,3 +127,7 @@ uint32_t gsm0502_fn_remap(uint32_t fn, enum gsm0502_fn_remap_channel channel);
uint16_t gsm0502_hop_seq_gen(const struct gsm_time *t,
uint8_t hsn, uint8_t maio,
size_t n, const uint16_t *ma);
+
+int gsm0502_fn2ccch_block(uint32_t fn);
+
+/*! @} */
diff --git a/include/osmocom/gsm/gsm0808.h b/include/osmocom/gsm/gsm0808.h
index dc3610fd..80ef6836 100644
--- a/include/osmocom/gsm/gsm0808.h
+++ b/include/osmocom/gsm/gsm0808.h
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -30,11 +26,11 @@
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/socket_compat.h>
+
#define BSSMAP_MSG_SIZE 1024
#define BSSMAP_MSG_HEADROOM 512
-struct sockaddr_storage;
-
struct msgb;
struct gsm0808_cell_id_list2;
@@ -54,10 +50,30 @@ struct msgb *gsm0808_create_clear_command2(uint8_t cause, bool csfb_ind);
struct msgb *gsm0808_create_clear_complete(void);
struct msgb *gsm0808_create_cipher(const struct gsm0808_encrypt_info *ei,
const uint8_t *cipher_response_mode);
+
+struct gsm0808_cipher_mode_command {
+ struct gsm0808_encrypt_info ei;
+
+ /*! 3GPP TS 48.008 3.2.2.34 Cipher Response Mode, optional IE */
+ bool cipher_response_mode_present;
+ /*! 3GPP TS 48.008 3.2.2.34 Cipher Response Mode:
+ * 0 - IMEISV must not be included by the Mobile Station;
+ * 1 - IMEISV must be included by the Mobile Station.
+ */
+ uint8_t cipher_response_mode;
+
+ bool kc128_present;
+ uint8_t kc128[16];
+
+ /* more items are defined in the spec and may be added later */
+ bool more_items; /*< always set this to false */
+};
+struct msgb *gsm0808_create_cipher2(const struct gsm0808_cipher_mode_command *cmc);
+
struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id);
struct msgb *gsm0808_create_cipher_reject(enum gsm0808_cause cause);
struct msgb *gsm0808_create_cipher_reject_ext(enum gsm0808_cause_class class, uint8_t ext);
-struct msgb *gsm0808_create_classmark_request();
+struct msgb *gsm0808_create_classmark_request(void);
struct msgb *gsm0808_create_classmark_update(const uint8_t *cm2, uint8_t cm2_len,
const uint8_t *cm3, uint8_t cm3_len);
struct msgb *gsm0808_create_sapi_reject_cause(uint8_t link_id, uint16_t cause);
@@ -198,7 +214,12 @@ struct gsm0808_handover_request {
uint8_t global_call_reference_len;
/* more items are defined in the spec and may be added later */
- bool more_items; /*!< always set this to false */
+ bool more_items; /*!< set this to true iff any fields below are used */
+
+ bool kc128_present;
+ uint8_t kc128[16];
+
+ bool more_items2; /*!< always set this to false */
};
struct msgb *gsm0808_create_handover_request(const struct gsm0808_handover_request *params);
@@ -225,8 +246,12 @@ struct gsm0808_handover_request_ack {
const struct sockaddr_storage *aoip_transport_layer;
+ bool more_items; /*!< set this to true iff any fields below are used */
+
+ struct gsm0808_speech_codec_list codec_list_bss_supported; /*< omit when .len == 0 */
+
/* more items are defined in the spec and may be added later */
- bool more_items; /*!< always set this to false */
+ bool more_items2; /*!< always set this to false */
};
struct msgb *gsm0808_create_handover_request_ack2(const struct gsm0808_handover_request_ack *params);
@@ -244,8 +269,8 @@ struct gsm0808_handover_command {
};
struct msgb *gsm0808_create_handover_command(const struct gsm0808_handover_command *params);
-struct msgb *gsm0808_create_handover_detect();
-struct msgb *gsm0808_create_handover_succeeded();
+struct msgb *gsm0808_create_handover_detect(void);
+struct msgb *gsm0808_create_handover_succeeded(void);
struct gsm0808_handover_complete {
bool rr_cause_present;
@@ -307,6 +332,243 @@ struct gsm0808_handover_performed {
};
struct msgb *gsm0808_create_handover_performed(const struct gsm0808_handover_performed *params);
+/*! 3GPP TS 48.008 §3.2.1.50 VGCS/VBS SETUP */
+struct gsm0808_vgcs_vbs_setup {
+ struct gsm0808_group_callref callref;
+
+ bool priority_present;
+ struct gsm0808_priority priority;
+
+ bool vgcs_feature_flags_present;
+ struct gsm0808_vgcs_feature_flags flags;
+};
+struct msgb *gsm0808_create_vgcs_vbs_setup(const struct gsm0808_vgcs_vbs_setup *params);
+
+/*! 3GPP TS 48.008 §3.2.1.51 VGCS/VBS SETUP ACK */
+struct gsm0808_vgcs_vbs_setup_ack {
+ bool vgcs_feature_flags_present;
+ struct gsm0808_vgcs_feature_flags flags;
+};
+struct msgb *gsm0808_create_vgcs_vbs_setup_ack(const struct gsm0808_vgcs_vbs_setup_ack *params);
+
+/*! 3GPP TS 48.008 §3.2.1.52 VGCS/VBS SETUP REFUSE */
+struct msgb *gsm0808_create_vgcs_vbs_setup_refuse(enum gsm0808_cause cause);
+
+/*! 3GPP TS 48.008 §3.2.1.53 VGCS/VBS ASSIGNMENT REQUEST */
+struct gsm0808_vgcs_vbs_assign_req {
+ struct gsm0808_channel_type channel_type;
+ enum gsm0808_assignment_requirement ass_req;
+ struct gsm0808_cell_id cell_identifier;
+ struct gsm0808_group_callref callref;
+
+ bool priority_present;
+ struct gsm0808_priority priority;
+
+ bool cic_present;
+ uint16_t cic;
+
+ bool downlink_dtx_flag_present;
+ enum gsm0808_downlink_dtx_flag downlink_dtx_flag;
+
+ bool encryption_information_present;
+ struct gsm0808_encrypt_info encryption_information;
+
+ bool vstk_rand_present;
+ uint8_t vstk_rand[5];
+
+ bool vstk_present;
+ uint8_t vstk[16];
+
+ bool cils_present;
+ struct gsm0808_cell_id_list_segment cils;
+
+ bool aoip_transport_layer_present;
+ struct sockaddr_storage aoip_transport_layer;
+
+ bool call_id_present;
+ uint32_t call_id;
+
+ bool codec_list_present;
+ struct gsm0808_speech_codec_list codec_list_msc_preferred;
+};
+struct msgb *gsm0808_create_vgcs_vbs_assign_req(const struct gsm0808_vgcs_vbs_assign_req *params);
+
+/*! 3GPP TS 48.008 §3.2.1.54 VGCS/VBS ASSIGNMENT RESULT */
+struct gsm0808_vgcs_vbs_assign_res {
+ struct gsm0808_channel_type channel_type;
+ struct gsm0808_cell_id cell_identifier;
+
+ bool chosen_channel_present;
+ uint8_t chosen_channel;
+
+ bool cic_present;
+ uint16_t cic;
+
+ bool circuit_pool_present;
+ uint8_t circuit_pool;
+
+ bool aoip_transport_layer_present;
+ struct sockaddr_storage aoip_transport_layer;
+
+ bool codec_present;
+ struct gsm0808_speech_codec codec_msc_chosen;
+
+ bool call_id_present;
+ uint32_t call_id;
+};
+struct msgb *gsm0808_create_vgcs_vbs_assign_res(const struct gsm0808_vgcs_vbs_assign_res *params);
+
+/*! 3GPP TS 48.008 §3.2.1.55 VGCS/VBS ASSIGNMENT FAILURE */
+struct gsm0808_vgcs_vbs_assign_fail {
+ enum gsm0808_cause cause;
+
+ bool circuit_pool_present;
+ uint8_t circuit_pool;
+
+ bool cpl_present;
+ struct gsm0808_circuit_pool_list cpl;
+
+ bool codec_list_present;
+ struct gsm0808_speech_codec_list codec_list_bss_supported;
+};
+struct msgb *gsm0808_create_vgcs_vbs_assign_fail(const struct gsm0808_vgcs_vbs_assign_fail *params);
+
+/*! 3GPP TS 48.008 §3.2.1.57 (VGCS) UPLINK REQUEST */
+struct gsm0808_uplink_request {
+ bool talker_priority_present;
+ enum gsm0808_talker_priority talker_priority;
+
+ bool cell_identifier_present;
+ struct gsm0808_cell_id cell_identifier;
+
+ bool l3_present;
+ struct gsm0808_layer_3_information l3;
+
+ bool mi_present;
+ struct osmo_mobile_identity mi;
+};
+struct msgb *gsm0808_create_uplink_request(const struct gsm0808_uplink_request *params);
+
+/*! 3GPP TS 48.008 §3.2.1.58 (VGCS) UPLINK REQUEST ACKNOWLEDGE */
+struct gsm0808_uplink_request_ack {
+ bool talker_priority_present;
+ enum gsm0808_talker_priority talker_priority;
+
+ bool emerg_set_ind_present;
+
+ bool talker_identity_present;
+ struct gsm0808_talker_identity talker_identity;
+};
+struct msgb *gsm0808_create_uplink_request_ack(const struct gsm0808_uplink_request_ack *params);
+
+/*! 3GPP TS 48.008 §3.2.1.59 (VGCS) UPLINK REQUEST CONFIRM */
+struct gsm0808_uplink_request_cnf {
+ struct gsm0808_cell_id cell_identifier;
+
+ bool talker_identity_present;
+ struct gsm0808_talker_identity talker_identity;
+
+ /* mandatory! */
+ struct gsm0808_layer_3_information l3;
+};
+struct msgb *gsm0808_create_uplink_request_cnf(const struct gsm0808_uplink_request_cnf *params);
+
+/*! 3GPP TS 48.008 §3.2.1.59a (VGCS) UPLINK APPLICATION DATA */
+struct gsm0808_uplink_app_data {
+ struct gsm0808_cell_id cell_identifier;
+ struct gsm0808_layer_3_information l3;
+ bool bt_ind;
+};
+struct msgb *gsm0808_create_uplink_app_data(const struct gsm0808_uplink_app_data *params);
+
+/*! 3GPP TS 48.008 §3.2.1.60 (VGCS) UPLINK RELEASE INDICATION */
+struct gsm0808_uplink_release_ind {
+ enum gsm0808_cause cause;
+
+ bool talker_priority_present;
+ enum gsm0808_talker_priority talker_priority;
+};
+struct msgb *gsm0808_create_uplink_release_ind(const struct gsm0808_uplink_release_ind *params);
+
+/*! 3GPP TS 48.008 §3.2.1.61 (VGCS) UPLINK REJECT COMMAND */
+struct gsm0808_uplink_reject_cmd {
+ enum gsm0808_cause cause;
+
+ bool current_talker_priority_present;
+ enum gsm0808_talker_priority current_talker_priority;
+ bool rejected_talker_priority_present;
+ enum gsm0808_talker_priority rejected_talker_priority;
+
+ bool talker_identity_present;
+ struct gsm0808_talker_identity talker_identity;
+};
+struct msgb *gsm0808_create_uplink_reject_cmd(const struct gsm0808_uplink_reject_cmd *params);
+
+/*! 3GPP TS 48.008 §3.2.1.62 (VGCS) UPLINK RELEASE COMMAND */
+struct msgb *gsm0808_create_uplink_release_cmd(const enum gsm0808_cause cause);
+
+/*! 3GPP TS 48.008 §3.2.1.63 (VGCS) UPLINK SEIZED COMMAND */
+struct gsm0808_uplink_seized_cmd {
+ enum gsm0808_cause cause;
+
+ bool talker_priority_present;
+ enum gsm0808_talker_priority talker_priority;
+
+ bool emerg_set_ind_present;
+
+ bool talker_identity_present;
+ struct gsm0808_talker_identity talker_identity;
+};
+struct msgb *gsm0808_create_uplink_seized_cmd(const struct gsm0808_uplink_seized_cmd *params);
+
+/*! 3GPP TS 48.008 §3.2.1.78 VGCS ADDITIONAL INFORMATION */
+struct msgb *gsm0808_create_vgcs_additional_info(const struct gsm0808_talker_identity *ti);
+
+/*! 3GPP TS 48.008 §3.2.1.79 VGCS/VBS AREA CELL INFO */
+struct gsm0808_vgcs_vbs_area_cell_info {
+ struct gsm0808_cell_id_list_segment cils;
+
+ bool ass_req_present;
+ enum gsm0808_assignment_requirement ass_req;
+};
+struct msgb *gsm0808_create_vgcs_vbs_area_cell_info(const struct gsm0808_vgcs_vbs_area_cell_info *params);
+
+/*! 3GPP TS 48.008 §3.2.1.80 VGCS/VBS ASSIGNMENT STATUS */
+struct gsm0808_vgcs_vbs_assign_stat {
+ /* established cells */
+ bool cils_est_present;
+ struct gsm0808_cell_id_list_segment cils_est;
+
+ /* cells to be established */
+ bool cils_tbe_present;
+ struct gsm0808_cell_id_list_segment cils_tbe;
+
+ /* released cells - no user present */
+ bool cils_rel_present;
+ struct gsm0808_cell_id_list_segment cils_rel;
+
+ /* not established cells - no establishment possible */
+ bool cils_ne_present;
+ struct gsm0808_cell_id_list_segment cils_ne;
+
+ bool cell_status_present;
+ enum gsm0808_vgcs_vbs_cell_status cell_status;
+};
+struct msgb *gsm0808_create_vgcs_vbs_assign_stat(const struct gsm0808_vgcs_vbs_assign_stat *params);
+
+/*! 3GPP TS 48.008 §3.2.1.81 VGCS SMS */
+struct msgb *gsm0808_create_vgcs_sms(const struct gsm0808_sms_to_vgcs *sms);
+
+/*! 3GPP TS 48.008 §3.2.1.82 (VGCS/VBS) NOTIFICATION DATA */
+struct gsm0808_notification_data {
+ struct gsm0808_application_data app_data;
+ struct gsm0808_data_identity data_ident;
+
+ bool msisdn_present;
+ char msisdn[MSISDN_MAXLEN + 1];
+};
+struct msgb *gsm0808_create_notification_data(const struct gsm0808_notification_data *parms);
+
struct msgb *gsm0808_create_dtap(struct msgb *msg, uint8_t link_id);
void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id);
diff --git a/include/osmocom/gsm/gsm0808_lcs.h b/include/osmocom/gsm/gsm0808_lcs.h
index 8fcbe380..71665013 100644
--- a/include/osmocom/gsm/gsm0808_lcs.h
+++ b/include/osmocom/gsm/gsm0808_lcs.h
@@ -20,10 +20,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
diff --git a/include/osmocom/gsm/gsm0808_utils.h b/include/osmocom/gsm/gsm0808_utils.h
index 76e90645..dbe2abf9 100644
--- a/include/osmocom/gsm/gsm0808_utils.h
+++ b/include/osmocom/gsm/gsm0808_utils.h
@@ -31,6 +31,8 @@ struct sockaddr_storage;
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/core/endian.h>
/*! (225-1)/2 is the maximum number of elements in a cell identifier list. */
#define GSM0808_CELL_ID_LIST2_MAXLEN 127
@@ -44,6 +46,8 @@ union gsm0808_cell_id_u {
uint16_t ci;
struct osmo_location_area_id lai_and_lac;
uint16_t lac;
+ struct osmo_service_area_id sai;
+ /* osmocom specific: */
struct osmo_cell_global_id_ps global_ps;
};
@@ -60,6 +64,164 @@ struct gsm0808_cell_id_list2 {
unsigned int id_list_len;
};
+/*! Packed representation of a Priority IE (GGPP TS 48.008 3.2.2.18) */
+struct gsm0808_priority {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t pvi:1, /* Preemption Vulnerability indicator */
+ qa:1, /* Queuing allowed indicator */
+ priority_level:4, /* Priority level: 1 == hightest, 14 == lowest */
+ pci:1, /* Preemption Capability indicator */
+ spare:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t spare:1, pci:1, priority_level:4, qa:1, pvi:1;
+#endif
+} __attribute__ ((packed));
+
+/*! Packed representation of a VGCS Feature Flags IE (3GPP TS 48.008 3.2.2.88) */
+struct gsm0808_vgcs_feature_flags {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t tp_ind:1, /* Talker priority supported */
+ as_ind_circuit:1, /* A-interface circuit sharing supported */
+ as_ind_link:1, /* A-interface link sharing supported */
+ bss_res:1, /* BSS supports re-establishment */
+ tcp:1, /* Talker channel parameter supported */
+ spare:3;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t spare:3, tcp:1, bss_res:1, as_ind_link:1, as_ind_circuit:1, tp_ind:1;
+#endif
+} __attribute__ ((packed));
+
+/* TS 48.008 3.2.2.52 */
+enum gsm0808_assignment_requirement {
+ GSM0808_ASRQ_DELAY_ALLOWED = 0x00,
+ GSM0808_ASRQ_IMMEDIATE = 0x01,
+ GSM0808_ASRQ_IMMEDIATE_ON_DEMAND = 0x02,
+};
+
+/* TS 48.008 Table 10.5.8 */
+enum gsm0808_service_flag {
+ GSM0808_SF_VBS = 0,
+ GSM0808_SF_VGCS = 1,
+};
+
+enum gsm0808_call_priority {
+ GSM0808_CALL_PRIORITY_NONE = 0x00,
+ GSM0808_CALL_PRIORITY_LEVEL_4 = 0x01,
+ GSM0808_CALL_PRIORITY_LEVEL_3 = 0x02,
+ GSM0808_CALL_PRIORITY_LEVEL_2 = 0x03,
+ GSM0808_CALL_PRIORITY_LEVEL_1 = 0x04,
+ GSM0808_CALL_PRIORITY_LEVEL_0 = 0x05,
+ GSM0808_CALL_PRIORITY_LEVEL_B = 0x06,
+ GSM0808_CALL_PRIORITY_LEVEL_A = 0x07,
+};
+
+/*! Packed representation of a Group Call Reference IE (3GPP TS 48.008 3.2.2.55) */
+struct gsm0808_group_callref {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t call_ref_hi[3];
+ uint8_t call_priority:3,
+ af:1, /* Acknowledgement flag */
+ sf:1, /* Service flag */
+ call_ref_lo:3;
+ uint8_t spare:4,
+ ciphering_info:4;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t call_ref_hi[3];
+ uint8_t call_ref_lo:3, sf:1, af:1, call_priority:3;
+ uint8_t ciphering_info:4, spare:4;
+#endif
+} __attribute__ ((packed));
+
+/* TS 48.008 3.2.2.26 */
+enum gsm0808_downlink_dtx_flag {
+ GSM0808_DTX_FLAG_ALLOW = 0,
+ GSM0808_DTX_FLAG_FORBID = 1,
+};
+
+/*! Parsed representation of a Cell Identifier List Segment IE (3GPP TS 48.008 3.2.2.27a) */
+struct gsm0808_cell_id_list_segment {
+ uint8_t seq_last;
+ uint8_t seq_number;
+ struct gsm0808_cell_id_list2 cil;
+};
+
+/*! Parsed representation of a Circuit Pool List IE (3GPP TS 48.008 3.2.2.26) */
+#define CIRCUIT_POOL_LIST_MAXLEN 252
+struct gsm0808_circuit_pool_list {
+ uint8_t pool[CIRCUIT_POOL_LIST_MAXLEN];
+ unsigned int list_len;
+};
+
+/* 3GPP TS 48.008 Table 3.2.2.90.1 Talker Priority */
+enum gsm0808_talker_priority {
+ GSM0808_TALKER_PRIORITY_NORMAL = 0x00,
+ GSM0808_TALKER_PRIORITY_PRIVILEGED = 0x01,
+ GSM0808_TALKER_PRIORITY_EMERGENCY = 0x02,
+};
+
+/*! Parsed representation of a Layer 3 Information IE (3GPP TS 48.008 3.2.2.24) */
+#define LAYER_3_INFORMATION_MAXLEN 252
+struct gsm0808_layer_3_information {
+ uint8_t l3[LAYER_3_INFORMATION_MAXLEN];
+ unsigned int l3_len;
+};
+
+/*! Parsed representation of a Talker Identity IE (3GPP TS 48.008 3.2.2.91) */
+#define TALKER_IDENTITY_MAXLEN 17
+struct gsm0808_talker_identity {
+ uint8_t talker_id[TALKER_IDENTITY_MAXLEN];
+ unsigned int id_bits;
+};
+
+/* 3GPP TS 48.008 3.2.2.94 VGCS/VBS Cell Status */
+enum gsm0808_vgcs_vbs_cell_status {
+ GSM0808_CSTAT_ESTABLISHED = 0x00,
+ GSM0808_CSTAT_NOT_ESTABLISHED1 = 0x01,
+ GSM0808_CSTAT_RELEASED_NO_USER = 0x02,
+ GSM0808_CSTAT_NOT_ESTABLISHED2 = 0x03,
+};
+
+/*! Parsed representation of a SMS to VGCS IE (3GPP TS 48.008 3.2.2.92) */
+#define SMS_TO_VGCS_MAXLEN 252
+struct gsm0808_sms_to_vgcs {
+ uint8_t sms[SMS_TO_VGCS_MAXLEN];
+ unsigned int sms_len;
+};
+
+/*! Parsed representation of a Application Data IE (3GPP TS 48.008 3.2.2.98) */
+#define APP_DATA_MAXLEN 9
+struct gsm0808_application_data {
+ uint8_t data[APP_DATA_MAXLEN];
+ unsigned int data_len;
+};
+
+/*! Packed representation of a Data Identity IE (GGPP TS 48.008 3.2.2.99) */
+enum gsm0808_application_idndicator {
+ GSM0808_AI_APP_DATA = 0x00,
+ GSM0808_AI_CONFIRM_APP_DATA = 0x01,
+};
+
+#define GSM0808_DP_MASK_TALKERS_LISTENERS 0x04
+#define GSM0808_DP_MASK_DISPATCHERS 0x02
+#define GSM0808_DP_MASK_NETWORK_APP 0x01
+
+struct gsm0808_data_identity {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t ai:1, /* Application Indicator */
+ di:4, /* Data identifier */
+ dp:3; /* Distribution parameter (bit mask) */
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t dp:3, di:4, ai:1;
+#endif
+} __attribute__ ((packed));
+
+/*! Parsed representation of a MSISDN IE (3GPP TS 48.008 3.2.2.101) */
+#define MSISDN_MAXLEN 20
+
/*! LCLS-related parameters from 3GPP TS 48.008 */
struct osmo_lcls {
enum gsm0808_lcls_config config; /**< §3.2.2.116 Configuration */
@@ -109,12 +271,17 @@ uint8_t gsm0808_enc_lcls(struct msgb *msg, const struct osmo_lcls *lcls);
int gsm0808_dec_lcls(struct osmo_lcls *lcls, const struct tlv_parsed *tp);
uint8_t gsm0808_enc_speech_codec(struct msgb *msg,
- const struct gsm0808_speech_codec *sc);
+ const struct gsm0808_speech_codec *sc)
+ OSMO_DEPRECATED("use gsm0808_enc_speech_codec2() instead");
+int gsm0808_enc_speech_codec2(struct msgb *msg,
+ const struct gsm0808_speech_codec *sc);
int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc,
const uint8_t *elem, uint8_t len);
uint8_t gsm0808_enc_speech_codec_list(struct msgb *msg,
- const struct gsm0808_speech_codec_list
- *scl);
+ const struct gsm0808_speech_codec_list *scl)
+ OSMO_DEPRECATED("use gsm0808_enc_speech_codec_list2() instead");
+int gsm0808_enc_speech_codec_list2(struct msgb *msg,
+ const struct gsm0808_speech_codec_list *scl);
int gsm0808_dec_speech_codec_list(struct gsm0808_speech_codec_list *scl,
const uint8_t *elem, uint8_t len);
uint8_t gsm0808_enc_channel_type(struct msgb *msg,
@@ -125,6 +292,8 @@ uint8_t gsm0808_enc_encrypt_info(struct msgb *msg,
const struct gsm0808_encrypt_info *ei);
int gsm0808_dec_encrypt_info(struct gsm0808_encrypt_info *ei,
const uint8_t *elem, uint8_t len);
+int gsm0808_enc_kc128(struct msgb *msg, const uint8_t *kc128);
+int gsm0808_dec_kc128(uint8_t *kc128, const uint8_t *elem, uint8_t len);
uint8_t gsm0808_enc_cell_id_list2(struct msgb *msg, const struct gsm0808_cell_id_list2 *cil);
uint8_t gsm0808_enc_cell_id_list(struct msgb *msg,
const struct gsm0808_cell_id_list *cil)
@@ -179,37 +348,34 @@ static inline uint8_t gsm0808_current_channel_type_1(enum gsm_chan_t type)
static inline enum gsm0808_permitted_speech gsm0808_permitted_speech(enum gsm_chan_t type,
enum gsm48_chan_mode mode)
{
- switch (mode) {
- case GSM48_CMODE_SPEECH_V1:
- switch (type) {
- case GSM_LCHAN_TCH_F:
- return GSM0808_PERM_FR1;
- case GSM_LCHAN_TCH_H:
- return GSM0808_PERM_HR1;
- default:
- return 0;
- }
- case GSM48_CMODE_SPEECH_EFR:
- switch (type) {
- case GSM_LCHAN_TCH_F:
- return GSM0808_PERM_FR2;
- case GSM_LCHAN_TCH_H:
- return GSM0808_PERM_HR2;
- default:
- return 0;
- }
- case GSM48_CMODE_SPEECH_AMR:
- switch (type) {
- case GSM_LCHAN_TCH_F:
- return GSM0808_PERM_FR3;
- case GSM_LCHAN_TCH_H:
- return GSM0808_PERM_HR3;
- default:
- return 0;
- }
+#define MODE_TYPE(mode, type) ((mode << 16) | type)
+
+ switch (MODE_TYPE(mode, type)) {
+ case MODE_TYPE(GSM48_CMODE_SPEECH_V1, GSM_LCHAN_TCH_F):
+ return GSM0808_PERM_FR1;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_V1, GSM_LCHAN_TCH_H):
+ return GSM0808_PERM_HR1;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_EFR, GSM_LCHAN_TCH_F):
+ return GSM0808_PERM_FR2;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_EFR, GSM_LCHAN_TCH_H):
+ return GSM0808_PERM_HR2; /* (deprecated) */
+ case MODE_TYPE(GSM48_CMODE_SPEECH_AMR, GSM_LCHAN_TCH_F):
+ return GSM0808_PERM_FR3;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_AMR, GSM_LCHAN_TCH_H):
+ return GSM0808_PERM_HR3;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_V4, GSM_LCHAN_TCH_F):
+ return GSM0808_PERM_FR4;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_V4, GSM_LCHAN_TCH_H):
+ return GSM0808_PERM_HR4;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_V5, GSM_LCHAN_TCH_F):
+ return GSM0808_PERM_FR5; /* FR only */
+ case MODE_TYPE(GSM48_CMODE_SPEECH_V6, GSM_LCHAN_TCH_H):
+ return GSM0808_PERM_HR6; /* HR only */
default:
return 0;
}
+
+#undef MODE_TYPE
}
/*! Return 3GPP TS 48.008 3.2.2.33 Chosen Channel. */
@@ -221,6 +387,9 @@ static inline uint8_t gsm0808_chosen_channel(enum gsm_chan_t type, enum gsm48_ch
case GSM48_CMODE_SPEECH_V1:
case GSM48_CMODE_SPEECH_EFR:
case GSM48_CMODE_SPEECH_AMR:
+ case GSM48_CMODE_SPEECH_V4:
+ case GSM48_CMODE_SPEECH_V5:
+ case GSM48_CMODE_SPEECH_V6:
channel_mode = 0x9;
break;
case GSM48_CMODE_SIGN:
@@ -238,6 +407,33 @@ static inline uint8_t gsm0808_chosen_channel(enum gsm_chan_t type, enum gsm48_ch
case GSM48_CMODE_DATA_3k6:
channel_mode = 0xd;
break;
+ case GSM48_CMODE_DATA_29k0:
+ channel_mode = 0x1;
+ break;
+ case GSM48_CMODE_DATA_32k0:
+ channel_mode = 0x2;
+ break;
+ case GSM48_CMODE_DATA_43k5:
+ channel_mode = 0x3;
+ break;
+ case GSM48_CMODE_DATA_43k5_14k5:
+ channel_mode = 0x4;
+ break;
+ case GSM48_CMODE_DATA_29k0_14k5:
+ channel_mode = 0x5;
+ break;
+ case GSM48_CMODE_DATA_43k5_29k0:
+ channel_mode = 0x6;
+ break;
+ case GSM48_CMODE_DATA_14k5_43k5:
+ channel_mode = 0x7;
+ break;
+ case GSM48_CMODE_DATA_14k5_29k0:
+ channel_mode = 0xa;
+ break;
+ case GSM48_CMODE_DATA_29k0_43k5:
+ channel_mode = 0xf;
+ break;
default:
return 0;
}
@@ -255,6 +451,7 @@ static inline uint8_t gsm0808_chosen_channel(enum gsm_chan_t type, enum gsm48_ch
case GSM_LCHAN_TCH_H:
channel = 0x9;
break;
+ /* TODO: more than 1 TCHs? */
default:
return 0;
}
@@ -266,4 +463,23 @@ const char *gsm0808_channel_type_name(const struct gsm0808_channel_type *ct);
char *gsm0808_channel_type_name_buf(char *buf, size_t buf_len, const struct gsm0808_channel_type *ct);
char *gsm0808_channel_type_name_c(const void *ctx, const struct gsm0808_channel_type *ct);
+uint8_t gsm0808_enc_group_callref(struct msgb *msg, const struct gsm0808_group_callref *gc);
+int gsm0808_dec_group_callref(struct gsm0808_group_callref *gc, const uint8_t *elem, uint8_t len);
+uint8_t gsm0808_enc_priority(struct msgb *msg, const struct gsm0808_priority *pri);
+int gsm0808_dec_priority(struct gsm0808_priority *pri, const uint8_t *elem, uint8_t len);
+uint8_t gsm0808_enc_vgcs_feature_flags(struct msgb *msg, const struct gsm0808_vgcs_feature_flags *ff);
+int gsm0808_dec_vgcs_feature_flags(struct gsm0808_vgcs_feature_flags *ff, const uint8_t *elem, uint8_t len);
+uint8_t gsm0808_enc_talker_identity(struct msgb *msg, const struct gsm0808_talker_identity *ti);
+int gsm0808_dec_talker_identity(struct gsm0808_talker_identity *ti, const uint8_t *elem, uint8_t len);
+uint8_t gsm0808_enc_data_identity(struct msgb *msg, const struct gsm0808_data_identity *ai);
+int gsm0808_dec_data_identity(struct gsm0808_data_identity *ai, const uint8_t *elem, uint8_t len);
+uint8_t gsm0808_enc_msisdn(struct msgb *msg, const char *msisdn);
+int gsm0808_dec_msisdn(char *msisdn, const char *elem, uint8_t len);
+uint8_t gsm0808_enc_assign_req(struct msgb *msg, const enum gsm0808_assignment_requirement ar);
+int gsm0808_dec_assign_req(enum gsm0808_assignment_requirement *ar, const uint8_t *elem, uint8_t len);
+uint8_t gsm0808_enc_cell_id_list_segment(struct msgb *msg, uint8_t ie_type,
+ const struct gsm0808_cell_id_list_segment *ci);
+int gsm0808_dec_cell_id_list_segment(struct gsm0808_cell_id_list_segment *ci, const uint8_t *elem, uint8_t len);
+int gsm0808_dec_call_id(uint32_t *ci, const uint8_t *elem, uint8_t len);
+
/*! @} */
diff --git a/include/osmocom/gsm/gsm23003.h b/include/osmocom/gsm/gsm23003.h
index 487379a4..4070581f 100644
--- a/include/osmocom/gsm/gsm23003.h
+++ b/include/osmocom/gsm/gsm23003.h
@@ -30,6 +30,9 @@ struct osmo_cell_global_id {
uint16_t cell_identity;
};
+/* 3GPP TS 48.018:
+ * 8c.1.4.1.1 GERAN BSS identification (RIM)
+ * sec 11.3.9 Cell Identifier */
struct osmo_cell_global_id_ps {
struct osmo_routing_area_id rai;
uint16_t cell_identity;
@@ -134,6 +137,10 @@ const char *osmo_cgi_ps_name(const struct osmo_cell_global_id_ps *cgi_ps);
const char *osmo_cgi_ps_name2(const struct osmo_cell_global_id_ps *cgi_ps);
char *osmo_cgi_ps_name_buf(char *buf, size_t buf_len, const struct osmo_cell_global_id_ps *cgi_ps);
char *osmo_cgi_ps_name_c(const void *ctx, const struct osmo_cell_global_id_ps *cgi_ps);
+const char *osmo_sai_name(const struct osmo_service_area_id *sai);
+const char *osmo_sai_name2(const struct osmo_service_area_id *sai);
+char *osmo_sai_name_buf(char *buf, size_t buf_len, const struct osmo_service_area_id *sai);
+char *osmo_sai_name_c(const void *ctx, const struct osmo_service_area_id *sai);
const char *osmo_gummei_name(const struct osmo_gummei *gummei);
char *osmo_gummei_name_buf(char *buf, size_t buf_len, const struct osmo_gummei *gummei);
char *osmo_gummei_name_c(const void *ctx, const struct osmo_gummei *gummei);
diff --git a/include/osmocom/gsm/gsm29118.h b/include/osmocom/gsm/gsm29118.h
index e81cce4c..33c40d36 100644
--- a/include/osmocom/gsm/gsm29118.h
+++ b/include/osmocom/gsm/gsm29118.h
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
diff --git a/include/osmocom/gsm/gsm29205.h b/include/osmocom/gsm/gsm29205.h
index 95754567..6c845fb9 100644
--- a/include/osmocom/gsm/gsm29205.h
+++ b/include/osmocom/gsm/gsm29205.h
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -37,6 +33,6 @@ struct osmo_gcr_parsed {
uint8_t cr[5]; /** Call Reference ID */
};
-uint8_t osmo_enc_gcr(struct msgb *msg, const struct osmo_gcr_parsed *g) OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE;
-int osmo_dec_gcr(struct osmo_gcr_parsed *gcr, const uint8_t *elem, uint8_t len) OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE;
+uint8_t osmo_enc_gcr(struct msgb *msg, const struct osmo_gcr_parsed *g);
+int osmo_dec_gcr(struct osmo_gcr_parsed *gcr, const uint8_t *elem, uint8_t len);
bool osmo_gcr_eq(const struct osmo_gcr_parsed *gcr1, const struct osmo_gcr_parsed *gcr2);
diff --git a/include/osmocom/gsm/gsm44021.h b/include/osmocom/gsm/gsm44021.h
new file mode 100644
index 00000000..8f89c56b
--- /dev/null
+++ b/include/osmocom/gsm/gsm44021.h
@@ -0,0 +1,8 @@
+#pragma once
+#include <osmocom/isdn/v110.h>
+
+int osmo_csd_12k_6k_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits);
+int osmo_csd_12k_6k_encode_frame(ubit_t *ra_bits, size_t ra_bits_size, const struct osmo_v110_decoded_frame *fr);
+int osmo_csd_3k6_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits);
+int osmo_csd_3k6_encode_frame(ubit_t *ra_bits, size_t ra_bits_size, const struct osmo_v110_decoded_frame *fr);
+void osmo_csd_ubit_dump(FILE *outf, const ubit_t *fr, size_t in_len);
diff --git a/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h
index c1ca6308..00fb6f40 100644
--- a/include/osmocom/gsm/gsm48.h
+++ b/include/osmocom/gsm/gsm48.h
@@ -20,7 +20,9 @@
* To mark an invalid / unset MNC, this value shall be used. */
#define GSM_MCC_MNC_INVALID 0xFFFF
-/* A parsed GPRS routing area */
+/* A parsed GPRS routing area.
+ * Preferably use struct osmo_routing_area_id, it is better integrated with API like osmo_plmn_cmp().
+ */
struct gprs_ra_id {
uint16_t mcc;
uint16_t mnc;
@@ -35,6 +37,7 @@ extern const struct tlv_definition gsm48_mm_att_tlvdef;
const char *gsm48_cc_state_name(uint8_t state);
const char *gsm48_cc_msg_name(uint8_t msgtype);
const char *gsm48_rr_msg_name(uint8_t msgtype);
+const char *gsm48_rr_short_pd_msg_name(uint8_t msgtype);
const char *rr_cause_name(uint8_t cause);
const char *osmo_rai_name(const struct gprs_ra_id *rai);
char *osmo_rai_name_buf(char *buf, size_t buf_len, const struct gprs_ra_id *rai);
@@ -95,18 +98,23 @@ char *osmo_mobile_identity_to_str_c(void *ctx, const struct osmo_mobile_identity
int osmo_mobile_identity_cmp(const struct osmo_mobile_identity *a, const struct osmo_mobile_identity *b);
int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t *mi_data, uint8_t mi_len,
bool allow_hex);
+int osmo_mobile_identity_decode_from_l3_buf(struct osmo_mobile_identity *mi, const uint8_t *l3_data, size_t l3_len,
+ bool allow_hex);
int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct msgb *msg, bool allow_hex);
int osmo_mobile_identity_encoded_len(const struct osmo_mobile_identity *mi, int *mi_digits);
int osmo_mobile_identity_encode_buf(uint8_t *buf, size_t buflen, const struct osmo_mobile_identity *mi, bool allow_hex);
int osmo_mobile_identity_encode_msgb(struct msgb *msg, const struct osmo_mobile_identity *mi, bool allow_hex);
/* Parse Routeing Area Identifier */
+int osmo_routing_area_id_decode(struct osmo_routing_area_id *dst, const uint8_t *ra_data, size_t ra_data_len);
+int osmo_routing_area_id_encode_buf(uint8_t *buf, size_t buflen, const struct osmo_routing_area_id *src);
+int osmo_routing_area_id_encode_msgb(struct msgb *msg, const struct osmo_routing_area_id *src);
void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf);
void gsm48_encode_ra(struct gsm48_ra_id *out, const struct gprs_ra_id *raid);
int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid) OSMO_DEPRECATED("Use gsm48_encode_ra() instead");
bool gsm48_ra_equal(const struct gprs_ra_id *raid1, const struct gprs_ra_id *raid2);
-int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc);
+int gsm48_number_of_paging_subchannels(const struct gsm48_control_channel_descr *chan_desc);
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");
diff --git a/include/osmocom/gsm/gsm48_ie.h b/include/osmocom/gsm/gsm48_ie.h
index b79cbfcb..4768283f 100644
--- a/include/osmocom/gsm/gsm48_ie.h
+++ b/include/osmocom/gsm/gsm48_ie.h
@@ -117,8 +117,9 @@ struct gsm_sysinfo_freq {
};
/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
-int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
- uint8_t len, uint8_t mask, uint8_t frqt);
+int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f,
+ const uint8_t *cd, uint8_t len,
+ uint8_t mask, uint8_t frqt);
/* decode "CSN.1 encoded Classmark 3" (10.5.1.7) */
int gsm48_decode_classmark3(struct gsm48_classmark3 *classmark3_out,
diff --git a/include/osmocom/gsm/gsm48_rest_octets.h b/include/osmocom/gsm/gsm48_rest_octets.h
index f2958249..cdb2e807 100644
--- a/include/osmocom/gsm/gsm48_rest_octets.h
+++ b/include/osmocom/gsm/gsm48_rest_octets.h
@@ -2,7 +2,7 @@
#include <stdbool.h>
#include <osmocom/gsm/sysinfo.h>
-#include <osmocom/gprs/protocol/gsm_04_60.h>
+#include <osmocom/gsm/protocol/gsm_44_060.h>
/* 16 is the max. number of SI2quater messages according to 3GPP TS 44.018 Table 10.5.2.33b.1:
4-bit index is used (2#1111 = 10#15) */
@@ -13,6 +13,9 @@
/* generate SI1 rest octets */
int osmo_gsm48_rest_octets_si1_encode(uint8_t *data, uint8_t *nch_pos, int is1800_net);
+int osmo_gsm48_si1ro_nch_pos_decode(uint8_t value, uint8_t *num_blocks, uint8_t *first_block);
+int osmo_gsm48_si1ro_nch_pos_encode(uint8_t num_blocks, uint8_t first_block);
+
int osmo_gsm48_rest_octets_si2quater_encode(uint8_t *data, uint8_t si2q_index, uint8_t si2q_count,
const uint16_t *uarfcn_list, size_t *u_offset,
size_t uarfcn_length, uint16_t *scramble_list,
diff --git a/include/osmocom/gsm/gsm_utils.h b/include/osmocom/gsm/gsm_utils.h
index 0909006e..b250c594 100644
--- a/include/osmocom/gsm/gsm_utils.h
+++ b/include/osmocom/gsm/gsm_utils.h
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -37,6 +33,7 @@
} while (0)
#define GSM_MAX_FN (26*51*2048)
+#define GSM_FN_UNSET 0xFFFFFFFF
/* Max length of random identifier which can be requested via osmo_get_rand_id() */
#define OSMO_MAX_RAND_ID_LEN 16
@@ -156,6 +153,13 @@ static inline int rach_max_trans_raw2val(int raw) {
const int tbl[4] = { 1, 2, 4, 7 };
return tbl[raw & 3];
}
+static inline uint8_t rach_tx_integer_raw2val(uint8_t raw) {
+ const int tbl[6] = { 14, 16, 20, 25, 32, 50 };
+ raw &= 0x0f;
+ if (raw <= 9)
+ return raw + 3;
+ return tbl[raw - 10];
+}
#define ARFCN_PCS 0x8000
#define ARFCN_UPLINK 0x4000
@@ -177,13 +181,21 @@ void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn);
char *gsm_fn_as_gsmtime_str(uint32_t fn);
/* Convert from GSM time to frame number */
-uint32_t gsm_gsmtime2fn(struct gsm_time *time);
+uint32_t gsm_gsmtime2fn(const struct gsm_time *time);
/* Returns static buffer with string representation of a GSM Time */
char *osmo_dump_gsmtime(const struct gsm_time *tm);
char *osmo_dump_gsmtime_buf(char *buf, size_t buf_len, const struct gsm_time *tm);
char *osmo_dump_gsmtime_c(const void *ctx, const struct gsm_time *tm);
+/* Reduced Frame Number (3GPP TS 44.018 §10.5.2.38) */
+#define GSM_RFN_MODULUS 42432
+uint32_t gsm_rfn2fn(uint16_t rfn, uint32_t curr_fn);
+static inline uint16_t gsm_fn2rfn(uint32_t fn)
+{
+ return fn % GSM_RFN_MODULUS;
+}
+
/* GSM TS 03.03 Chapter 2.6 */
enum gprs_tlli_type {
TLLI_LOCAL,
@@ -213,9 +225,11 @@ enum gsm_phys_chan_config {
GSM_PCHAN_UNKNOWN,
GSM_PCHAN_CCCH_SDCCH4_CBCH,
GSM_PCHAN_SDCCH8_SACCH8C_CBCH,
- GSM_PCHAN_TCH_F_TCH_H_PDCH,
+ GSM_PCHAN_OSMO_DYN,
_GSM_PCHAN_MAX
};
+/* Backward compatibility with older naming: */
+#define GSM_PCHAN_TCH_F_TCH_H_PDCH GSM_PCHAN_OSMO_DYN
/* Osmocom internal, not part of any gsm spec */
enum gsm_chan_t {
diff --git a/include/osmocom/gsm/gsup.h b/include/osmocom/gsm/gsup.h
index 56d7a309..6ad72d28 100644
--- a/include/osmocom/gsm/gsup.h
+++ b/include/osmocom/gsm/gsup.h
@@ -37,9 +37,13 @@
*
*/
#pragma once
+#if (!EMBEDDED)
#include <stdint.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/defs.h>
+#include <osmocom/core/endian.h>
+#include <osmocom/core/socket.h>
#include <osmocom/gsm/gsup_sms.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gsm/protocol/gsm_03_40.h>
@@ -57,8 +61,7 @@
/*! Maximum number of octets encoding MSISDN in BCD format */
#define OSMO_GSUP_MAX_MSISDN_LEN 9
#define OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN 43 /* TS 24.008 10.5.4.7 */
-
-#define OSMO_GSUP_PDP_TYPE_SIZE 2
+#define OSMO_GSUP_MAX_PCO_LEN 251
/*! Information Element Identifiers for GSUP IEs */
enum osmo_gsup_iei {
@@ -71,12 +74,14 @@ enum osmo_gsup_iei {
OSMO_GSUP_FREEZE_PTMSI_IE = 0x07,
OSMO_GSUP_MSISDN_IE = 0x08,
OSMO_GSUP_HLR_NUMBER_IE = 0x09,
- OSMO_GSUP_MESSAGE_CLASS_IE = 0x0a,
+ OSMO_GSUP_MESSAGE_CLASS_IE = 0x0a,
OSMO_GSUP_PDP_CONTEXT_ID_IE = 0x10,
- OSMO_GSUP_PDP_TYPE_IE = 0x11,
+ OSMO_GSUP_PDP_ADDRESS_IE = 0x11,
+#define OSMO_GSUP_PDP_TYPE_IE OSMO_GSUP_PDP_ADDRESS_IE /* Backward compat */
OSMO_GSUP_ACCESS_POINT_NAME_IE = 0x12,
OSMO_GSUP_PDP_QOS_IE = 0x13,
OSMO_GSUP_CHARG_CHAR_IE = 0x14,
+ OSMO_GSUP_PCO_IE = 0x15,
OSMO_GSUP_RAND_IE = 0x20,
OSMO_GSUP_SRES_IE = 0x21,
OSMO_GSUP_KC_IE = 0x22,
@@ -198,6 +203,10 @@ enum osmo_gsup_message_type {
OSMO_GSUP_MSGT_E_ABORT = 0b01001011,
OSMO_GSUP_MSGT_ROUTING_ERROR = 0b01001110,
+
+ OSMO_GSUP_MSGT_EPDG_TUNNEL_REQUEST = 0b01010000,
+ OSMO_GSUP_MSGT_EPDG_TUNNEL_ERROR = 0b01010001,
+ OSMO_GSUP_MSGT_EPDG_TUNNEL_RESULT = 0b01010010,
};
#define OSMO_GSUP_MSGT_E_ROUTING_ERROR OSMO_GSUP_MSGT_ROUTING_ERROR
@@ -256,8 +265,20 @@ osmo_gsup_session_state_name(enum osmo_gsup_session_state val)
struct osmo_gsup_pdp_info {
unsigned int context_id;
int have_info;
- /*! Type of PDP context */
- uint16_t pdp_type;
+ /*! Type of PDP context, 3GPP TS 29.060, 7.7.27 */
+ union {
+ uint16_t pdp_type OSMO_DEPRECATED("use pdp_type_org and pdp_type_nr instead");
+ struct {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t pdp_type_nr; /* enum gsm48_pdp_type_nr */
+ uint8_t pdp_type_org; /* enum gsm48_pdp_type_org */
+#elif OSMO_IS_BIG_ENDIAN
+ uint8_t pdp_type_org; /* enum gsm48_pdp_type_org */
+ uint8_t pdp_type_nr; /* enum gsm48_pdp_type_nr */
+#endif
+ };
+ };
+ struct osmo_sockaddr pdp_address[2];
/*! APN information, still in encoded form. Can be NULL if no
* APN information included */
const uint8_t *apn_enc;
@@ -281,6 +302,7 @@ enum osmo_gsup_message_class {
OSMO_GSUP_MESSAGE_CLASS_SMS = 2,
OSMO_GSUP_MESSAGE_CLASS_USSD = 3,
OSMO_GSUP_MESSAGE_CLASS_INTER_MSC = 4,
+ OSMO_GSUP_MESSAGE_CLASS_IPSEC_EPDG = 5,
/* Keep this as last entry with a value of max(enum osmo_gsup_message_class) + 1.
* This value shall serve as the size for an array to aid de-muxing all known GSUP classes. */
OSMO_GSUP_MESSAGE_CLASS_ARRAYSIZE
@@ -386,6 +408,12 @@ struct osmo_gsup_message {
enum osmo_rat_type current_rat_type;
enum osmo_rat_type supported_rat_types[8]; /*!< arbitrary choice */
size_t supported_rat_types_len;
+
+ /*! PCO protocol option 3GPP TS 24.008 10.5.6.3 / Table 10.5.136. PCO contains Octet 3-ZA */
+ const uint8_t *pco;
+ /*! Number of bytes of the PCO. */
+ size_t pco_len;
+
};
int osmo_gsup_decode(const uint8_t *data, size_t data_len,
@@ -394,4 +422,5 @@ int osmo_gsup_encode(struct msgb *msg, const struct osmo_gsup_message *gsup_msg)
int osmo_gsup_get_err_msg_type(enum osmo_gsup_message_type type_in)
OSMO_DEPRECATED("Use OSMO_GSUP_TO_MSGT_ERROR() instead");
+#endif /* (!EMBEDDED) */
/*! @} */
diff --git a/include/osmocom/gsm/i460_mux.h b/include/osmocom/gsm/i460_mux.h
index 8e392434..7c2f4af3 100644
--- a/include/osmocom/gsm/i460_mux.h
+++ b/include/osmocom/gsm/i460_mux.h
@@ -1,121 +1,2 @@
-/*! \file i460_mux.h
- * ITU-T I.460 sub-channel multiplexer + demultiplexer */
-/*
- * (C) 2020 by Harald Welte <laforge@gnumonks.org>
- *
- * SPDX-License-Identifier: GPL-2.0+
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
- */
-
#pragma once
-#include <stdint.h>
-#include <osmocom/core/bits.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/msgb.h>
-
-/* I.460 sub-slot rate */
-enum osmo_i460_rate {
- OSMO_I460_RATE_NONE, /* disabled */
- OSMO_I460_RATE_64k,
- OSMO_I460_RATE_32k,
- OSMO_I460_RATE_16k,
- OSMO_I460_RATE_8k,
-};
-
-struct osmo_i460_subchan;
-
-typedef void (*out_cb_bits_t)(struct osmo_i460_subchan *schan, void *user_data,
- const ubit_t *bits, unsigned int num_bits);
-typedef void (*out_cb_bytes_t)(struct osmo_i460_subchan *schan, void *user_data,
- const uint8_t *bytes, unsigned int num_bytes);
-
-struct osmo_i460_subchan_demux {
- /*! bit-buffer for output bits */
- uint8_t *out_bitbuf;
- /*! size of out_bitbuf in bytes */
- unsigned int out_bitbuf_size;
- /*! offset of next bit to be written in out_bitbuf */
- unsigned int out_idx;
- /*! callback to be called once we have received out_bitbuf_size bits */
- out_cb_bits_t out_cb_bits;
- out_cb_bytes_t out_cb_bytes;
- void *user_data;
-};
-
-typedef void (*in_cb_queue_empty_t)(struct osmo_i460_subchan *schan, void *user_data);
-
-struct osmo_i460_subchan_mux {
- /*! list of to-be-transmitted message buffers */
- struct llist_head tx_queue;
- in_cb_queue_empty_t in_cb_queue_empty;
- void *user_data;
-};
-
-struct osmo_i460_subchan {
- struct osmo_i460_timeslot *ts; /* back-pointer */
- enum osmo_i460_rate rate; /* 8/16/32/64k */
- uint8_t bit_offset; /* bit offset inside each byte of the B-channel */
- struct osmo_i460_subchan_demux demux;
- struct osmo_i460_subchan_mux mux;
-};
-
-struct osmo_i460_timeslot {
- struct osmo_i460_subchan schan[8];
-};
-
-/*! description of a sub-channel; passed by caller */
-struct osmo_i460_schan_desc {
- enum osmo_i460_rate rate;
- uint8_t bit_offset;
- struct {
- /* size (in bits) of the internal buffer; determines granularity */
- size_t num_bits;
- /*! call-back function called whenever we received num_bits */
- out_cb_bits_t out_cb_bits;
- /*! out_cb_bytes call-back function called whenever we received num_bits.
- * The user is usually expected to provide either out_cb_bits or out_cb_bytes. If only
- * out_cb_bits is provided, output data will always be provided as unpacked bits; if only
- * out_cb_bytes is provided, output data will always be provided as packet bits (bytes). If
- * both are provided, it is up to the I.460 multiplex to decide if it calls either of the two,
- * depending on what can be provided without extra conversion. */
- out_cb_bytes_t out_cb_bytes;
- /* opaque user data pointer to pass to out_cb */
- void *user_data;
- } demux;
-
- struct {
- /* call-back function whenever the muxer requires more input data from the sub-channels,
- * but has nothing enqueued yet. A typical function would then call osmo_i460_mux_enqueue() */
- in_cb_queue_empty_t in_cb_queue_empty;
- /* opaque user data pointer to pass to in_cb */
- void *user_data;
- } mux;
-};
-
-void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len);
-
-void osmo_i460_mux_enqueue(struct osmo_i460_subchan *schan, struct msgb *msg);
-int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len);
-
-void osmo_i460_ts_init(struct osmo_i460_timeslot *ts);
-
-struct osmo_i460_subchan *
-osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd);
-
-void osmo_i460_subchan_del(struct osmo_i460_subchan *schan);
-
-/*! @} */
+#include <osmocom/isdn/i460_mux.h>
diff --git a/include/osmocom/gsm/ipa.h b/include/osmocom/gsm/ipa.h
index 93cb1bf1..851b58e0 100644
--- a/include/osmocom/gsm/ipa.h
+++ b/include/osmocom/gsm/ipa.h
@@ -29,7 +29,7 @@ const char *ipa_ccm_idtag_name(uint8_t tag);
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");
+ OSMO_DEPRECATED_OUTSIDE("Use ipa_ccm_id_{get,resp}_parse instead");
/* 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);
diff --git a/include/osmocom/gsm/iuup.h b/include/osmocom/gsm/iuup.h
new file mode 100644
index 00000000..ab6b8acf
--- /dev/null
+++ b/include/osmocom/gsm/iuup.h
@@ -0,0 +1,129 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/prim.h>
+#include <osmocom/gsm/protocol/gsm_25_415.h>
+
+/***********************************************************************
+ * Primitives towards the lower layers (typically RTP transport)
+ ***********************************************************************/
+enum osmo_iuup_tnl_prim_type {
+ OSMO_IUUP_TNL_UNITDATA,
+};
+
+struct osmo_iuup_tnl_prim {
+ struct osmo_prim_hdr oph;
+};
+
+/***********************************************************************
+ * Primitives towards the upper layers at the RNL SAP
+ ***********************************************************************/
+
+/* 3GPP TS 25.415 Section 7.2.1 */
+enum osmo_iuup_rnl_prim_type {
+ OSMO_IUUP_RNL_CONFIG,
+ OSMO_IUUP_RNL_DATA,
+ OSMO_IUUP_RNL_STATUS,
+ OSMO_IUUP_RNL_UNIT_DATA,
+};
+
+/* TS 25.413 9.2.1.3*/
+#define IUUP_MAX_SUBFLOWS 7
+#define IUUP_MAX_RFCIS 64
+
+#define IUUP_TIMER_INIT_T_DEFAULT 1000
+#define IUUP_TIMER_TA_T_DEFAULT 500
+#define IUUP_TIMER_RC_T_DEFAULT 500
+#define IUUP_TIMER_INIT_N_DEFAULT 3
+#define IUUP_TIMER_TA_N_DEFAULT 1
+#define IUUP_TIMER_RC_N_DEFAULT 1
+struct osmo_iuup_rnl_config_timer {
+ uint32_t t_ms; /* time in ms */
+ uint32_t n_max; /* max number of repetitions */
+};
+struct osmo_iuup_rfci {
+ uint8_t used:1,
+ spare1:1,
+ id:6;
+ uint8_t spare2:4,
+ IPTI:4; /* values range 0-15, 4 bits */;
+ uint16_t subflow_sizes[IUUP_MAX_SUBFLOWS];
+};
+struct osmo_iuup_rnl_config {
+ /* transparent (true) or SMpSDU (false): */
+ bool transparent;
+
+ /* should we actively transmit INIT in SmpSDU mode? */
+ bool active;
+
+ /* Currently Version 0 or 1: */
+ uint8_t data_pdu_type;
+
+ /* Supported mode versions */
+ uint16_t supported_versions_mask;
+ uint8_t num_rfci;
+ uint8_t num_subflows;
+ bool IPTIs_present;
+ struct osmo_iuup_rfci rfci[IUUP_MAX_RFCIS];
+
+ /* TODO: Indication of delivery of erroneous SDUs*/
+ struct osmo_iuup_rnl_config_timer t_init;
+ struct osmo_iuup_rnl_config_timer t_ta;
+ struct osmo_iuup_rnl_config_timer t_rc;
+};
+
+struct osmo_iuup_rnl_data {
+ uint8_t rfci;
+ uint8_t frame_nr;
+ uint8_t fqc;
+};
+
+struct osmo_iuup_rnl_status {
+ enum iuup_procedure procedure;
+ union {
+ struct {
+ enum iuup_error_cause cause;
+ enum iuup_error_distance distance;
+ } error_event;
+ struct {
+ uint16_t mode_version;
+ uint8_t data_pdu_type;
+ uint8_t num_rfci;
+ uint8_t num_subflows;
+ bool IPTIs_present;
+ struct osmo_iuup_rfci rfci[IUUP_MAX_RFCIS];
+ } initialization;
+ struct {
+ } rate_control;
+ struct {
+ } time_alignment;
+ } u;
+};
+
+/* SAP on the upper side of IuUP, towards the user */
+struct osmo_iuup_rnl_prim {
+ struct osmo_prim_hdr oph;
+ union {
+ struct osmo_iuup_rnl_config config;
+ struct osmo_iuup_rnl_data data;
+ struct osmo_iuup_rnl_status status;
+ //struct osmo_iuup_rnl_unitdata unitdata;
+ } u;
+};
+
+struct osmo_iuup_instance;
+struct osmo_iuup_instance *osmo_iuup_instance_alloc(void *ctx, const char *id);
+void osmo_iuup_instance_free(struct osmo_iuup_instance *iui);
+
+void osmo_iuup_instance_set_user_prim_cb(struct osmo_iuup_instance *iui, osmo_prim_cb func, void *priv);
+void osmo_iuup_instance_set_transport_prim_cb(struct osmo_iuup_instance *iui, osmo_prim_cb func, void *priv);
+int osmo_iuup_tnl_prim_up(struct osmo_iuup_instance *iui, struct osmo_iuup_tnl_prim *itp);
+int osmo_iuup_rnl_prim_down(struct osmo_iuup_instance *inst, struct osmo_iuup_rnl_prim *irp);
+
+
+int osmo_iuup_compute_header_crc(const uint8_t *iuup_pdu, unsigned int pdu_len);
+int osmo_iuup_compute_payload_crc(const uint8_t *iuup_pdu, unsigned int pdu_len);
+
+struct osmo_iuup_rnl_prim *osmo_iuup_rnl_prim_alloc(void *ctx, unsigned int primitive, unsigned int operation, unsigned int size);
+struct osmo_iuup_tnl_prim *osmo_iuup_tnl_prim_alloc(void *ctx, unsigned int primitive, unsigned int operation, unsigned int size);
diff --git a/include/osmocom/gsm/l1sap.h b/include/osmocom/gsm/l1sap.h
index d1396908..0faab7e0 100644
--- a/include/osmocom/gsm/l1sap.h
+++ b/include/osmocom/gsm/l1sap.h
@@ -27,6 +27,8 @@ enum osmo_mph_info_type {
PRIM_INFO_MODIFY, /*!< Mode Modify of channel */
PRIM_INFO_ACT_CIPH, /*!< Activation of ciphering */
PRIM_INFO_DEACT_CIPH, /*!< Deactivation of ciphering */
+ PRIM_INFO_ACT_UL_ACC, /*!< Activation of uplink access detection */
+ PRIM_INFO_DEACT_UL_ACC, /*!< Deactivation of uplink access detection */
};
/*! PH-DATA presence information */
@@ -142,6 +144,11 @@ struct info_ciph_req_param {
uint8_t uplink; /*!< Apply to uplink */
};
+/*! for {ACT_UL_ACC,DEACT_UL_ACC} MPH-INFO.req */
+struct info_ulacc_req_param {
+ uint8_t chan_nr; /*!< Channel Number (Like RSL) */
+};
+
/*! for MPH-INFO.ind */
struct mph_info_param {
enum osmo_mph_info_type type; /*!< Info message type */
@@ -151,6 +158,7 @@ struct mph_info_param {
struct info_act_req_param act_req;
struct info_act_cnf_param act_cnf;
struct info_ciph_req_param ciph_req;
+ struct info_ulacc_req_param ulacc_req;
} u;
};
diff --git a/include/osmocom/gsm/lapd_core.h b/include/osmocom/gsm/lapd_core.h
index 69e10875..a4dc2505 100644
--- a/include/osmocom/gsm/lapd_core.h
+++ b/include/osmocom/gsm/lapd_core.h
@@ -1,177 +1,2 @@
-/*! \file lapd_core.h
- * primitive related stuff
- */
#pragma once
-
-#include <stdint.h>
-
-#include <osmocom/core/timer.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/prim.h>
-
-/*! \defgroup lapd LAPD implementation common part
- * @{
- * \file lapd_core.h
- */
-
-#define LOGDL(dl, level, fmt, args...) \
- LOGP(DLLAPD, level, "(%s) " fmt, (dl)->name, ## args)
-
-/*! LAPD related primitives (L2<->L3 SAP)*/
-enum osmo_dl_prim {
- PRIM_DL_UNIT_DATA, /*!< DL-UNIT-DATA */
- PRIM_DL_DATA, /*!< DL-DATA */
- PRIM_DL_EST, /*!< DL-ESTABLISH */
- PRIM_DL_REL, /*!< DL-RLEEASE */
- PRIM_DL_SUSP, /*!< DL-SUSPEND */
- PRIM_DL_RES, /*!< DL-RESUME */
- PRIM_DL_RECON, /*!< DL-RECONNECT */
- PRIM_MDL_ERROR, /*!< MDL-ERROR */
-};
-
-/* Uses the same values as RLL, so no conversion for GSM is required. */
-#define MDL_CAUSE_T200_EXPIRED 0x01
-#define MDL_CAUSE_REEST_REQ 0x02
-#define MDL_CAUSE_UNSOL_UA_RESP 0x03
-#define MDL_CAUSE_UNSOL_DM_RESP 0x04
-#define MDL_CAUSE_UNSOL_DM_RESP_MF 0x05
-#define MDL_CAUSE_UNSOL_SPRV_RESP 0x06
-#define MDL_CAUSE_SEQ_ERR 0x07
-#define MDL_CAUSE_UFRM_INC_PARAM 0x08
-#define MDL_CAUSE_SFRM_INC_PARAM 0x09
-#define MDL_CAUSE_IFRM_INC_MBITS 0x0a
-#define MDL_CAUSE_IFRM_INC_LEN 0x0b
-#define MDL_CAUSE_FRM_UNIMPL 0x0c
-#define MDL_CAUSE_SABM_MF 0x0d
-#define MDL_CAUSE_SABM_INFO_NOTALL 0x0e
-#define MDL_CAUSE_FRMR 0x0f
-
-/*! for MDL-ERROR.ind */
-struct mdl_error_ind_param {
- uint8_t cause; /*!< generic cause value */
-};
-
-/*! for DL-REL.req */
-struct dl_rel_req_param {
- uint8_t mode; /*!< release mode */
-};
-
-/*! primitive header for LAPD DL-SAP primitives */
-struct osmo_dlsap_prim {
- struct osmo_prim_hdr oph; /*!< generic primitive header */
- union {
- struct mdl_error_ind_param error_ind;
- struct dl_rel_req_param rel_req;
- } u; /*!< request-specific data */
-};
-
-/*! LAPD mode/role */
-enum lapd_mode {
- LAPD_MODE_USER, /*!< behave like user */
- LAPD_MODE_NETWORK, /*!< behave like network */
-};
-
-/*! LAPD state (Figure B.2/Q.921)*/
-enum lapd_state {
- LAPD_STATE_NULL = 0,
- LAPD_STATE_TEI_UNASS,
- LAPD_STATE_ASS_TEI_WAIT,
- LAPD_STATE_EST_TEI_WAIT,
- LAPD_STATE_IDLE,
- LAPD_STATE_SABM_SENT,
- LAPD_STATE_DISC_SENT,
- LAPD_STATE_MF_EST,
- LAPD_STATE_TIMER_RECOV,
-};
-
-/*! LAPD message format (I / S / U) */
-enum lapd_format {
- LAPD_FORM_UKN = 0,
- LAPD_FORM_I,
- LAPD_FORM_S,
- LAPD_FORM_U,
-};
-
-/*! LAPD message context */
-struct lapd_msg_ctx {
- struct lapd_datalink *dl;
- int n201;
- /* address */
- uint8_t cr;
- uint8_t sapi;
- uint8_t tei;
- uint8_t lpd;
- /* control */
- uint8_t format;
- uint8_t p_f; /* poll / final bit */
- uint8_t n_send;
- uint8_t n_recv;
- uint8_t s_u; /* S or repectivly U function bits */
- /* length */
- int length;
- uint8_t more;
-};
-
-struct lapd_cr_ent {
- uint8_t cmd;
- uint8_t resp;
-};
-
-struct lapd_history {
- struct msgb *msg; /* message to be sent / NULL, if histoy is empty */
- int more; /* if message is fragmented */
-};
-
-/*! LAPD datalink */
-struct lapd_datalink {
- int (*send_dlsap)(struct osmo_dlsap_prim *dp,
- struct lapd_msg_ctx *lctx);
- int (*send_ph_data_req)(struct lapd_msg_ctx *lctx, struct msgb *msg);
- int (*update_pending_frames)(struct lapd_msg_ctx *lctx);
- struct {
- /*! filled-in once we set the lapd_mode above */
- struct lapd_cr_ent loc2rem;
- struct lapd_cr_ent rem2loc;
- } cr;
- enum lapd_mode mode; /*!< current mode of link */
- int use_sabme; /*!< use SABME instead of SABM */
- int reestablish; /*!< enable reestablish support */
- int n200, n200_est_rel; /*!< number of retranmissions */
- struct lapd_msg_ctx lctx; /*!< LAPD context */
- int maxf; /*!< maximum frame size (after defragmentation) */
- uint8_t k; /*!< maximum number of unacknowledged frames */
- uint8_t v_range; /*!< range of sequence numbers */
- uint8_t v_send; /*!< seq nr of next I frame to be transmitted */
- uint8_t v_ack; /*!< last frame ACKed by peer */
- uint8_t v_recv; /*!< seq nr of next I frame expected to be received */
- uint32_t state; /*!< LAPD state (\ref lapd_state) */
- int seq_err_cond; /*!< condition of sequence error */
- uint8_t own_busy; /*!< receiver busy on our side */
- uint8_t peer_busy; /*!< receiver busy on remote side */
- int t200_sec, t200_usec; /*!< retry timer (default 1 sec) */
- int t203_sec, t203_usec; /*!< retry timer (default 10 secs) */
- struct osmo_timer_list t200; /*!< T200 timer */
- struct osmo_timer_list t203; /*!< T203 timer */
- uint8_t retrans_ctr; /*!< re-transmission counter */
- struct llist_head tx_queue; /*!< frames to L1 */
- struct llist_head send_queue; /*!< frames from L3 */
- struct msgb *send_buffer; /*!< current frame transmitting */
- int send_out; /*!< how much was sent from send_buffer */
- struct lapd_history *tx_hist; /*!< tx history structure array */
- uint8_t range_hist; /*!< range of history buffer 2..2^n */
- struct msgb *rcv_buffer; /*!< buffer to assemble the received message */
- struct msgb *cont_res; /*!< buffer to store content resolution data on network side, to detect multiple phones on same channel */
- char *name; /*!< user-provided name */
-};
-
-void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf)
- OSMO_DEPRECATED("Use lapd_dl_init2() instead");
-void lapd_dl_init2(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf, const char *name);
-void lapd_dl_set_name(struct lapd_datalink *dl, const char *name);
-void lapd_dl_exit(struct lapd_datalink *dl);
-void lapd_dl_reset(struct lapd_datalink *dl);
-int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode);
-int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx);
-int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
-
-/*! @} */
+#include <osmocom/isdn/lapd_core.h>
diff --git a/include/osmocom/gsm/lapdm.h b/include/osmocom/gsm/lapdm.h
index 633df1ad..6812102e 100644
--- a/include/osmocom/gsm/lapdm.h
+++ b/include/osmocom/gsm/lapdm.h
@@ -24,6 +24,7 @@ struct lapdm_msg_ctx {
uint8_t link_id;
uint8_t ta_ind; /* TA indicated by network */
uint8_t tx_power_ind; /* MS power indicated by network */
+ uint32_t fn;
};
/*! LAPDm datalink like TS 04.06 / Section 3.5.2 */
@@ -32,6 +33,10 @@ struct lapdm_datalink {
struct lapdm_msg_ctx mctx; /*!< context of established connection */
struct lapdm_entity *entity; /*!< LAPDm entity we are part of */
+
+ struct llist_head tx_ui_queue; /*!< UI frames to L1 */
+ uint32_t t200_fn; /*!< T200 timer in frames */
+ uint32_t t200_timeout; /*!< T200 timeout frame number */
};
/*! LAPDm datalink SAPIs */
@@ -45,6 +50,8 @@ typedef int (*lapdm_cb_t)(struct msgb *msg, struct lapdm_entity *le, void *ctx);
#define LAPDM_ENT_F_EMPTY_FRAME 0x0001
#define LAPDM_ENT_F_POLLING_ONLY 0x0002
+#define LAPDM_ENT_F_DROP_2ND_REJ 0x0004
+#define LAPDM_ENT_F_RTS 0x0008
/*! a LAPDm Entity */
struct lapdm_entity {
@@ -90,7 +97,7 @@ void lapdm_entity_init2(struct lapdm_entity *le, enum lapdm_mode mode,
void lapdm_entity_init3(struct lapdm_entity *le, enum lapdm_mode mode,
const int *t200_ms, int n200, const char *name_pfx);
void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode)
- OSMO_DEPRECATED("Use lapdm_channel_init3() instead");
+ OSMO_DEPRECATED_OUTSIDE("Use lapdm_channel_init3() instead");
int lapdm_channel_init2(struct lapdm_channel *lc, enum lapdm_mode mode,
const int *t200_ms_dcch, const int *t200_ms_acch, enum gsm_chan_t chan_t);
int lapdm_channel_init3(struct lapdm_channel *lc, enum lapdm_mode mode,
@@ -118,6 +125,11 @@ void lapdm_channel_reset(struct lapdm_channel *lc);
void lapdm_entity_set_flags(struct lapdm_entity *le, unsigned int flags);
void lapdm_channel_set_flags(struct lapdm_channel *lc, unsigned int flags);
+void lapdm_entity_set_t200_fn(struct lapdm_entity *le, const uint32_t *t200_fn);
+void lapdm_channel_set_t200_fn(struct lapdm_channel *lc, const uint32_t *t200_fn_dcch, const uint32_t *t200_fn_acch);
+
int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp);
+int lapdm_phsap_dequeue_prim_fn(struct lapdm_entity *le, struct osmo_phsap_prim *pp, uint32_t fn);
+void lapdm_t200_fn(struct lapdm_entity *le, uint32_t fn);
/*! @} */
diff --git a/include/osmocom/gsm/meas_rep.h b/include/osmocom/gsm/meas_rep.h
index 79f9f06b..5628d8ec 100644
--- a/include/osmocom/gsm/meas_rep.h
+++ b/include/osmocom/gsm/meas_rep.h
@@ -11,7 +11,7 @@ struct gsm_rx_lev_qual {
uint8_t rx_qual;
};
-/* unidirectional measumrement report */
+/* unidirectional measurement report */
struct gsm_meas_rep_unidir {
struct gsm_rx_lev_qual full;
struct gsm_rx_lev_qual sub;
@@ -28,5 +28,5 @@ enum meas_rep_field {
MEAS_REP_UL_RXQUAL_SUB,
};
-size_t gsm0858_rsl_ul_meas_enc(struct gsm_meas_rep_unidir *mru, bool dtxd_used,
+size_t gsm0858_rsl_ul_meas_enc(const struct gsm_meas_rep_unidir *mru, bool dtxd_used,
uint8_t *buf);
diff --git a/include/osmocom/gsm/prim.h b/include/osmocom/gsm/prim.h
index 045e353b..73cd7f71 100644
--- a/include/osmocom/gsm/prim.h
+++ b/include/osmocom/gsm/prim.h
@@ -18,4 +18,7 @@ enum osmo_gsm_sap {
SAP_NS,
SAP_BSSGP_RIM,
+
+ SAP_IUUP_TNL,
+ SAP_IUUP_RNL,
};
diff --git a/include/osmocom/gsm/protocol/Makefile.am b/include/osmocom/gsm/protocol/Makefile.am
new file mode 100644
index 00000000..23429dcd
--- /dev/null
+++ b/include/osmocom/gsm/protocol/Makefile.am
@@ -0,0 +1,30 @@
+osmogsmproto_HEADERS = \
+ gsm_23_032.h \
+ gsm_03_40.h \
+ gsm_03_41.h \
+ gsm_04_08.h \
+ gsm_04_08_gprs.h \
+ gsm_04_11.h \
+ gsm_04_12.h \
+ gsm_04_14.h \
+ gsm_04_80.h \
+ gsm_08_08.h \
+ gsm_08_58.h \
+ gsm_09_02.h \
+ gsm_12_21.h \
+ gsm_23_003.h \
+ gsm_23_041.h \
+ gsm_25_415.h \
+ gsm_29_118.h \
+ gsm_44_004.h \
+ gsm_44_060.h \
+ gsm_44_068.h \
+ gsm_44_318.h \
+ gsm_48_049.h \
+ gsm_48_071.h \
+ gsm_49_031.h \
+ ipaccess.h \
+ smpp34_osmocom.h \
+ $(NULL)
+
+osmogsmprotodir = $(includedir)/osmocom/gsm/protocol
diff --git a/include/osmocom/gsm/protocol/gsm_03_41.h b/include/osmocom/gsm/protocol/gsm_03_41.h
index 1b399aee..f6c36cad 100644
--- a/include/osmocom/gsm/protocol/gsm_03_41.h
+++ b/include/osmocom/gsm/protocol/gsm_03_41.h
@@ -25,7 +25,7 @@ struct gsm341_ms_message {
uint8_t update:4;
uint8_t code_lo:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t gs:2, code_hi:6;
uint8_t code_lo:4, update:4;
#endif
@@ -36,7 +36,7 @@ struct gsm341_ms_message {
uint8_t language:4;
uint8_t group:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t group:4, language:4;
#endif
} dcs;
@@ -45,7 +45,7 @@ struct gsm341_ms_message {
uint8_t total:4;
uint8_t current:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t current:4, total:4;
#endif
} page;
@@ -63,7 +63,7 @@ struct gsm341_etws_message {
uint8_t update:4;
uint8_t code_lo:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t gs:2, alert:1, popup:1, code_hi:4;
uint8_t code_lo:4, update:4;
#endif
diff --git a/include/osmocom/gsm/protocol/gsm_04_08.h b/include/osmocom/gsm/protocol/gsm_04_08.h
index 3fa94191..075b6ca6 100644
--- a/include/osmocom/gsm/protocol/gsm_04_08.h
+++ b/include/osmocom/gsm/protocol/gsm_04_08.h
@@ -22,7 +22,7 @@ struct gsm48_classmark1 {
rev_lev:2,
spare:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:1, rev_lev:2, es_ind:1, a5_1:1, pwr_lev:3;
#endif
} __attribute__ ((packed));
@@ -51,7 +51,7 @@ struct gsm48_classmark2 {
spare4:1,
cm3:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:1, rev_lev:2, es_ind:1, a5_1:1, pwr_lev:3;
uint8_t spare2:1, ps_cap:1, ss_scr:2, sm_cap:1, vbs:1, vgcs:1, fc:1;
uint8_t cm3:1, spare4:1, lcsva_cap:1, spare3:1, solsa:1, cmsp:1, a5_3:1, a5_2:1;
@@ -280,7 +280,7 @@ struct gsm48_range_1024 {
uint8_t w16:6,
w15_lo:2;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t form_id:5, f0:1, w1_hi:2;
uint8_t w1_lo;
uint8_t w2_hi;
@@ -335,7 +335,7 @@ struct gsm48_range_512 {
uint8_t w17:5,
w16_lo:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t form_id:7, orig_arfcn_hi:1;
uint8_t orig_arfcn_mid;
uint8_t orig_arfcn_lo:1, w1_hi:7;
@@ -396,7 +396,7 @@ struct gsm48_range_256 {
w21:4,
w20_lo:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t form_id:7, orig_arfcn_hi:1;
uint8_t orig_arfcn_mid;
uint8_t orig_arfcn_lo:1, w1_hi:7;
@@ -459,7 +459,7 @@ struct gsm48_range_128 {
w27:3,
w26_lo:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t form_id:7, orig_arfcn_hi:1;
uint8_t orig_arfcn_mid;
uint8_t orig_arfcn_lo:1, w1:7;
@@ -489,7 +489,7 @@ struct gsm48_var_bit {
orig_arfcn_lo:1;
uint8_t rrfcn8_111[13];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t form_id:7, orig_arfcn_hi:1;
uint8_t orig_arfcn_mid;
uint8_t orig_arfcn_lo:1, rrfcn1_7:7;
@@ -509,7 +509,7 @@ struct gsm48_chan_desc {
uint8_t hsn:6,
maio_low:2;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t tsc:3, h:1, maio_high:4;
uint8_t maio_low:2, hsn:6;
#endif
@@ -522,7 +522,7 @@ struct gsm48_chan_desc {
tsc:3;
uint8_t arfcn_low;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t tsc:3, h:1, spare:2, arfcn_high:2;
uint8_t arfcn_low;
#endif
@@ -573,7 +573,7 @@ struct gsm48_meas_res {
uint8_t bsic_nc6:6,
bcch_f_nc6_lo:2;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t ba_used:1, dtx_used:1, rxlev_full:6;
uint8_t spare:1, meas_valid:1, rxlev_sub:6;
uint8_t spare2:1, rxqual_full:3, rxqual_sub:3, no_nc_n_hi:1;
@@ -593,6 +593,12 @@ struct gsm48_meas_res {
#endif
} __attribute__ ((packed));
+/*! Check if the given mr contains valid measurement results */
+static inline bool gsm48_meas_res_is_valid(const struct gsm48_meas_res *mr)
+{
+ return (mr->meas_valid == 0); /* 0 means valid */
+}
+
/* Chapter 10.5.2.21aa */
struct gsm48_multi_rate_conf {
#if OSMO_IS_LITTLE_ENDIAN
@@ -610,7 +616,7 @@ struct gsm48_multi_rate_conf {
m10_2 : 1,
m12_2 : 1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t ver:3, nscb:1, icmi:1, spare:1, smod:2;
uint8_t m12_2:1, m10_2:1, m7_95:1, m7_40:1, m6_70:1, m5_90:1, m5_15:1, m4_75:1;
#endif
@@ -623,7 +629,7 @@ struct gsm48_power_cmd {
spare:2,
atc:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t atc:1, spare:2, power_level:5;
#endif
} __attribute__((packed));
@@ -638,7 +644,7 @@ struct gsm48_rach_control {
uint8_t t2; /* ACC 8-15 barred flags */
uint8_t t3; /* ACC 0-7 barred flags */
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t max_trans:2, tx_integer:4, cell_bar:1, re:1;
uint8_t t2;
uint8_t t3;
@@ -696,7 +702,7 @@ struct gsm48_req_ref {
uint8_t t2:5,
t3_low:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t ra;
uint8_t t1:5, t3_high:3;
uint8_t t3_low:3, t2:5;
@@ -711,7 +717,7 @@ struct gsm48_start_time {
uint8_t t2:5,
t3_low:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t t1:5, t3_high:3;
uint8_t t3_low:3, t2:5;
#endif
@@ -725,7 +731,7 @@ struct gsm48_sync_ind {
nci:1,
sync_ie:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t sync_ie:4, nci:1, rot:1, si:2;
#endif
} __attribute__((packed));
@@ -740,18 +746,58 @@ struct gsm48_chan_mode_modify {
uint8_t mode;
} __attribute__ ((packed));
+/*! 10.5.2.6 Channel Mode value */
enum gsm48_chan_mode {
+ /*! Signalling only (TCH/F or TCH/H) */
GSM48_CMODE_SIGN = 0x00,
+ /*! Speech: FR (TCH/FS) or HR (TCH/HS) */
GSM48_CMODE_SPEECH_V1 = 0x01,
- GSM48_CMODE_SPEECH_EFR = 0x21,
- GSM48_CMODE_SPEECH_AMR = 0x41,
+ /*! Speech: EFR (TCH/EFS) */
+ GSM48_CMODE_SPEECH_EFR = 0x21, /*!< a.k.a. V2 */
+ /*! Speech: AMR (TCH/AFS or TCH/AHS) */
+ GSM48_CMODE_SPEECH_AMR = 0x41, /*!< a.k.a. V3 */
+ /*! Speech: OFR AMR-WB (O-TCH/WFS) or OHR AMR-WB (O-TCH/WHS) */
+ GSM48_CMODE_SPEECH_V4 = 0x81,
+ /*! Speech: FR AMR-WB (TCH/WFS) */
+ GSM48_CMODE_SPEECH_V5 = 0x82,
+ /*! Speech: OHR AMR (O-TCH/AHS) */
+ GSM48_CMODE_SPEECH_V6 = 0x83,
+
+ /* ECSD: 43.5 kbit/s (DL) + 14.5 kbit/s (UL) */
+ GSM48_CMODE_DATA_43k5_14k5 = 0x61,
+ /* ECSD: 29.0 kbit/s (DL) + 14.5 kbit/s (UL) */
+ GSM48_CMODE_DATA_29k0_14k5 = 0x62,
+ /* ECSD: 43.5 kbit/s (DL) + 29.0 kbit/s (UL) */
+ GSM48_CMODE_DATA_43k5_29k0 = 0x64,
+ /* ECSD: 14.5 kbit/s (DL) + 43.5 kbit/s (UL) */
+ GSM48_CMODE_DATA_14k5_43k5 = 0x67,
+ /* ECSD: 14.5 kbit/s (DL) + 29.0 kbit/s (UL) */
+ GSM48_CMODE_DATA_14k5_29k0 = 0x65,
+ /* ECSD: 29.0 kbit/s (DL) + 43.5 kbit/s (UL) */
+ GSM48_CMODE_DATA_29k0_43k5 = 0x66,
+
+ /*! ECSD: 43.5 kbit/s radio interface rate, 43.2 kbit/s services (E-TCH/F43.2) */
+ GSM48_CMODE_DATA_43k5 = 0x27,
+ /*! ECSD: 32.0 kbit/s radio interface rate, 32.0 kbit/s services (E-TCH/F32.0) */
+ GSM48_CMODE_DATA_32k0 = 0x63,
+ /*! ECSD: 29.0 kbit/s radio interface rate, 28.8 kbit/s services (E-TCH/F28.8) */
+ GSM48_CMODE_DATA_29k0 = 0x43,
+ /*! CSD: 14.5 kbit/s radio interface rate, 14.4 kbit/s services (TCH/F14.4) */
GSM48_CMODE_DATA_14k5 = 0x0f,
+ /*! CSD: 12.0 kbit/s radio interface rate, 9.6 kbit/s services (TCH/F9.6) */
GSM48_CMODE_DATA_12k0 = 0x03,
+ /*! CSD: 6.0 kbit/s radio interface rate, 4.8 kbit/s services (TCH/{F,H}4.8) */
GSM48_CMODE_DATA_6k0 = 0x0b,
+ /*! CSD: 3.6 kbit/s radio interface rate, 2.4 kbit/s and less services (TCH/{F,H}2.4) */
GSM48_CMODE_DATA_3k6 = 0x13,
+
+ /*! Same as GSM48_CMODE_SPEECH_V1, in VAMOS mode */
GSM48_CMODE_SPEECH_V1_VAMOS = 0xc1,
+ /*! Same as GSM48_CMODE_SPEECH_EFR, in VAMOS mode */
GSM48_CMODE_SPEECH_V2_VAMOS = 0xc2,
+ /*! Same as GSM48_CMODE_SPEECH_AMR, in VAMOS mode */
GSM48_CMODE_SPEECH_V3_VAMOS = 0xc3,
+ /*! Speech: GSM48_CMODE_SPEECH_V5, in VAMOS mode */
GSM48_CMODE_SPEECH_V5_VAMOS = 0xc5,
};
@@ -791,7 +837,7 @@ struct gsm48_cell_desc {
arfcn_hi:2;
uint8_t arfcn_lo;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t arfcn_hi:2, ncc:3, bcc:3;
uint8_t arfcn_lo;
#endif
@@ -834,7 +880,7 @@ struct gsm48_pag_resp {
uint8_t mi_len;
uint8_t mi[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t key_seq:4, spare:4;
union {
uint32_t classmark2;
@@ -861,7 +907,7 @@ struct gsm48_auth_req {
spare:4;
uint8_t rand[16];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:4, key_seq:4;
uint8_t rand[16];
#endif
@@ -882,7 +928,7 @@ struct gsm48_loc_upd_req {
uint8_t mi_len;
uint8_t mi[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t key_seq:4, type:4;
struct gsm48_loc_area_id lai;
struct gsm48_classmark1 classmark1;
@@ -898,6 +944,20 @@ struct gsm48_hdr {
uint8_t data[0];
} __attribute__ ((packed));
+/* Short header */
+struct gsm48_hdr_sh {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t l2_header:2, /* < short layer 2 header : bit(2) > See 3GPP TS 44.006 §6.4a */
+ msg_type:5, /* < message type : bit(5) > See 3GPP TS 44.018 Table 10.4.2 */
+ rr_short_pd:1; /* < RR short PD : bit > See 3GPP TS 24.007 §11.3.2 */
+ uint8_t data[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t rr_short_pd:1, msg_type:5, l2_header:2;
+ uint8_t data[0];
+#endif
+} __attribute__ ((packed));
+
/* Section 9.1.3x System information Type header */
struct gsm48_system_information_type_header {
#if OSMO_IS_LITTLE_ENDIAN
@@ -906,7 +966,7 @@ struct gsm48_system_information_type_header {
skip_indicator:4;
uint8_t system_information;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t l2_plen;
uint8_t skip_indicator:4, rr_protocol_discriminator:4;
uint8_t system_information;
@@ -922,7 +982,7 @@ struct gsm48_cell_sel_par {
neci:1,
acs:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t cell_resel_hyst:3, ms_txpwr_max_ccch:5;
uint8_t acs:1, neci:1, rxlev_acc_min:6;
#endif
@@ -941,7 +1001,7 @@ struct gsm48_control_channel_descr {
spare_2 :1;
uint8_t t3212;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t mscr:1, att:1, bs_ag_blks_res:3, ccch_conf:3;
uint8_t spare_2:1, cbq3:2, spare_1:2, bs_pa_mfrms:3;
uint8_t t3212;
@@ -964,7 +1024,7 @@ struct gsm48_cell_options {
/* either DN-IND or top bit of DTX IND */
d:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t d:1, pwrc:1, dtx:2, radio_link_timeout:4;
#endif
} __attribute__ ((packed));
@@ -985,7 +1045,7 @@ struct gsm48_service_request {
uint8_t mi[0];
/* optional priority level */
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t cipher_key_seq:4, cm_service_type:4;
union {
uint32_t classmark;
@@ -1068,7 +1128,7 @@ struct gsm48_system_information_type_5 {
uint8_t system_information;
uint8_t bcch_frequency_list[16];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t skip_indicator:4, rr_protocol_discriminator:4;
uint8_t system_information;
uint8_t bcch_frequency_list[16];
@@ -1083,7 +1143,7 @@ struct gsm48_system_information_type_5bis {
uint8_t system_information;
uint8_t bcch_frequency_list[16];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t skip_indicator:4, rr_protocol_discriminator:4;
uint8_t system_information;
uint8_t bcch_frequency_list[16];
@@ -1098,7 +1158,7 @@ struct gsm48_system_information_type_5ter {
uint8_t system_information;
uint8_t bcch_frequency_list[16];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t skip_indicator:4, rr_protocol_discriminator:4;
uint8_t system_information;
uint8_t bcch_frequency_list[16];
@@ -1117,7 +1177,7 @@ struct gsm48_system_information_type_6 {
uint8_t ncc_permitted;
uint8_t rest_octets[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t skip_indicator:4, rr_protocol_discriminator:4;
uint8_t system_information;
uint16_t cell_identity;
@@ -1128,6 +1188,27 @@ struct gsm48_system_information_type_6 {
#endif
} __attribute__ ((packed));
+/* Section 9.1.50 System Information type 10 (ASCI) */
+struct gsm48_system_information_type_10 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t l2_header:2, /* < short layer 2 header : bit(2) > See 3GPP TS 44.006 §6.4a */
+ msg_type:5, /* < message type : bit(5) > See 3GPP TS 44.018 Table 10.4.2 */
+ rr_short_pd:1; /* < RR short PD : bit > See 3GPP TS 24.007 §11.3.2 */
+ uint8_t rest_octets[0]; /* < SI10 Rest Octets : bit(160) > See 3GPP TS 44.018 §10.5.2.44 */
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t rr_short_pd:1, msg_type:5, l2_header:2;
+ uint8_t rest_octets[0];
+#endif
+} __attribute__ ((packed));
+
+/* TS 44.018 Section 9.1.49 */
+struct gsm0408_vgcs_ul_grant {
+ struct gsm48_hdr hdr;
+ struct gsm48_req_ref req_ref;
+ uint8_t ta;
+} __attribute__ ((packed));
+
/* Section 9.1.43a System Information type 13 */
struct gsm48_system_information_type_13 {
struct gsm48_system_information_type_header header;
@@ -1183,7 +1264,7 @@ struct gsm48_cip_mode_cmd {
cr:1,
spare:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:3, cr:1, alg_id:3, sc:1;
#endif
} __attribute__((packed));
@@ -1228,6 +1309,14 @@ struct gsm48_imm_ass_rej {
uint8_t rest[0];
} __attribute__ ((packed));
+/* Section 9.1.21b */
+struct gsm48_notification_nch {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t data[0];
+} __attribute__((packed));
+
/* Section 9.1.22 */
struct gsm48_paging1 {
#if OSMO_IS_LITTLE_ENDIAN
@@ -1240,7 +1329,7 @@ struct gsm48_paging1 {
cneed2:2;
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t l2_plen;
uint8_t proto_discr;
uint8_t msg_type;
@@ -1263,7 +1352,7 @@ struct gsm48_paging2 {
uint32_t tmsi2;
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t l2_plen;
uint8_t proto_discr;
uint8_t msg_type;
@@ -1293,7 +1382,7 @@ struct gsm48_paging3 {
spare2:4;
uint8_t rest[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t l2_plen;
uint8_t proto_discr;
uint8_t msg_type;
@@ -1316,7 +1405,7 @@ struct gsm48_pag_rsp {
struct gsm48_classmark2 cm2;
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:5, key_seq:3;
uint8_t cm2_len;
struct gsm48_classmark2 cm2;
@@ -1329,6 +1418,18 @@ struct gsm48_rr_status {
uint8_t rr_cause;
} __attribute__((packed));
+/* Section 9.1.44 */
+struct gsm48_talker_indication {
+ uint8_t cm2_len;
+ struct gsm48_classmark2 cm2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.48 */
+struct gsm48_uplink_release {
+ uint8_t rr_cause;
+} __attribute__((packed));
+
/* Section 10.2 + GSM 04.07 12.2.3.1.1 + 3GPP TS 24.007 11.2.3.1.1 */
#define GSM48_PDISC_GROUP_CC 0x00
#define GSM48_PDISC_BCAST_CC 0x01
@@ -1347,6 +1448,9 @@ struct gsm48_rr_status {
#define GSM48_PDISC_TEST 0x0f /* as per 11.10, 04.14 */
#define GSM48_PDISC_MASK 0x0f
+/* Section 11.3.2.1 3GPP TS 24.007: Short PDISC */
+#define GSM48_PDISC_SH_RR 0
+
extern const struct value_string gsm48_pdisc_names[];
static inline const char *gsm48_pdisc_name(uint8_t val)
{ return get_value_string(gsm48_pdisc_names, val); }
@@ -1534,6 +1638,21 @@ void gsm48_set_dtx(struct gsm48_cell_options *op, enum gsm48_dtx_mode full,
#define GSM48_MT_RR_APP_INFO 0x38
+/* 3GPP TS 44.018 Table 10.4.2 */
+#define GSM48_MT_RR_SH_SI10 0x0
+#define GSM48_MT_RR_SH_FACCH 0x1
+#define GSM48_MT_RR_SH_UL_FREE 0x2
+#define GSM48_MT_RR_SH_MEAS_REP 0x4
+#define GSM48_MT_RR_SH_MEAS_INFO 0x5
+#define GSM48_MT_RR_SH_VGCS_RECON 0x6
+#define GSM48_MT_RR_SH_VGCS_RECON2 0x7
+#define GSM48_MT_RR_SH_VGCS_INFO 0x8
+#define GSM48_MT_RR_SH_VGCS_SMS 0x9
+#define GSM48_MT_RR_SH_SI10bis 0xA
+#define GSM48_MT_RR_SH_SI10ter 0xB
+#define GSM48_MT_RR_SH_VGCS_NEIGH 0xC
+#define GSM48_MT_RR_SH_APP_DATA 0xD
+
/* Table 10.2/3GPP TS 04.08 */
#define GSM48_MT_MM_IMSI_DETACH_IND 0x01
#define GSM48_MT_MM_LOC_UPD_ACCEPT 0x02
@@ -1699,6 +1818,10 @@ static inline const char *osmo_lu_type_name(uint8_t lu_type)
#define GSM48_IE_FRQSHORT_AFTER 0x02
#define GSM48_IE_MUL_RATE_CFG 0x03 /* 10.5.2.21aa */
#define GSM48_IE_FREQ_L_AFTER 0x05
+#define GSM48_IE_GROUP_CIP_SEQ_HO 0x08 /* HO = Half Octet Tag */
+#define GSM48_IE_CIP_MODE_SET_HO 0x09 /* HO = Half Octet Tag */
+#define GSM48_IE_GPRS_RESUMPT_HO 0xc0 /* HO = Half Octet Tag */
+#define GSM48_IE_SYNC_IND_HO 0x0d /* HO = Half Octet Tag */
#define GSM48_IE_MSLOT_DESC 0x10
#define GSM48_IE_CHANMODE_2 0x11
#define GSM48_IE_FRQSHORT_BEFORE 0x12
@@ -1740,20 +1863,21 @@ static inline const char *osmo_lu_type_name(uint8_t lu_type)
#define GSM48_IE_START_TIME 0x7c
#define GSM48_IE_INDIVIDUAL_PRIORITIES 0x7c /* 44.018 Section 9.1.7 */
#define GSM48_IE_TIMING_ADVANCE 0x7d
-#define GSM48_IE_GROUP_CIP_SEQ 0x80
-#define GSM48_IE_CIP_MODE_SET 0x90
-#define GSM48_IE_GPRS_RESUMPT 0xc0
-#define GSM48_IE_SYNC_IND 0xd0
+#define GSM48_IE_GROUP_CIP_SEQ 0x80 /* DEPRECATED, use GSM48_IE_GROUP_CIP_SEQ_HO instead */
+#define GSM48_IE_CIP_MODE_SET 0x90 /* DEPRECATED, use GSM48_IE_CIP_MODE_SET_HO instead */
+#define GSM48_IE_GPRS_RESUMPT 0xc0 /* DEPRECATED, use GSM48_IE_GPRS_RESUMPT_HO instead */
+#define GSM48_IE_SYNC_IND 0xd0 /* DEPRECATED, use GSM48_IE_SYNC_IND_HO instead */
/* System Information 4 (types are equal IEs above) */
#define GSM48_IE_CBCH_CHAN_DESC 0x64
#define GSM48_IE_CBCH_MOB_AL 0x72
/* Additional MM elements */
+#define GSM48_IE_PRIORITY_LEV_HO 0x08 /* HO = Half Octet Tag */
#define GSM48_IE_LOCATION_AREA 0x13
#define GSM48_IE_AUTN 0x20
#define GSM48_IE_AUTH_RES_EXT 0x21
#define GSM48_IE_AUTS 0x22
-#define GSM48_IE_PRIORITY_LEV 0x80
+#define GSM48_IE_PRIORITY_LEV 0x80 /* DEPRECATED, use GSM48_IE_PRIORITY_LEV_HO instead */
#define GSM48_IE_FOLLOW_ON_PROC 0xa1
#define GSM48_IE_CTS_PERMISSION 0xa2
@@ -2086,8 +2210,31 @@ enum gsm48_bcap_modem_type {
GSM48_BCAP_MT_AUTO_1 = 8,
};
+/*! Table 10.5.112/3GPP TS 24.008: Other modem type (octet 6d) */
+enum gsm48_bcap_other_modem_type {
+ GSM48_BCAP_OTHER_MT_NONE = 0, /*!< No other modem type specified */
+ GSM48_BCAP_OTHER_MT_V34 = 2, /*!< According to ITU-T Rec. V.34 */
+};
+
+/*! Table 10.5.112/3GPP TS 24.008: Fixed network user rate (octet 6d) */
+enum gsm48_bcap_fixed_net_user_rate {
+ GSM48_BCAP_FNUR_NONE = 0, /*!< FNUR not applicable / No meaning associated */
+ GSM48_BCAP_FNUR_X1_V110_9600 = 1, /*!< 9.6 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_X1_V110_14400 = 2, /*!< 14.4 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_X1_V110_19200 = 3, /*!< 19.2 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_X1_V110_28800 = 4, /*!< 28.8 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_X1_V110_38400 = 5, /*!< 38.4 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_X1_V110_48000 = 6, /*!< 48.0 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_X1_V110_56000 = 7, /*!< 56.0 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_BT_64000 = 8, /*!< 64.0 kbit/s bit transparent */
+ GSM48_BCAP_FNUR_BT_33600 = 9, /*!< 33.6 kbit/s bit transparent */
+ GSM48_BCAP_FNUR_I460_32000 = 10, /*!< 32.0 kbit/s (according to ITU-T Rec. I.460) */
+ GSM48_BCAP_FNUR_V34_31200 = 11, /*!< 31.2 kbit/s (according to ITU-T Rec. V.34) */
+};
+
/*! GSM 04.08 Bearer Capability: Speech Version Indication
- * (See also 3GPP TS 24.008, Table 10.5.103) */
+ * (See also 3GPP TS 24.008, Table 10.5.103
+ * and 3GPP TS 26.103, Table 4.1 "Support of Codec Types in Radio Access Technologies") */
enum gsm48_bcap_speech_ver {
GSM48_BCAP_SV_FR = 0, /*!< GSM FR V1 (GSM FR) */
GSM48_BCAP_SV_HR = 1, /*!< GSM HR V1 (GSM HR) */
diff --git a/include/osmocom/gsm/protocol/gsm_04_08_gprs.h b/include/osmocom/gsm/protocol/gsm_04_08_gprs.h
index dbac2597..2671aafb 100644
--- a/include/osmocom/gsm/protocol/gsm_04_08_gprs.h
+++ b/include/osmocom/gsm/protocol/gsm_04_08_gprs.h
@@ -1,7 +1,6 @@
/*! \file gsm_04_08_gprs.h */
-#ifndef _GSM48_GPRS_H
-#define _GSM48_GPRS_H
+#pragma once
#include <stdint.h>
#include <stdbool.h>
@@ -83,6 +82,8 @@ extern const struct value_string *gprs_upd_t_strs;
enum gsm48_gprs_ie_mm {
GSM48_IE_GMM_CIPH_CKSN = 0x08, /* 10.5.1.2 */
+ GSM48_IE_GMM_PTMSI_TYPE = 0x0e, /* 10.5.5.29 */
+ GSM48_IE_GMM_TMSI_BASED_NRI_C = 0x10, /* 10.5.5.31 */
GSM48_IE_GMM_TIMER_READY = 0x17, /* 10.5.7.3 */
GSM48_IE_GMM_ALLOC_PTMSI = 0x18, /* 10.5.1.4 */
GSM48_IE_GMM_PTMSI_SIG = 0x19, /* 10.5.5.8 */
@@ -90,27 +91,39 @@ enum gsm48_gprs_ie_mm {
GSM48_IE_GMM_AUTH_SRES = 0x22, /* 10.5.3.2 */
GSM48_IE_GMM_IMEISV = 0x23, /* 10.5.1.4 */
GSM48_IE_GMM_CAUSE = 0x25, /* 10.5.5.14 */
+ GSM48_IE_GMM_RX_NPDU_NUM_LIST = 0x26, /* 10.5.5.11 */
GSM48_IE_GMM_DRX_PARAM = 0x27, /* 10.5.5.6 */
GSM48_IE_GMM_AUTN = 0x28, /* 10.5.3.1.1 */
GSM48_IE_GMM_AUTH_RES_EXT = 0x29, /* 10.5.3.2.1 */
+ GSM48_IE_GMM_TIMER_T3302 = 0x2A, /* 10.5.7.4 */
GSM48_IE_GMM_AUTH_FAIL_PAR = 0x30, /* 10.5.3.2.2 */
GSM48_IE_GMM_MS_NET_CAPA = 0x31, /* 10.5.5.12 */
GSM48_IE_GMM_PDP_CTX_STATUS = 0x32, /* 10.5.7.1 */
GSM48_IE_GMM_PS_LCS_CAPA = 0x33, /* 10.5.5.22 */
GSM48_IE_GMM_GMM_MBMS_CTX_ST = 0x35, /* 10.5.7.6 */
+ GSM48_IE_GMM_TIMER_T3346 = 0x3A, /* 10.5.7.4 */
GSM48_IE_GMM_NET_FEAT_SUPPORT = 0xB0, /* 10.5.5.23 */
};
enum gsm48_gprs_ie_sm {
+ GSM48_IE_GSM_RADIO_PRIO = 0x08, /* 10.5.7.2 */
+ GSM48_IE_GSM_DEV_PROP = 0x0C, /* 10.5.7.8 */
GSM48_IE_GSM_APN = 0x28, /* 10.5.6.1 */
GSM48_IE_GSM_PROTO_CONF_OPT = 0x27, /* 10.5.6.3 */
GSM48_IE_GSM_PDP_ADDR = 0x2b, /* 10.5.6.4 */
GSM48_IE_GSM_AA_TMR = 0x29, /* 10.5.7.3 */
+ GSM48_IE_GSM_QOS = 0x30, /* 10.5.6.5 */
+ GSM48_IE_GSM_TFT = 0x31, /* 10.5.6.12 */
+ GSM48_IE_GSM_LLC_SAPI = 0x32, /* 10.5.6.9 */
+ GSM48_IE_GSM_MBIFOM_CONT = 0x33, /* 10.5.6.21 */
+ GSM48_IE_GSM_PFI = 0x34, /* 10.5.6.11 */
GSM48_IE_GSM_NAME_FULL = 0x43, /* 10.5.3.5a */
GSM48_IE_GSM_NAME_SHORT = 0x45, /* 10.5.3.5a */
GSM48_IE_GSM_TIMEZONE = 0x46, /* 10.5.3.8 */
GSM48_IE_GSM_UTC_AND_TZ = 0x47, /* 10.5.3.9 */
GSM48_IE_GSM_LSA_ID = 0x48, /* 10.5.3.11 */
+ GSM48_IE_GSM_EXT_QOS = 0x5C, /* 10.5.6.5B */
+ GSM48_IE_GSM_EXT_PROTO_CONF_OPT = 0x7B, /* 10.5.6.3a */
/* Fake IEs that are not present on the Layer3 air interface,
* but which we use to simplify internal APIs */
@@ -129,7 +142,7 @@ struct gsm48_ra_upd_ack {
struct gsm48_ra_id ra_id; /* 10.5.5.15 */
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t upd_result:4, force_stby:4;
uint8_t ra_upd_timer;
struct gsm48_ra_id ra_id;
@@ -158,7 +171,7 @@ struct gsm48_attach_ack {
struct gsm48_ra_id ra_id; /* 10.5.5.15 */
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t force_stby:4, att_result:4;
uint8_t ra_upd_timer;
uint8_t radio_prio;
@@ -176,7 +189,7 @@ struct gsm48_auth_ciph_req {
ac_ref_nr:4; /* 10.5.5.19 */
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t imeisv_req:4, ciph_alg:4;
uint8_t ac_ref_nr:4, force_stby:4;
uint8_t data[0];
@@ -190,7 +203,7 @@ struct gsm48_auth_ciph_resp {
spare:4;
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:4, ac_ref_nr:4;
uint8_t data[0];
#endif
@@ -281,17 +294,52 @@ enum gsm48_pdp_state {
PDP_S_MODIFY_PENDING,
};
-/* Table 10.5.155/3GPP TS 24.008 */
+/* TS 24.008 Table 10.5.155/3GPP */
enum gsm48_pdp_type_org {
PDP_TYPE_ORG_ETSI = 0x00,
PDP_TYPE_ORG_IETF = 0x01,
+ PDP_TYPE_ORG_EMPTY = 0x0f,
};
enum gsm48_pdp_type_nr {
PDP_TYPE_N_ETSI_RESERVED = 0x00,
PDP_TYPE_N_ETSI_PPP = 0x01,
PDP_TYPE_N_IETF_IPv4 = 0x21,
PDP_TYPE_N_IETF_IPv6 = 0x57,
+ PDP_TYPE_N_IETF_IPv4v6 = 0x8D,
};
+/* TS 24.008 10.5.6.4 "Packet data protocol address" value
+ * Note: This can be reused for 3GPP TS 29.060 7.7.27 "End User Address"
+ * with minor changes in the values, like spare being 1111 instead.
+*/
+struct gsm48_pdp_address {
+#if OSMO_IS_LITTLE_ENDIAN
+uint8_t organization:4, /* enum gsm48_pdp_type_org */
+ spare:4; /* 0000 */
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+uint8_t spare:4, organization:4;
+#endif
+uint8_t type; /* enum gsm48_pdp_type_nr */
+ union {
+ /* PDP_TYPE_ORG_ETSI */
+ union {
+ } etsi;
+ /* PDP_TYPE_ORG_IETF */
+ union {
+ /* PDP_TYPE_N_IETF_IPv4 */
+ uint32_t v4;
+
+ /* PDP_TYPE_N_IETF_IPv6 */
+ uint8_t v6[16];
+
+ /* PDP_TYPE_N_IETF_IPv4v6 */
+ struct {
+ uint32_t v4;
+ uint8_t v6[16];
+ } __attribute__ ((packed)) v4v6;
+ } ietf;
+ };
+} __attribute__ ((packed));
/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
enum gsm48_qos_reliab_class {
@@ -464,6 +512,3 @@ struct gsm48_qos {
/* octet 16 */
uint8_t guar_bitrate_down_ext;
};
-
-
-#endif /* _GSM48_GPRS_H */
diff --git a/include/osmocom/gsm/protocol/gsm_04_11.h b/include/osmocom/gsm/protocol/gsm_04_11.h
index 31f25acb..90543020 100644
--- a/include/osmocom/gsm/protocol/gsm_04_11.h
+++ b/include/osmocom/gsm/protocol/gsm_04_11.h
@@ -62,6 +62,16 @@ enum gsm411_rp_ie {
GSM411_IE_RP_CAUSE = 0x42, /* 8.2.5.4 */
};
+/* Sections 8.2.5.1 and 8.2.5.2 set limits on the length of an SMSC-address.
+ * The spec states these limits in terms of min and max values of the length
+ * octet in type 4 IEs SM-RP-OA and SM-RP-DA; these IE length limits translate
+ * into a minimum of 1 digit and a maximum of 20 digits.
+ */
+#define GSM411_SMSC_ADDR_MIN_OCTETS 2
+#define GSM411_SMSC_ADDR_MAX_OCTETS 11
+#define GSM411_SMSC_ADDR_MIN_DIGITS 1
+#define GSM411_SMSC_ADDR_MAX_DIGITS 20
+
/* Chapter 8.2.5.4 Table 8.4 */
enum gsm411_rp_cause {
/* valid only for MO */
diff --git a/include/osmocom/gsm/protocol/gsm_04_12.h b/include/osmocom/gsm/protocol/gsm_04_12.h
index 3f34ee7f..17ac6454 100644
--- a/include/osmocom/gsm/protocol/gsm_04_12.h
+++ b/include/osmocom/gsm/protocol/gsm_04_12.h
@@ -23,7 +23,7 @@ struct gsm412_block_type {
lpd : 2,
spare : 1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:1, lpd:2, lb:1, seq_nr:4;
#endif
} __attribute__((packed));
@@ -37,7 +37,7 @@ struct gsm412_sched_msg {
uint8_t cbsms_msg_map[6];
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t type:2, beg_slot_nr:6;
uint8_t spare2:1, spare1:1, end_slot_nr:6;
uint8_t cbsms_msg_map[6];
diff --git a/include/osmocom/gsm/protocol/gsm_04_14.h b/include/osmocom/gsm/protocol/gsm_04_14.h
index deb474ec..dddec519 100644
--- a/include/osmocom/gsm/protocol/gsm_04_14.h
+++ b/include/osmocom/gsm/protocol/gsm_04_14.h
@@ -30,7 +30,7 @@ struct gsm414_close_mslot_loop_cmd {
loop_mech:3,
tn:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t tn:3, loop_mech:3, chc:2;
#endif
} __attribute__((packed));
@@ -43,7 +43,7 @@ struct gsm414_close_mslot_loop_ack {
chc:2,
spare:2;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:2, chc:2, loop_mech:3, err_ind:1;
#endif
} __attribute__((packed));
@@ -70,7 +70,7 @@ struct gsm414_gprs_test_mode_cmd {
dl_tx_offset:3,
_spare:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint16_t d:12,
spare:3,
l:1;
@@ -86,7 +86,7 @@ struct gsm414_egprs_st_sb_loop_cmd {
dl_tx_offset:3,
m:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t m:1, dl_tx_offset:3, _spare:4;
#endif
} __attribute__((packed));
diff --git a/include/osmocom/gsm/protocol/gsm_08_08.h b/include/osmocom/gsm/protocol/gsm_08_08.h
index 983783ee..1e211dc9 100644
--- a/include/osmocom/gsm/protocol/gsm_08_08.h
+++ b/include/osmocom/gsm/protocol/gsm_08_08.h
@@ -25,9 +25,10 @@ enum CELL_IDENT {
CELL_IDENT_UTRAN_PLMN_LAC_RNC = 8,
CELL_IDENT_UTRAN_RNC = 9,
CELL_IDENT_UTRAN_LAC_RNC = 10,
+ CELL_IDENT_SAI = 11,
- /* Not in 03.03 nor 08.08 */
- CELL_IDENT_WHOLE_GLOBAL_PS = 11, /* CGI with + RAC */
+ /* Not in 03.03 nor 08.08. Place them > 0x0f (discr_id is 4 bits) */
+ CELL_IDENT_WHOLE_GLOBAL_PS = 128, /* CGI + RAC, TS 48.018 8c.1.4.1.1 */
};
/* Keep this misnamed CELL_IDENT for API backwards compatibility (see OS#3124). */
#define CELL_IDENT_LAI_AND_LAC CELL_IDENT_LAI
@@ -53,7 +54,7 @@ struct dtap_header {
dlci_spare:3,
dlci_cc:2;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t dlci_cc:2, dlci_spare:3, dlci_sapi:3;
#endif
};
@@ -167,12 +168,15 @@ enum BSS_MAP_MSG_TYPE {
BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION = 30,
BSS_MAP_MSG_UPLINK_RQST = 31,
BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE = 39,
+ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_STATUS = 59,
+ BSS_MAP_MSG_VGCS_VBS_AREA_CELL_INFO = 60,
BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION = 73,
BSS_MAP_MSG_UPLINK_RELEASE_INDICATION = 74,
BSS_MAP_MSG_UPLINK_REJECT_CMD = 75,
BSS_MAP_MSG_UPLINK_RELEASE_CMD = 76,
BSS_MAP_MSG_UPLINK_SEIZED_CMD = 77,
BSS_MAP_MSG_VGCS_ADDL_INFO = 0x60,
+ BSS_MAP_MSG_VGCS_SMS = 0x61,
BSS_MAP_MSG_NOTIFICATION_DATA = 0x62,
BSS_MAP_MSG_UPLINK_APP_DATA = 0x63,
@@ -265,7 +269,7 @@ enum GSM0808_IE_CODING {
GSM0808_IE_SEGMENTATION = 79,
GSM0808_IE_SERVICE_HANDOVER = 80,
GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS = 81,
- GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000= 82,
+ GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000 = 82,
GSM0808_IE_RESERVED_5 = 65,
GSM0808_IE_RESERVED_6 = 66,
GSM0808_IE_GERAN_CLASSMARK = 0x53,
@@ -442,13 +446,15 @@ enum gsm0808_chan_indicator {
GSM0808_CHAN_SPEECH = 1,
GSM0808_CHAN_DATA = 2,
GSM0808_CHAN_SIGN = 3,
+ GSM0808_CHAN_SPEECH_CTM_TEXT_TELEPHONY = 4,
};
/* GSM 08.08 3.2.2.11 Channel Type */
+#define GSM0808_DATA_FULL_RPREF GSM0808_DATA_FULL_PREF
enum gsm0808_chan_rate_type_data {
GSM0808_DATA_FULL_BM = 0x8,
GSM0808_DATA_HALF_LM = 0x9,
- GSM0808_DATA_FULL_RPREF = 0xa,
+ GSM0808_DATA_FULL_PREF = 0xa,
GSM0808_DATA_HALF_PREF = 0xb,
GSM0808_DATA_FULL_PREF_NO_CHANGE = 0x1a,
GSM0808_DATA_HALF_PREF_NO_CHANGE = 0x1b,
@@ -497,6 +503,42 @@ enum gsm0808_permitted_speech {
GSM0808_PERM_HR6 = 0x45, /*!< OHR AMR */
};
+/* 3GPP TS 48.008 3.2.2.11 Channel Type
+ * Transparent: Data Rate */
+enum gsm0808_data_rate_transp {
+ GSM0808_DATA_RATE_TRANSP_32k0 = 0x3a,
+ GSM0808_DATA_RATE_TRANSP_28k8 = 0x39,
+ GSM0808_DATA_RATE_TRANSP_14k4 = 0x18,
+ GSM0808_DATA_RATE_TRANSP_9k6 = 0x10,
+ GSM0808_DATA_RATE_TRANSP_4k8 = 0x11,
+ GSM0808_DATA_RATE_TRANSP_2k4 = 0x12,
+ GSM0808_DATA_RATE_TRANSP_1k2 = 0x13,
+ GSM0808_DATA_RATE_TRANSP_600 = 0x14,
+ GSM0808_DATA_RATE_TRANSP_1200_75 = 0x15,
+};
+
+/* 3GPP TS 48.008 3.2.2.11 Channel Type
+ * Non-Transparent: Radio Interface Data Rate (preferred) */
+enum gsm0808_data_rate_non_transp {
+ GSM0808_DATA_RATE_NON_TRANSP_12000_6000 = 0x00,
+ GSM0808_DATA_RATE_NON_TRANSP_43k5 = 0x34,
+ GSM0808_DATA_RATE_NON_TRANSP_29k0 = 0x31,
+ GSM0808_DATA_RATE_NON_TRANSP_14k5 = 0x14,
+ GSM0808_DATA_RATE_NON_TRANSP_12k0 = 0x10,
+ GSM0808_DATA_RATE_NON_TRANSP_6k0 = 0x11,
+};
+
+/* 3GPP TS 48.008 3.2.2.11 Channel Type
+ * Non-Transparent: Allowed Radio Interface Data Rate (all possible allowed) */
+enum gsm0808_data_rate_allowed_r_if {
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_43k5 = 0x40,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_32k0 = 0x20,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_29k0 = 0x10,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_14k5 = 0x08,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_12k0 = 0x02,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_6k0 = 0x01,
+};
+
extern const struct value_string gsm0808_permitted_speech_names[];
static inline const char *gsm0808_permitted_speech_name(enum gsm0808_permitted_speech val)
{ return get_value_string(gsm0808_permitted_speech_names, val); }
@@ -515,6 +557,12 @@ enum gsm0808_speech_codec_type {
GSM0808_SCT_CSD = 0xfd, /*!< CSData (see also TS 26.103) */
};
+/* Codec Extension (the real Codec Type follows in the next octet).
+ * This value is intentionally not included in gsm0808_speech_codec_type,
+ * because {enc,dec}_speech_codec() functions take care of the extended
+ * encoding internally. It shall not be used in struct gsm0808_speech_codec. */
+#define GSM0808_SCT_EXT 0x0f
+
extern const struct value_string gsm0808_speech_codec_type_names[];
static inline const char *gsm0808_speech_codec_type_name(enum gsm0808_speech_codec_type val)
{ return get_value_string(gsm0808_speech_codec_type_names, val); }
@@ -539,11 +587,16 @@ enum gsm0808_paging_info {
GSM0808_PAGINF_FOR_USSD = 0x02,
};
-/*! 3GPP TS 48.008 3.2.2.104 Speech Codec */
+/*! 3GPP TS 48.008 3.2.2.104 Speech Codec.
+ * Valid if (fi || pi || pt) == true, otherwise ignore. */
struct gsm0808_speech_codec {
+ /*! Full IP: AoIP with compressed speech via RTP/UDP/IP. */
bool fi;
+ /*! PCMoIP: PCM over A-Interface via RTP/UPD/IP. */
bool pi;
+ /*! PCMoTDM: PCM over A-Interface with TDM as transport. */
bool pt;
+ /*! TFO (Inband Tandem Free Operation). Only valid if (pi || pt) == true. */
bool tf;
/*! See enum gsm0808_speech_codec_type. */
uint8_t type;
@@ -562,7 +615,44 @@ struct gsm0808_speech_codec {
*
* Default values for FR_AMR_WB, OFR_AMR_WB and OHR_AMR_WB:
* See also: 3GPP TS 26.103, Table 5.7-1: Allowed Configurations
- * for the Adaptive Multi-Rate - Wideband Codec Types */
+ * for the Adaptive Multi-Rate - Wideband Codec Types
+ *
+ * This is a copy of 3GPP TS 28.062, Table 7.11.3.1.3-2:
+ *
+ * S0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ * 12,20 (x) x x x
+ * 10,20 x x x
+ * 7,95 x x x
+ * 7,40 x x x x
+ * 6,70 x x x x x x
+ * 5,90 x x x x x x x x x x
+ * 5,15
+ * 4,75 x x x x x x x x x x
+ *
+ * OM F F F F F F F F F F F A F A F A
+ *
+ * HR Y Y Y Y Y Y Y Y Y
+ * FR Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
+ *
+ * Each bit allows one Codec Configuration.
+ * E.g. when bit S3 is set, look at column labeled "3", and see that only 6,7k is active in this configuration; it is
+ * "F" forbidden to change in Optimisation Mode, "Y" HR AMR supports this mode, and "Y" FR AMR can also do it.
+ *
+ * This means that whichever configuration is chosen from S0 thru S15, there are never more than four rates active.
+ *
+ * The spec praises S1 as the most desired configuration: "because it leads in all call cases to TFO/TrFO compatible
+ * connections with optimal voice quality." (Since HR AMR supports up to 7.95k, it seems that S14 would be more optimal
+ * voice quality, but it is not marked as supported by HR AMR.)
+ *
+ * For FR_AMR below, the default of 0x57ff means:
+ * 0x57ff = 0101 0111 1111 1111
+ * ^14 ^10 ^0
+ * allow config 0 thru 10, and configs 12 and 14.
+ *
+ * For HR_AMR, drop all those where there is no "Y" in the HR row:
+ * 0x073f = 0000 0111 0011 1111
+ * ^15 ^11 ^6 ^0
+ */
enum gsm0808_speech_codec_defaults {
GSM0808_SC_CFG_DEFAULT_FR_AMR = 0x57ff,
GSM0808_SC_CFG_DEFAULT_HR_AMR = 0x073f,
@@ -572,9 +662,12 @@ enum gsm0808_speech_codec_defaults {
GSM0808_SC_CFG_DEFAULT_OHR_AMR_WB = 0x01,
};
-/*! Default speech codec configurations broken down by reate.
+/*! Default speech codec configurations broken down by rate.
* See also: 3GPP TS 28.062, Table 7.11.3.1.3-2: Preferred Configurations for
- * the Adaptive Multi-Rate Codec Types. */
+ * the Adaptive Multi-Rate Codec Types.
+ *
+ * Set all Sn bits that have this rate listed as active.
+ */
enum gsm0808_speech_codec_rate_defaults {
GSM0808_SC_CFG_DEFAULT_AMR_4_75 = 0xff03,
GSM0808_SC_CFG_DEFAULT_AMR_5_15 = 0x0000,
@@ -586,9 +679,12 @@ enum gsm0808_speech_codec_rate_defaults {
GSM0808_SC_CFG_DEFAULT_AMR_12_2 = 0xc082
};
-/*! Single speech codec configurations broken down by reate.
+/*! Single speech codec configurations broken down by rate.
* See also: 3GPP TS 28.062, Table 7.11.3.1.3-2: Preferred Configurations for
- * the Adaptive Multi-Rate Codec Types. */
+ * the Adaptive Multi-Rate Codec Types.
+ *
+ * Set bit Sn (S0 = 0x01), where Sn is identified by a descriptive name.
+ */
enum gsm0808_speech_codec_rate {
GSM0808_SC_CFG_AMR_4_75 = 0x0001,
GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20 = 0x0002,
@@ -600,6 +696,29 @@ enum gsm0808_speech_codec_rate {
GSM0808_SC_CFG_AMR_12_2 = 0x0080,
};
+/* Bit index of a mode as returned by gsm0808_amr_modes_from_cfg[].
+ * Example:
+ * if (gsm0808_amr_modes_from_cfg[full_rate ? 1 : 0][9] & GSM0808_AMR_MODE_4_75)
+ * printf("S9 supports 4k75");
+ */
+enum gsm0808_amr_mode {
+ GSM0808_AMR_MODE_4_75 = 0,
+ GSM0808_AMR_MODE_5_15,
+ GSM0808_AMR_MODE_5_90,
+ GSM0808_AMR_MODE_6_70,
+ GSM0808_AMR_MODE_7_40,
+ GSM0808_AMR_MODE_7_95,
+ GSM0808_AMR_MODE_10_2,
+ GSM0808_AMR_MODE_12_2,
+};
+extern const struct value_string gsm0808_amr_mode_names[];
+static inline const char *gsm0808_amr_mode_name(enum gsm0808_amr_mode val)
+{
+ return get_value_string(gsm0808_amr_mode_names, val);
+}
+
+extern const uint8_t gsm0808_amr_modes_from_cfg[2][16];
+
/* 3GPP TS 48.008 3.2.2.103 Speech Codec List */
#define SPEECH_CODEC_MAXLEN 255
struct gsm0808_speech_codec_list {
@@ -607,13 +726,32 @@ struct gsm0808_speech_codec_list {
uint8_t len;
};
+/* 3GPP TS 48.008 3.2.2.11 Channel Type
+ * Asymmetry Preference (used for data, non-transparent service) */
+enum gsm0808_channel_type_asym_pref {
+ GSM0808_CT_ASYM_PREF_NOT_APPLICABLE = 0,
+ GSM0808_CT_ASYM_PREF_UL = 1,
+ GSM0808_CT_ASYM_PREF_DL = 2,
+ GSM0808_CT_ASYM_PREF_SPARE = 3,
+};
+
/* 3GPP TS 48.008 3.2.2.11 Channel Type */
#define CH_TYPE_PERM_SPCH_MAXLEN 9
struct gsm0808_channel_type {
uint8_t ch_indctr;
uint8_t ch_rate_type;
+
+ /* Speech only */
uint8_t perm_spch[CH_TYPE_PERM_SPCH_MAXLEN];
unsigned int perm_spch_len;
+
+ /* Data only */
+ bool data_transparent;
+ uint8_t data_rate;
+ bool data_rate_allowed_is_set;
+ uint8_t data_rate_allowed;
+ bool data_asym_pref_is_set;
+ enum gsm0808_channel_type_asym_pref data_asym_pref;
};
/* 3GPP TS 48.008 3.2.2.10 Encryption Information */
@@ -677,7 +815,7 @@ struct gsm0808_diagnostics {
uint8_t error_pointer_bit_spare:4,
error_pointer_bit:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t error_pointer_bit:4, error_pointer_bit_spare:4;
#endif
uint8_t msg[0]; /*! received message which provoked the error */
diff --git a/include/osmocom/gsm/protocol/gsm_08_58.h b/include/osmocom/gsm/protocol/gsm_08_58.h
index 79ddb685..e8758df0 100644
--- a/include/osmocom/gsm/protocol/gsm_08_58.h
+++ b/include/osmocom/gsm/protocol/gsm_08_58.h
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -33,6 +29,7 @@
/* Channel Number 9.3.1 */
union abis_rsl_chan_nr {
+ struct {
#if OSMO_IS_BIG_ENDIAN
uint8_t cbits:5,
tn:3;
@@ -40,7 +37,8 @@ union abis_rsl_chan_nr {
uint8_t tn:3,
cbits:5;
#endif
- uint8_t chan_nr;
+ } __attribute__ ((packed));
+ uint8_t chan_nr;
} __attribute__ ((packed));
#define ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs 0x01
#define ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(ss) (0x02 + (ss))
@@ -53,8 +51,13 @@ union abis_rsl_chan_nr {
#define ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH4 0x19 /*< non-standard, for CBCH/SDCCH4 */
#define ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH8 0x1a /*< non-standard, for CBCH/SDCCH8 */
+/* non-standard, Osmocom specific Bm/Lm equivalents for VAMOS */
+#define ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Bm_ACCHs 0x1d /*< VAMOS TCH/F */
+#define ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Lm_ACCHs(ss) (0x1e + (ss)) /*< VAMOS TCH/H */
+
/* Link Identifier 9.3.2 */
union abis_rsl_link_id {
+ struct {
#if OSMO_IS_BIG_ENDIAN
uint8_t cbits:2,
na:1,
@@ -66,7 +69,8 @@ union abis_rsl_link_id {
na:1,
cbits:2;
#endif
- uint8_t link_id;
+ } __attribute__ ((packed));
+ uint8_t link_id;
} __attribute__ ((packed));
#define ABIS_RSL_LINK_ID_CBITS_FACCH_SDCCH 0x00
#define ABIS_RSL_LINK_ID_CBITS_SACCH 0x01
@@ -118,20 +122,29 @@ struct abis_rsl_cchan_hdr {
/* Osmocom specific IE to negotiate repeated ACCH capabilities */
struct abis_rsl_osmo_rep_acch_cap {
-#if OSMO_IS_BIG_ENDIAN
- uint8_t reserved:1,
- rxqual:3,
- ul_sacch:1,
- dl_sacch:1,
- dl_facch_all:1,
- dl_facch_cmd:1;
-#elif OSMO_IS_LITTLE_ENDIAN
- uint8_t dl_facch_cmd:1,
- dl_facch_all:1,
- dl_sacch:1,
- ul_sacch:1,
- rxqual:3,
- reserved:1;
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t dl_facch_cmd:1,
+ dl_facch_all:1,
+ dl_sacch:1,
+ ul_sacch:1,
+ rxqual:3,
+ reserved:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t reserved:1, rxqual:3, ul_sacch:1, dl_sacch:1, dl_facch_all:1, dl_facch_cmd:1;
+#endif
+} __attribute__ ((packed));
+
+/* Osmocom specific IE to negotiate temporary overpower of ACCH channels */
+struct abis_rsl_osmo_temp_ovp_acch_cap {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t overpower_db:3,
+ rxqual:3,
+ facch_enable:1,
+ sacch_enable:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t sacch_enable:1, facch_enable:1, rxqual:3, overpower_db:3;
#endif
} __attribute__ ((packed));
@@ -356,7 +369,10 @@ enum abis_rsl_ie {
RSL_IE_SIEMENS_SUGGESTED_RATE = 0x4f,
/* Osmocom specific */
- RSL_IE_OSMO_REP_ACCH_CAP= 0x60,
+ RSL_IE_OSMO_REP_ACCH_CAP = 0x60,
+ RSL_IE_OSMO_TRAINING_SEQUENCE = 0x61,
+ RSL_IE_OSMO_TEMP_OVP_ACCH_CAP = 0x62,
+ RSL_IE_OSMO_OSMUX_CID = 0x63,
/* ip.access */
RSL_IE_IPAC_SRTP_CONFIG = 0xe0,
@@ -374,9 +390,9 @@ enum abis_rsl_ie {
RSL_IE_IPAC_RTP_CSD_FMT = 0xf9,
RSL_IE_IPAC_RTP_JIT_BUF = 0xfa,
RSL_IE_IPAC_RTP_COMPR = 0xfb,
- RSL_IE_IPAC_RTP_PAYLOAD2= 0xfc,
+ RSL_IE_IPAC_RTP_PAYLOAD2 = 0xfc,
RSL_IE_IPAC_RTP_MPLEX = 0xfd,
- RSL_IE_IPAC_RTP_MPLEX_ID= 0xfe,
+ RSL_IE_IPAC_RTP_MPLEX_ID = 0xfe,
};
/* Ericsson specific IEs, clash with above partially, so they're not
@@ -411,7 +427,7 @@ enum {
IPAC_OSMO_EWMA_AVE,
};
-/* IPAC MEAS_PREPROC AVERAGING PARAMID */
+/* IPAC MEAS_PREPROC AVERAGING PARAM ID */
enum {
IPAC_RXLEV_AVE = 0,
IPAC_RXQUAL_AVE,
@@ -454,6 +470,11 @@ enum {
#define RSL_CHAN_OSMO_CBCH4 0xc8 /*< non-standard, for CBCH/SDCCH4 */
#define RSL_CHAN_OSMO_CBCH8 0xd0 /*< non-standard, for CBCH/SDCCH8 */
+/* non-standard, Osmocom specific Bm/Lm equivalents for VAMOS */
+#define RSL_CHAN_OSMO_VAMOS_Bm_ACCHs 0xe8 /* VAMOS TCH/F */
+#define RSL_CHAN_OSMO_VAMOS_Lm_ACCHs 0xf0 /* VAMOS TCH/H */
+#define RSL_CHAN_OSMO_VAMOS_MASK 0xe0 /* VAMOS TCH/{F,H} */
+
/* Chapter 9.3.3 */
#define RSL_ACT_TYPE_INITIAL 0x00
#define RSL_ACT_TYPE_REACT 0x80
@@ -479,49 +500,66 @@ enum rsl_cmod_spd {
RSL_CMOD_SPD_DATA = 0x02,
RSL_CMOD_SPD_SIGN = 0x03,
};
-#define RSL_CMOD_CRT_SDCCH 0x01
-#define RSL_CMOD_CRT_TCH_Bm 0x08 /* full-rate */
-#define RSL_CMOD_CRT_TCH_Lm 0x09 /* half-rate */
-#define RSL_CMOD_CRT_TCH_BI_Bm 0x0a /* full-rate: bi-directional (multislot) */
-#define RSL_CMOD_CRT_TCH_UNI_Bm 0x1a /* full-rate: uni-directional (multislot) */
-#define RSL_CMOD_CRT_TCH_GROUP_Bm 0x18 /* full-rate: group call channel */
-#define RSL_CMOD_CRT_TCH_GROUP_Lm 0x19 /* half-rate: group call channel */
-#define RSL_CMOD_CRT_TCH_BCAST_Bm 0x28 /* full-rate: broadcast call channel */
-#define RSL_CMOD_CRT_TCH_BCAST_Lm 0x29 /* half-rate: broadcast call channel */
-/* Speech */
-#define RSL_CMOD_SP_GSM1 0x01
-#define RSL_CMOD_SP_GSM2 0x11
-#define RSL_CMOD_SP_GSM3 0x21
-#define RSL_CMOD_SP_GSM4 0x31
-#define RSL_CMOD_SP_GSM5 0x09
-#define RSL_CMOD_SP_GSM6 0x0d
-/* non-transparent data (asymmetric) */
-#define RSL_CMOD_CSD_NTA_43k5_14k5 0x61 /* asymmetric 43.5 kbit/s (DL) + 14.5 kbit/s (UL) */
-#define RSL_CMOD_CSD_NTA_29k0_14k5 0x62 /* asymmetric 29.0 kbit/s (DL) + 14.5 kbit/s (UL) */
-#define RSL_CMOD_CSD_NTA_43k5_29k0 0x63 /* asymmetric 43.5 kbit/s (DL) + 29.0 kbit/s (UL) */
-#define RSL_CMOD_CSD_NTA_14k5_43k5 0x69 /* asymmetric 14.5 kbit/s (DL) + 43.5 kbit/s (UL) */
-#define RSL_CMOD_CSD_NTA_14k5_29k0 0x6a /* asymmetric 14.5 kbit/s (DL) + 29.0 kbit/s (UL) */
-#define RSL_CMOD_CSD_NTA_29k0_43k5 0x6b /* asymmetric 29.0 kbit/s (DL) + 43.5 kbit/s (UL) */
-/* non-transparent data */
-#define RSL_CMOD_CSD_NT_43k5 0x74
-#define RSL_CMOD_CSD_NT_28k8 0x71
-#define RSL_CMOD_CSD_NT_14k5 0x58
-#define RSL_CMOD_CSD_NT_12k0 0x50
-#define RSL_CMOD_CSD_NT_6k0 0x51
+/*! Channel rate and type */
+enum rsl_cmod_crt {
+ RSL_CMOD_CRT_SDCCH = 0x01,
+ RSL_CMOD_CRT_TCH_Bm = 0x08, /* full-rate */
+ RSL_CMOD_CRT_TCH_Lm = 0x09, /* half-rate */
+ RSL_CMOD_CRT_TCH_BI_Bm = 0x0a, /* full-rate: bi-directional (multislot) */
+ RSL_CMOD_CRT_TCH_UNI_Bm = 0x1a, /* full-rate: uni-directional (multislot) */
+ RSL_CMOD_CRT_TCH_GROUP_Bm = 0x18, /* full-rate: group call channel */
+ RSL_CMOD_CRT_TCH_GROUP_Lm = 0x19, /* half-rate: group call channel */
+ RSL_CMOD_CRT_TCH_BCAST_Bm = 0x28, /* full-rate: broadcast call channel */
+ RSL_CMOD_CRT_TCH_BCAST_Lm = 0x29, /* half-rate: broadcast call channel */
+ RSL_CMOD_CRT_OSMO_TCH_VAMOS_Bm = 0x88, /* full-rate in VAMOS mode */
+ RSL_CMOD_CRT_OSMO_TCH_VAMOS_Lm = 0x89, /* half-rate in VAMOS mode */
+};
+/*! Speech */
+enum rsl_cmod_sp {
+ RSL_CMOD_SP_GSM1 = 0x01,
+ RSL_CMOD_SP_GSM2 = 0x11,
+ RSL_CMOD_SP_GSM3 = 0x21,
+ RSL_CMOD_SP_GSM4 = 0x31,
+ RSL_CMOD_SP_GSM5 = 0x09,
+ RSL_CMOD_SP_GSM6 = 0x0d,
+};
+/*! Non-transparent data */
+enum rsl_cmod_csd_nt {
+ RSL_CMOD_CSD_NTA_43k5_14k5 = 0x61, /* asymmetric 43.5 kbit/s (DL) + 14.5 kbit/s (UL) */
+ RSL_CMOD_CSD_NTA_29k0_14k5 = 0x62, /* asymmetric 29.0 kbit/s (DL) + 14.5 kbit/s (UL) */
+ RSL_CMOD_CSD_NTA_43k5_29k0 = 0x63, /* asymmetric 43.5 kbit/s (DL) + 29.0 kbit/s (UL) */
+ RSL_CMOD_CSD_NTA_14k5_43k5 = 0x69, /* asymmetric 14.5 kbit/s (DL) + 43.5 kbit/s (UL) */
+ RSL_CMOD_CSD_NTA_14k5_29k0 = 0x6a, /* asymmetric 14.5 kbit/s (DL) + 29.0 kbit/s (UL) */
+ RSL_CMOD_CSD_NTA_29k0_43k5 = 0x6b, /* asymmetric 29.0 kbit/s (DL) + 43.5 kbit/s (UL) */
+ RSL_CMOD_CSD_NT_43k5 = 0x74,
+ RSL_CMOD_CSD_NT_28k8 = 0x71,
+ RSL_CMOD_CSD_NT_14k5 = 0x58,
+ RSL_CMOD_CSD_NT_12k0 = 0x50,
+ RSL_CMOD_CSD_NT_6k0 = 0x51,
+};
/* legacy #defines with wrong name */
#define RSL_CMOD_SP_NT_14k5 RSL_CMOD_CSD_NT_14k5
#define RSL_CMOD_SP_NT_12k0 RSL_CMOD_CSD_NT_12k0
#define RSL_CMOD_SP_NT_6k0 RSL_CMOD_CSD_NT_6k0
-/* transparent data */
-#define RSL_CMOD_CSD_T_32000 0x38
-#define RSL_CMOD_CSD_T_29000 0x39
-#define RSL_CMOD_CSD_T_14400 0x18
-#define RSL_CMOD_CSD_T_9600 0x10
-#define RSL_CMOD_CSD_T_4800 0x11
-#define RSL_CMOD_CSD_T_2400 0x12
-#define RSL_CMOD_CSD_T_1200 0x13
-#define RSL_CMOD_CSD_T_600 0x14
-#define RSL_CMOD_CSD_T_1200_75 0x15
+#define RSL_CMOD_CSD_T_32000 RSL_CMOD_CSD_T_32k0
+#define RSL_CMOD_CSD_T_29000 RSL_CMOD_CSD_T_29k0
+#define RSL_CMOD_CSD_T_14400 RSL_CMOD_CSD_T_14k4
+#define RSL_CMOD_CSD_T_9600 RSL_CMOD_CSD_T_9k6
+#define RSL_CMOD_CSD_T_4800 RSL_CMOD_CSD_T_4k8
+#define RSL_CMOD_CSD_T_2400 RSL_CMOD_CSD_T_2k4
+#define RSL_CMOD_CSD_T_1200 RSL_CMOD_CSD_T_1k2
+/*! Transparent data */
+enum rsl_cmod_csd_t {
+ RSL_CMOD_CSD_T_32k0 = 0x38,
+ RSL_CMOD_CSD_T_29k0 = 0x39,
+ RSL_CMOD_CSD_T_14k4 = 0x18,
+ RSL_CMOD_CSD_T_9k6 = 0x10,
+ RSL_CMOD_CSD_T_4k8 = 0x11,
+ RSL_CMOD_CSD_T_2k4 = 0x12,
+ RSL_CMOD_CSD_T_1k2 = 0x13,
+ RSL_CMOD_CSD_T_600 = 0x14,
+ RSL_CMOD_CSD_T_1200_75 = 0x15,
+};
/*! RSL Channel Identification IE (Chapter 9.3.5) */
struct rsl_ie_chan_ident {
@@ -639,7 +677,7 @@ struct rsl_ie_chan_ident {
#define RSL_CHANNEED_TCH_F 0x02
#define RSL_CHANNEED_TCH_ForH 0x03
-/*! RSL Cell Broadcast Command (Chapter 9.3.45) */
+/*! RSL Cell Broadcast Command (Chapter 9.3.41) */
struct rsl_ie_cb_cmd_type {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t last_block:2;
@@ -647,7 +685,7 @@ struct rsl_ie_cb_cmd_type {
uint8_t def_bcast:1;
uint8_t command:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t command:4, def_bcast:1, spare:1, last_block:2;
#endif
} __attribute__ ((packed));
@@ -665,6 +703,23 @@ struct rsl_ie_cb_cmd_type {
#define RSL_CB_CMD_LASTBLOCK_2 2
#define RSL_CB_CMD_LASTBLOCK_3 3
+/*! NCH DRX Information (Chapter 9.3.47) */
+struct rsl_ie_nch_drx_info {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t nln:2;
+ uint8_t emlpp_priority:3;
+ uint8_t nln_status:1;
+ uint8_t spare:2;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t spare:2, nln_status:1, emlpp_priority:3, nln:2;
+#endif
+} __attribute__ ((packed));
+
+/*! Command Indicator (Chapter 9.3.48) */
+#define RSL_CMD_INDICATOR_START 0x00
+#define RSL_CMD_INDICATOR_STOP 0x01
+
/* Chapter 3.3.2.3 Brocast control channel */
/* CCCH-CONF, NC is not combined */
#define RSL_BCCH_CCCH_CONF_1_NC 0x00
@@ -710,10 +765,10 @@ enum rsl_ipac_speech_mode_m {
/* RSL_IE_IPAC_RTP_CSD_FMT, lower four bits */
enum rsl_ipac_rtp_csd_format_d {
- RSL_IPAC_RTP_CSD_EXT_TRAU = 0,
- RSL_IPAC_RTP_CSD_NON_TRAU = 1,
- RSL_IPAC_RTP_CSD_TRAU_BTS = 2,
- RSL_IPAC_RTP_CSD_IWF_FREE = 3,
+ RSL_IPAC_RTP_CSD_EXT_TRAU = 0, /*!< TRAU-like RTP format, without leading zero-bits */
+ RSL_IPAC_RTP_CSD_NON_TRAU = 1, /*!< packed 16k (252/288 bit) / 8k (126 bit) in RTP */
+ RSL_IPAC_RTP_CSD_TRAU_BTS = 2, /*!< TRAU in BTS; V.110 in RTP/CLEARMODE */
+ RSL_IPAC_RTP_CSD_IWF_FREE = 3, /*!< unknown proprietary IWF-free BTS-BTS data */
};
/* RSL_IE_IPAC_RTP_CSD_FMT, upper four bits */
enum rsl_ipac_rtp_csd_format_ir {
@@ -781,8 +836,14 @@ enum rsl_ipac_embedded_ie {
RSL_IPAC_EIE_SDCCH_CTL_PARAM = 0x1a,
RSL_IPAC_EIE_AMR_CONV_THRESH = 0x1b,
+ /* Osmocom specific extensions: */
+ RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG = 0xf0,
+ RSL_IPAC_EIE_OSMO_MS_PWR_CTL = 0xf1,
+ RSL_IPAC_EIE_OSMO_PC_THRESH_COMP = 0xf2,
+
};
+/* Value of TLV IE RSL_IPAC_EIE_MEAS_AVG_CFG */
struct ipac_preproc_ave_cfg {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t h_reqave:5,
@@ -792,14 +853,39 @@ struct ipac_preproc_ave_cfg {
ave_method:3;
uint8_t params[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved:1, param_id:2, h_reqave:5;
uint8_t ave_method:3, h_reqt:5;
uint8_t params[0];
#endif
}__attribute__ ((packed));
-/*! MS/BS Power Control Thresholds */
+
+struct osmo_preproc_ave_cfg_field {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t h_reqave:5,
+ ave_enabled:1,
+ reserved:2;
+ uint8_t h_reqt:5,
+ ave_method:3;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t reserved:2, ave_enabled:1, h_reqave:5;
+ uint8_t ave_method:3, h_reqt:5;
+#endif
+}__attribute__ ((packed));
+/* Value of TLV IE RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG: */
+struct osmo_preproc_ave_cfg {
+ struct osmo_preproc_ave_cfg_field ci_fr;
+ struct osmo_preproc_ave_cfg_field ci_hr;
+ struct osmo_preproc_ave_cfg_field ci_amr_fr;
+ struct osmo_preproc_ave_cfg_field ci_amr_hr;
+ struct osmo_preproc_ave_cfg_field ci_sdcch;
+ struct osmo_preproc_ave_cfg_field ci_gprs;
+ uint8_t params[0]; /* Contains params for each above, appended one after the other */
+}__attribute__ ((packed));
+
+/*! MS/BS Power Control Thresholds (RSL_IPAC_EIE_MS_PWR_CTL) */
struct ipac_preproc_pc_thresh {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t l_rxlev:6, reserved_l_rxlev:2;
@@ -807,13 +893,24 @@ struct ipac_preproc_pc_thresh {
uint8_t u_rxqual:3, reserved_u_rxqual:1,
l_rxqual:3, reserved_l_rxqual:1;
#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved_l_rxlev:2, l_rxlev:6;
uint8_t reserved_u_rxlev:2, u_rxlev:6;
- uint8_t reserved_l_rxqual:1, l_rxqual:3,
- reserved_u_rxqual:1, u_rxqual:3;
+ uint8_t reserved_l_rxqual:1, l_rxqual:3, reserved_u_rxqual:1, u_rxqual:3;
#endif
}__attribute__ ((packed));
+/*! Osmocom extension for: MS/BS Power Control Thresholds (RSL_IPAC_EIE_OSMO_MS_PWR_CTL) */
+struct osmo_preproc_pc_thresh {
+ /* Carrier-to-Interference (C/I), in dB: */
+ int8_t l_ci_fr; int8_t u_ci_fr; /* FR/EFR */
+ int8_t l_ci_hr; int8_t u_ci_hr; /* HR */
+ int8_t l_ci_amr_fr; int8_t u_ci_amr_fr; /* AMR FR */
+ int8_t l_ci_amr_hr; int8_t u_ci_amr_hr; /* AMR HR */
+ int8_t l_ci_sdcch; int8_t u_ci_sdcch; /* SDCCH */
+ int8_t l_ci_gprs; int8_t u_ci_gprs; /* GPRS */
+}__attribute__ ((packed));
+
/*! Handover Thresholds */
struct ipac_preproc_ho_thresh {
#if OSMO_IS_LITTLE_ENDIAN
@@ -832,7 +929,7 @@ struct ipac_preproc_ho_thresh {
uint8_t ms_range_max:6,
reserved_ms_range:2;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved_l_rxlev_ul:2, l_rxlev_ul_h:6;
uint8_t reserved_l_rxlev_dl:2, l_rxlev_dl_h:6;
uint8_t reserved_rxlev_ul:2, rxlev_ul_ih:6;
@@ -842,7 +939,7 @@ struct ipac_preproc_ho_thresh {
#endif
}__attribute__ ((packed));
-/*! PC Threshold Comparators */
+/*! PC Threshold Comparators (RSL_IPAC_EIE_PC_THRESH_COMP) */
struct ipac_preproc_pc_comp {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t p1:5, reserved_p1:3;
@@ -856,6 +953,7 @@ struct ipac_preproc_pc_comp {
uint8_t pc_interval:5, reserved_pc:3;
uint8_t red_step_size:4, inc_step_size:4;
#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved_p1:3, p1:5;
uint8_t reserved_n1:3, n1:5;
uint8_t reserved_p2:3, p2:5;
@@ -869,6 +967,31 @@ struct ipac_preproc_pc_comp {
#endif
}__attribute__ ((packed));
+/*! Osmocom extension for: PC Threshold Comparators (RSL_IPAC_EIE_OSMO_PC_THRESH_COMP) */
+struct ipac_preproc_pc_comp_field {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t lower_p:5, reserved_lower_p:3;
+ uint8_t lower_n:5, reserved_lower_n:3;
+ uint8_t upper_p:5, reserved_upper_p:3;
+ uint8_t upper_n:5, reserved_upper_n:3;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t reserved_lower_p:3, lower_p:5;
+ uint8_t reserved_lower_n:3, lower_n:5;
+ uint8_t reserved_upper_p:3, upper_p:5;
+ uint8_t reserved_upper_n:3, upper_n:5;
+#endif
+}__attribute__ ((packed));
+struct osmo_preproc_pc_comp {
+ /* Used for Carrier-to-Interference (C/I), in dB: */
+ struct ipac_preproc_pc_comp_field ci_fr;
+ struct ipac_preproc_pc_comp_field ci_hr;
+ struct ipac_preproc_pc_comp_field ci_amr_fr;
+ struct ipac_preproc_pc_comp_field ci_amr_hr;
+ struct ipac_preproc_pc_comp_field ci_sdcch;
+ struct ipac_preproc_pc_comp_field ci_gprs;
+}__attribute__ ((packed));
+
/*! HO Threshold Comparators */
struct ipac_preproc_ho_comp {
#if OSMO_IS_LITTLE_ENDIAN
@@ -893,7 +1016,7 @@ struct ipac_preproc_ho_comp {
uint8_t reserved;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved_p5:3, p5:5;
uint8_t reserved_n5:3, n5:5;
uint8_t reserved_p6:3, p6:5;
@@ -916,7 +1039,7 @@ struct ipac_preproc_ho_candidates {
s:1,
reserved1:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved0:2, bsic:6;
uint8_t reserved1:1, s:1, ba_used:1, bcch_freq:5;
#endif
@@ -931,7 +1054,7 @@ struct ipac_preproc_ncell_dflts {
uint8_t ms_txpwr_max_def:5,
reserved_ms_txpwr_max_def:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved_rxlev_min_def:2, rxlev_min_def:6;
uint8_t reserved_ho_margin_def:3, ho_margin_def:5;
uint8_t reserved_ms_txpwr_max_def:3, ms_txpwr_max_def:5;
@@ -944,7 +1067,7 @@ struct ipac_preproc_ho_ctl_param {
sdcch_ho_umts:1,
reserved:6;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved:6, sdcch_ho_umts:1, sdcch_ho_gsm:1;
#endif
}__attribute__ ((packed));
@@ -961,18 +1084,16 @@ struct ipac_preproc_cfg {
};
struct rsl_l1_info {
-#if OSMO_IS_BIG_ENDIAN
- uint8_t ms_pwr:5,
- fpc_epc:1,
- srr_sro:1,
- reserved:1;
- uint8_t ta;
-#elif OSMO_IS_LITTLE_ENDIAN
+#if OSMO_IS_LITTLE_ENDIAN
uint8_t reserved:1,
srr_sro:1,
fpc_epc:1,
ms_pwr:5;
uint8_t ta;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t ms_pwr:5, fpc_epc:1, srr_sro:1, reserved:1;
+ uint8_t ta;
#endif
} __attribute__ ((packed));
diff --git a/include/osmocom/gsm/protocol/gsm_09_02.h b/include/osmocom/gsm/protocol/gsm_09_02.h
index 0b54fb74..4d5ff13c 100644
--- a/include/osmocom/gsm/protocol/gsm_09_02.h
+++ b/include/osmocom/gsm/protocol/gsm_09_02.h
@@ -1,8 +1,7 @@
/*! \file gsm_09_02.h
* GSM TS 09.02 definitions (MAP). */
-#ifndef PROTO_GSM_09_02_H
-#define PROTO_GSM_09_02_H
+#pragma once
/* Section 17.7.4 */
/* SS-Status */
@@ -134,5 +133,3 @@
#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_C 0xDC
#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_D 0xDD
#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_E 0xDE
-
-#endif /* PROTO_GSM_09_02_H */
diff --git a/include/osmocom/gsm/protocol/gsm_12_21.h b/include/osmocom/gsm/protocol/gsm_12_21.h
index 34622b33..39b1d45c 100644
--- a/include/osmocom/gsm/protocol/gsm_12_21.h
+++ b/include/osmocom/gsm/protocol/gsm_12_21.h
@@ -1,3 +1,4 @@
+/* 3GPP TS 12.21, nowadays 3GPP TS 52.021 */
/*
* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
@@ -12,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -445,7 +442,7 @@ enum abis_nm_attr {
NM_ATT_IPACC_NS_CFG = 0xa0,
NM_ATT_IPACC_BSSGP_CFG = 0xa1,
NM_ATT_IPACC_NS_LINK_CFG = 0xa2,
- NM_ATT_IPACC_RLC_CFG = 0xa3,
+ NM_ATT_IPACC_RLC_CFG = 0xa3,
NM_ATT_IPACC_ALM_THRESH_LIST = 0xa4,
NM_ATT_IPACC_MONIT_VAL_LIST = 0xa5,
NM_ATT_IPACC_TIB_CONTROL = 0xa6,
@@ -462,7 +459,7 @@ enum abis_nm_attr {
NM_ATT_BS11_RF_RES_IND_PER = 0x8f,
-
+
NM_ATT_BS11_RX_LEV_MIN_CELL = 0x90,
NM_ATT_BS11_ABIS_EXT_TIME = 0x91,
NM_ATT_BS11_TIMER_HO_REQUEST = 0x92,
@@ -526,9 +523,11 @@ enum abis_nm_adm_state {
/*! OML Availability State (Section 9.4.7) */
enum abis_nm_avail_state {
- NM_AVSTATE_IN_TEST = 1,
+ NM_AVSTATE_IN_TEST = 0,
+ NM_AVSTATE_FAILED = 1,
NM_AVSTATE_POWER_OFF = 2,
NM_AVSTATE_OFF_LINE = 3,
+ /* <not used> = 4, */
NM_AVSTATE_DEPENDENCY = 5,
NM_AVSTATE_DEGRADED = 6,
NM_AVSTATE_NOT_INSTALLED= 7,
@@ -564,8 +563,10 @@ enum abis_nm_chan_comb {
NM_CHANC_IPAC_TCHFull_PDCH = 0x80,
NM_CHANC_IPAC_TCHFull_TCHHalf = 0x81,
/* osmocom */
- NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH = 0x90,
+ NM_CHANC_OSMO_DYN = 0x90,
};
+/* Backward compatibility with older naming: */
+#define NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH NM_CHANC_OSMO_DYN
/*! Event Type (Section 9.4.16) */
enum abis_nm_event_type {
@@ -774,6 +775,98 @@ enum ipac_eie {
NM_IPAC_EIE_BTS_ID = 0x25,
};
+/*! ip.access support flags for NM_IPAC_EIE_FREQ_BANDS */
+#define NM_IPAC_F_FREQ_BAND_PGSM (1 << 0)
+#define NM_IPAC_F_FREQ_BAND_EGSM (1 << 1)
+#define NM_IPAC_F_FREQ_BAND_RGSM (1 << 2)
+#define NM_IPAC_F_FREQ_BAND_DCS (1 << 3)
+#define NM_IPAC_F_FREQ_BAND_PCS (1 << 4)
+#define NM_IPAC_F_FREQ_BAND_850 (1 << 5)
+#define NM_IPAC_F_FREQ_BAND_480 (1 << 6)
+#define NM_IPAC_F_FREQ_BAND_450 (1 << 7)
+
+/*! ip.access support flags for NM_IPAC_EIE_CIPH_ALGOS */
+#define NM_IPAC_F_CIPH_ALGO_A51 (1 << 0)
+#define NM_IPAC_F_CIPH_ALGO_A52 (1 << 1)
+#define NM_IPAC_F_CIPH_ALGO_A53 (1 << 2)
+#define NM_IPAC_F_CIPH_ALGO_A54 (1 << 3)
+#define NM_IPAC_F_CIPH_ALGO_A55 (1 << 4)
+#define NM_IPAC_F_CIPH_ALGO_A56 (1 << 5)
+#define NM_IPAC_F_CIPH_ALGO_A57 (1 << 6)
+#define NM_IPAC_F_CIPH_ALGO_A58 (1 << 7)
+
+/*! ip.access support flags for NM_IPAC_EIE_CHAN_TYPES (1st octet) */
+#define NM_IPAC_F_CHANT_TCHF (1 << 0)
+#define NM_IPAC_F_CHANT_TCHH (1 << 1)
+#define NM_IPAC_F_CHANT_SDCCH8 (1 << 2)
+#define NM_IPAC_F_CHANT_BCCH (1 << 3)
+#define NM_IPAC_F_CHANT_BCCH_SDCCH4 (1 << 4)
+#define NM_IPAC_F_CHANT_BCH (1 << 5)
+#define NM_IPAC_F_CHANT_BCCH_SDCCH4_CBCH (1 << 6)
+#define NM_IPAC_F_CHANT_SDCCH8_CBCH (1 << 7)
+/*! ip.access support flags for NM_IPAC_EIE_CHAN_TYPES (2nd octet) */
+#define NM_IPAC_F_CHANT_PDCHF (1 << 8)
+#define NM_IPAC_F_CHANT_TCHF_PDCHF (1 << 9)
+#define NM_IPAC_F_CHANT_TCHH_PDCHH (1 << 10)
+#define NM_IPAC_F_CHANT_TCHF_TCHH (1 << 11)
+
+/*! ip.access support flags for NM_IPAC_EIE_CHAN_MODES (speech codecs) */
+#define NM_IPAC_F_CHANM_SPEECH_FS (1 << 0)
+#define NM_IPAC_F_CHANM_SPEECH_EFS (1 << 1)
+#define NM_IPAC_F_CHANM_SPEECH_AFS (1 << 2)
+#define NM_IPAC_F_CHANM_SPEECH_HS (1 << 3)
+#define NM_IPAC_F_CHANM_SPEECH_AHS (1 << 4)
+/*! ip.access support flags for NM_IPAC_EIE_CHAN_MODES (CSD non-transparent) */
+#define NM_IPAC_F_CHANM_CSD_NT_4k8 (1 << 8)
+#define NM_IPAC_F_CHANM_CSD_NT_9k6 (1 << 9)
+#define NM_IPAC_F_CHANM_CSD_NT_14k4 (1 << 10)
+/*! ip.access support flags for NM_IPAC_EIE_CHAN_MODES (CSD transparent) */
+#define NM_IPAC_F_CHANM_CSD_T_1200_75 (1 << 16)
+#define NM_IPAC_F_CHANM_CSD_T_600 (1 << 17)
+#define NM_IPAC_F_CHANM_CSD_T_1k2 (1 << 18)
+#define NM_IPAC_F_CHANM_CSD_T_2k4 (1 << 19)
+#define NM_IPAC_F_CHANM_CSD_T_4k8 (1 << 20)
+#define NM_IPAC_F_CHANM_CSD_T_9k6 (1 << 21)
+#define NM_IPAC_F_CHANM_CSD_T_14k4 (1 << 22)
+
+/*! ip.access support flags for NM_IPAC_EIE_GPRS_CODING (GPRS) */
+#define NM_IPAC_F_GPRS_CODING_CS1 (1 << 0)
+#define NM_IPAC_F_GPRS_CODING_CS2 (1 << 1)
+#define NM_IPAC_F_GPRS_CODING_CS3 (1 << 2)
+#define NM_IPAC_F_GPRS_CODING_CS4 (1 << 3)
+/*! ip.access support flags for NM_IPAC_EIE_GPRS_CODING (EGPRS) */
+#define NM_IPAC_F_GPRS_CODING_MCS1 (1 << 7)
+#define NM_IPAC_F_GPRS_CODING_MCS2 (1 << 8)
+#define NM_IPAC_F_GPRS_CODING_MCS3 (1 << 9)
+#define NM_IPAC_F_GPRS_CODING_MCS4 (1 << 10)
+#define NM_IPAC_F_GPRS_CODING_MCS5 (1 << 11)
+#define NM_IPAC_F_GPRS_CODING_MCS6 (1 << 12)
+#define NM_IPAC_F_GPRS_CODING_MCS7 (1 << 13)
+#define NM_IPAC_F_GPRS_CODING_MCS8 (1 << 14)
+#define NM_IPAC_F_GPRS_CODING_MCS9 (1 << 15)
+
+/*! ip.access support flags for NM_IPAC_EIE_RTP_FEATURES */
+#define NM_IPAC_F_RTP_FEAT_COMPR_CONTROL (1 << 0) /* RTP Compression Control */
+#define NM_IPAC_F_RTP_FEAT_IR_8k (1 << 1) /* IR 8 kbit/s */
+#define NM_IPAC_F_RTP_FEAT_IR_16k (1 << 2) /* IR 16 kbit/s */
+#define NM_IPAC_F_RTP_FEAT_IR_32k (1 << 3) /* IR 32 kbit/s */
+#define NM_IPAC_F_RTP_FEAT_IR_64k (1 << 4) /* IR 64 kbit/s */
+#define NM_IPAC_F_RTP_FEAT_MULTIPLEX_RTP (1 << 6) /* RTP Multiplexing */
+#define NM_IPAC_F_RTP_FEAT_MULTIPLEX_SRTP (1 << 7) /* SRTP Multiplexing */
+
+/*! ip.access support flags for NM_IPAC_EIE_RSL_FEATURES */
+#define NM_IPAC_F_RSL_FEAT_PHYSICAL_CONTEXT (1 << 0)
+#define NM_IPAC_F_RSL_FEAT_DYN_PDCH_ACT (1 << 1)
+#define NM_IPAC_F_RSL_FEAT_RTP_PT2 (1 << 2)
+
+extern const struct value_string abis_nm_ipacc_freq_band_desc[];
+extern const struct value_string abis_nm_ipacc_ciph_algo_desc[];
+extern const struct value_string abis_nm_ipacc_chant_desc[];
+extern const struct value_string abis_nm_ipacc_chanm_desc[];
+extern const struct value_string abis_nm_ipacc_gprs_coding_desc[];
+extern const struct value_string abis_nm_ipacc_rtp_feat_desc[];
+extern const struct value_string abis_nm_ipacc_rsl_feat_desc[];
+
/*! ip.access NWL BCCH information type */
enum ipac_bcch_info_type {
IPAC_BINF_RXLEV = (1 << 8),
@@ -789,6 +882,62 @@ enum ipac_bcch_info_type {
IPAC_BINF_CELL_ALLOC = (1 << 2),
};
+/*! ip.access NM_ATT_IPACC_NS_CFG value */
+struct abis_nm_ipacc_att_ns_cfg {
+ uint8_t un_blocking_timer; /* (un)blocking Timer (Tns-block) timeout */
+ uint8_t un_blocking_retries; /* (un)blocking Timer (Tns-block) number of retries */
+ uint8_t reset_timer; /* Reset Timer (Tns-reset) timeout */
+ uint8_t reset_retries; /* Reset Timer (Tns-reset) number of retries */
+ uint8_t test_timer; /* Test Timer (Tns-test) timeout */
+ uint8_t alive_timer; /* Alive Timer (Tns-alive) timeout */
+ uint8_t alive_retries; /* Alive Timer (Tns-alive) number of retries */
+} __attribute__((packed));
+
+/*! ip.access NM_ATT_IPACC_BSSGP_CFG value */
+struct abis_nm_ipacc_att_bssgp_cfg {
+ uint8_t t1_s; /* blocking timer (T1) */
+ uint8_t t1_blocking_retries; /* blocking retries */
+ uint8_t t1_unblocking_retries; /* unblocking retries */
+ uint8_t t2_s; /* reset timer (T2) */
+ uint8_t t2_retries; /* reset retries */
+ uint8_t t3_100ms; /* suspend timer (T3) in 100ms */
+ uint8_t t3_retries; /* suspend retries */
+ uint8_t t4_100ms; /* resume timer (T4) in 100ms */
+ uint8_t t4_retries; /* resume retries */
+ uint8_t t5_s; /* capability update timer (T5) */
+ uint8_t t5_retries; /* capability update retries */
+} __attribute__((packed));
+
+/*! ip.access NM_ATT_IPACC_RLC_CFG value */
+struct abis_nm_ipacc_att_rlc_cfg {
+ uint8_t t3142;
+ uint8_t t3169;
+ uint8_t t3191;
+ uint8_t t3193_10ms;
+ uint8_t t3195;
+ uint8_t n3101;
+ uint8_t n3103;
+ uint8_t n3105;
+ uint8_t rlc_cv_countdown;
+} __attribute__((packed));
+
+/*! ip.access NM_ATT_IPACC_RLC_CFG_2 value */
+struct abis_nm_ipacc_att_rlc_cfg_2 {
+ /* T downlink TBF extension (0..500, network order) */
+ uint16_t t_dl_tbf_ext_10ms;
+ /* T uplink TBF extension (0..500, network order) */
+ uint16_t t_ul_tbf_ext_10ms;
+ /* Initial CS to use: CS1 -> 1, CS2 -> 2, CS3 -> 3, CS4 -> 4 */
+ uint8_t initial_cs;
+} __attribute__((packed));
+
+/*! ip.access NM_ATT_IPACC_RLC_CFG_3 value */
+struct abis_nm_ipacc_att_rlc_cfg_3 {
+ /* Initial MCS to use when EGPRS is used:
+ * MCS1 -> 1, MCS2 -> 2, ..., MCS9 -> 9 */
+ uint8_t initial_mcs;
+} __attribute__((packed));
+
/*! Osmocom NSVC address type for NM_ATT_OSMO_NS_LINK_CFG */
enum osmo_oml_nsvc_address_type {
OSMO_NSVC_ADDR_UNSPEC = 0x00,
diff --git a/include/osmocom/gsm/protocol/gsm_23_032.h b/include/osmocom/gsm/protocol/gsm_23_032.h
index a4c05061..6eb65ca2 100644
--- a/include/osmocom/gsm/protocol/gsm_23_032.h
+++ b/include/osmocom/gsm/protocol/gsm_23_032.h
@@ -54,7 +54,7 @@ struct gad_raw_head {
uint8_t spare:4,
type:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t type:4, spare:4;
#endif
} __attribute__ ((packed));
@@ -73,7 +73,7 @@ struct gad_raw_ell_point_unc_circle {
uint8_t unc:7,
spare2:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
struct gad_raw_head h;
uint8_t lat[3];
uint8_t lon[3];
@@ -94,7 +94,7 @@ struct gad_raw_ell_point_unc_ellipse {
uint8_t confidence:7,
spare3:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
struct gad_raw_head h;
uint8_t lat[3];
uint8_t lon[3];
@@ -111,7 +111,7 @@ struct gad_raw_polygon {
uint8_t num_points:4;
uint8_t type:4; /*!< type = GAD_TYPE_POLYGON */
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t type:4, num_points:4;
#endif
} h;
@@ -144,7 +144,7 @@ struct gad_raw_ell_point_alt_unc_ell {
uint8_t confidence:7,
spare4:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
struct gad_raw_head h;
uint8_t lat[3];
uint8_t lon[3];
@@ -170,7 +170,7 @@ struct gad_raw_ell_arc {
uint8_t confidence:7,
spare2:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
struct gad_raw_head h;
uint8_t lat[3];
uint8_t lon[3];
@@ -194,7 +194,7 @@ struct gad_raw_ha_ell_point_unc_ell {
uint8_t confidence:7,
spare1:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
struct gad_raw_head h;
uint8_t lat[4];
uint8_t lon[4];
@@ -221,7 +221,7 @@ struct gad_raw_ha_ell_point_alt_unc_ell {
uint8_t v_confidence:7,
spare2:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
struct gad_raw_head h;
uint8_t lat[4];
uint8_t lon[4];
diff --git a/include/osmocom/gsm/protocol/gsm_23_041.h b/include/osmocom/gsm/protocol/gsm_23_041.h
index e726cff2..2a2b006f 100644
--- a/include/osmocom/gsm/protocol/gsm_23_041.h
+++ b/include/osmocom/gsm/protocol/gsm_23_041.h
@@ -2,6 +2,16 @@
#include <osmocom/core/endian.h>
+/* Section 9.3.24: Warning-Type */
+enum gsm23041_warning_type_value {
+ CBS_ETWS_WARN_TYPE_EARTHQUAKE = 0,
+ CBS_ETWS_WARN_TYPE_TSUNAMI = 1,
+ CBS_ETWS_WARN_TYPE_EARTHQUAKE_AND_TSUNAMI = 2,
+ CBS_ETWS_WARN_TYPE_TEST = 3,
+ CBS_ETWS_WARN_TYPE_OTHER = 4,
+ /* 0000101-1111111 Reserved for future use */
+};
+
/* Section 9.4.1.2: GSM Message Format */
struct gsm23041_msg_param_gsm {
uint16_t serial_nr;
@@ -12,7 +22,7 @@ struct gsm23041_msg_param_gsm {
uint8_t num_pages:4,
page_nr:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t page_nr:4, num_pages:4;
#endif
} page_param;
diff --git a/include/osmocom/gsm/protocol/gsm_25_415.h b/include/osmocom/gsm/protocol/gsm_25_415.h
new file mode 100644
index 00000000..5c4dd2bb
--- /dev/null
+++ b/include/osmocom/gsm/protocol/gsm_25_415.h
@@ -0,0 +1,222 @@
+#pragma once
+/* Iu User Plane (IuUP) Definitions as per 3GPP TS 25.415 */
+/* (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <stdint.h>
+#include <osmocom/core/endian.h>
+
+/* 3GPP TS 25.415 Section 6.6.2.1 */
+struct iuup_pdutype0_hdr {
+#if OSMO_IS_LITTLE_ENDIAN
+ /* control part */
+ uint8_t frame_nr:4,
+ pdu_type:4;
+ uint8_t rfci:6,
+ fqc:2;
+ /* checksum part */
+ uint8_t payload_crc_hi:2, header_crc:6;
+ uint8_t payload_crc_lo;
+
+ /* payload part */
+ uint8_t payload[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t pdu_type:4, frame_nr:4;
+ uint8_t fqc:2, rfci:6;
+ uint8_t header_crc:6, payload_crc_hi:2;
+ uint8_t payload_crc_lo;
+ uint8_t payload[0];
+#endif
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.2 */
+struct iuup_pdutype1_hdr {
+#if OSMO_IS_LITTLE_ENDIAN
+ /* control part */
+ uint8_t frame_nr:4,
+ pdu_type:4;
+ uint8_t rfci:6,
+ fqc:2;
+ /* checksum part */
+ uint8_t spare:2,
+ header_crc:6;
+ /* payload part */
+ uint8_t payload[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t pdu_type:4, frame_nr:4;
+ uint8_t fqc:2, rfci:6;
+ uint8_t header_crc:6, spare:2;
+ uint8_t payload[0];
+#endif
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.3 */
+struct iuup_pdutype14_hdr {
+#if OSMO_IS_LITTLE_ENDIAN
+ /* control part */
+ uint8_t frame_nr:2,
+ ack_nack:2,
+ pdu_type:4;
+ uint8_t proc_ind:4,
+ mode_version:4;
+ /* checksum part */
+ uint8_t payload_crc_hi:2, header_crc:6;
+ uint8_t payload_crc_lo;
+ /* payload part */
+ uint8_t payload[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t pdu_type:4, ack_nack:2, frame_nr:2;
+ uint8_t mode_version:4, proc_ind:4;
+ uint8_t header_crc:6, payload_crc_hi:2;
+ uint8_t payload_crc_lo;
+ uint8_t payload[0];
+#endif
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.3.4.1 */
+struct iuup_ctrl_init_rfci_hdr {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t rfci:6,
+ li:1,
+ lri:1;
+ uint8_t subflow_length[0]; /* 1 or 2 bytes depending on li */
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t lri:1, li:1, rfci:6;
+ uint8_t subflow_length[0];
+#endif
+} __attribute__((packed));
+struct iuup_ctrl_init_hdr {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t chain_ind:1,
+ num_subflows_per_rfci:3,
+ ti:1,
+ spare:3;
+ uint8_t rfci_data[0]; /* struct iuup_ctrl_init_rfci_hdr* */
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t spare:3, ti:1, num_subflows_per_rfci:3, chain_ind:1;
+ uint8_t rfci_data[0]; /* struct iuup_ctrl_init_rfci_hdr* */
+;
+#endif
+} __attribute__((packed));
+struct iuup_ctrl_init_tail {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint16_t versions_supported;
+ uint8_t spare:4,
+ data_pdu_type:4;
+ uint8_t spare_extension[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint16_t versions_supported;
+ uint8_t data_pdu_type:4, spare:4;
+ uint8_t spare_extension[0];
+#endif
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.3.4.4 */
+struct iuup_ctrl_error_event {
+#if OSMO_IS_LITTLE_ENDIAN
+ struct iuup_pdutype14_hdr hdr;
+ uint8_t error_cause:6,
+ error_distance:2;
+ uint8_t spare_extension[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ struct iuup_pdutype14_hdr hdr;
+ uint8_t error_distance:2, error_cause:6;
+ uint8_t spare_extension[0];
+#endif
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.3.2 */
+struct iuup_ctrl_ack {
+ struct iuup_pdutype14_hdr hdr;
+ uint8_t spare_extension[0];
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.3.3 */
+struct iuup_ctrl_nack {
+#if OSMO_IS_LITTLE_ENDIAN
+ struct iuup_pdutype14_hdr hdr;
+ uint8_t spare:2,
+ error_cause:6;
+ uint8_t spare_extension[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ struct iuup_pdutype14_hdr hdr;
+ uint8_t error_cause:6, spare:2;
+ uint8_t spare_extension[0];
+#endif
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2 + 6.6.3.1 */
+enum iuup_pdu_type {
+ IUUP_PDU_T_DATA_CRC = 0,
+ IUUP_PDU_T_DATA_NOCRC = 1,
+ IUUP_PDU_T_CONTROL = 14,
+};
+
+/* 3GPP TS 25.415 Section 6.6.3.2 */
+enum iuup_ack_nack {
+ IUUP_AN_PROCEDURE = 0,
+ IUUP_AN_ACK = 1,
+ IUUP_AN_NACK = 2,
+};
+
+/* 3GPP TS 25.415 Section 6.6.3.5 */
+enum iuup_fqc {
+ IUUP_FQC_FRAME_GOOD = 0,
+ IUUP_FQC_FRAME_BAD = 1,
+ IUUP_FQC_FRAME_BAD_RADIO = 2,
+};
+
+/* 3GPP TS 25.415 Section 6.6.3.7 */
+enum iuup_procedure {
+ IUUP_PROC_INIT = 0,
+ IUUP_PROC_RATE_CTRL = 1,
+ IUUP_PROC_TIME_ALIGN = 2,
+ IUUP_PROC_ERR_EVENT = 3,
+};
+
+
+/* 3GPP TS 25.415 Section 6.6.3.15 */
+enum iuup_error_distance {
+ IUUP_ERR_DIST_LOCAL = 0,
+ IUUP_ERR_DIST_FIRST_FWD = 1,
+ IUUP_ERR_DIST_SECOND_FWD = 2,
+ IUUP_ERR_DIST_RESERVED = 3,
+};
+
+
+/* 3GPP TS 25.415 Section 6.6.3.16 */
+enum iuup_error_cause {
+ IUUP_ERR_CAUSE_CRC_ERR_HDR = 0,
+ IUUP_ERR_CAUSE_CRC_ERR_DATA = 1,
+ IUUP_ERR_CAUSE_UNEXPECTED_FN = 2,
+ IUUP_ERR_CAUSE_FRAME_LOSS = 3,
+ IUUP_ERR_CAUSE_UNKNOWN_PDUTYPE = 4,
+ IUUP_ERR_CAUSE_UNKNOWN_PROC = 5,
+ IUUP_ERR_CAUSE_UNKNNOWN_RES_VAL = 6,
+ IUUP_ERR_CAUSE_UNKNNOWN_FIELD = 7,
+ IUUP_ERR_CAUSE_FRAME_TOO_SHORT = 8,
+ IUUP_ERR_CAUSE_MISSING_FIELDS = 9,
+ IUUP_ERR_CAUSE_UNEXPECTED_PDU_T = 16,
+ IUUP_ERR_CAUSE_UNEXPECTED_PROC = 18,
+ IUUP_ERR_CAUSE_UNEXPECTED_RFCI = 19,
+ IUUP_ERR_CAUSE_UNEXPECTED_VALUE = 20,
+ IUUP_ERR_CAUSE_INIT_FAILURE = 42,
+ IUUP_ERR_CAUSE_INIT_FAILURE_NET_TMR = 43,
+ IUUP_ERR_CAUSE_INIT_FAILURE_REP_NACK = 44,
+ IUUP_ERR_CAUSE_RATE_CTRL_FAILURE = 45,
+ IUUP_ERR_CAUSE_ERR_EVENT_FAIL = 46,
+ IUUP_ERR_CAUSE_TIME_ALIGN_NOTSUPP = 47,
+ IUUP_ERR_CAUSE_REQ_TIME_ALIGN_NOTPOSS = 48,
+ IUUP_ERR_CAUSE_MODE_VERSION_NOT_SUPPORTED = 49,
+};
diff --git a/include/osmocom/gsm/protocol/gsm_44_004.h b/include/osmocom/gsm/protocol/gsm_44_004.h
index 83be9bfd..c30ba0c9 100644
--- a/include/osmocom/gsm/protocol/gsm_44_004.h
+++ b/include/osmocom/gsm/protocol/gsm_44_004.h
@@ -1,19 +1,19 @@
#pragma once
+#include <osmocom/core/endian.h>
+
/* TS 44.004 Section 7.1 */
struct gsm_sacch_l1_hdr {
-#if OSMO_IS_BIG_ENDIAN
- uint8_t reserved:1,
- srr_sro:1,
- fpc_epc:1,
- ms_pwr:5;
- uint8_t ta;
-#elif OSMO_IS_LITTLE_ENDIAN
+#if OSMO_IS_LITTLE_ENDIAN
uint8_t ms_pwr:5,
fpc_epc:1,
srr_sro:1,
reserved:1;
uint8_t ta;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t reserved:1, srr_sro:1, fpc_epc:1, ms_pwr:5;
+ uint8_t ta;
#endif
} __attribute__ ((packed));
diff --git a/include/osmocom/gsm/protocol/gsm_44_060.h b/include/osmocom/gsm/protocol/gsm_44_060.h
new file mode 100644
index 00000000..1df2f800
--- /dev/null
+++ b/include/osmocom/gsm/protocol/gsm_44_060.h
@@ -0,0 +1,252 @@
+/*! \file gsm_44_060.h
+ * General Packet Radio Service (GPRS).
+ * Radio Link Control / Medium Access Control (RLC/MAC) protocol
+ * 3GPP TS 44.060
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/endian.h>
+
+/* TS 44.060 10.3a.4.1.1 */
+struct gprs_rlc_ul_header_egprs_1 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t r:1,
+ si:1,
+ cv:4,
+ tfi_hi:2;
+ uint8_t tfi_lo:3,
+ bsn1_hi:5;
+ uint8_t bsn1_lo:6,
+ bsn2_hi:2;
+ uint8_t bsn2_lo:8;
+ uint8_t cps:5,
+ rsb:1,
+ pi:1,
+ spare_hi:1;
+ uint8_t spare_lo:6,
+ dummy:2;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t tfi_hi:2, cv:4, si:1, r:1;
+ uint8_t bsn1_hi:5, tfi_lo:3;
+ uint8_t bsn2_hi:2, bsn1_lo:6;
+ uint8_t bsn2_lo:8;
+ uint8_t spare_hi:1, pi:1, rsb:1, cps:5;
+ uint8_t dummy:2, spare_lo:6;
+#endif
+} __attribute__ ((packed));
+
+/* TS 44.060 10.3a.4.2.1 */
+struct gprs_rlc_ul_header_egprs_2 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t r:1,
+ si:1,
+ cv:4,
+ tfi_hi:2;
+ uint8_t tfi_lo:3,
+ bsn1_hi:5;
+ uint8_t bsn1_lo:6,
+ cps_hi:2;
+ uint8_t cps_lo:1,
+ rsb:1,
+ pi:1,
+ spare_hi:5;
+ uint8_t spare_lo:5,
+ dummy:3;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t tfi_hi:2, cv:4, si:1, r:1;
+ uint8_t bsn1_hi:5, tfi_lo:3;
+ uint8_t cps_hi:2, bsn1_lo:6;
+ uint8_t spare_hi:5, pi:1, rsb:1, cps_lo:1;
+ uint8_t dummy:3, spare_lo:5;
+#endif
+} __attribute__ ((packed));
+
+/* TS 44.060 10.3a.4.3.1 */
+struct gprs_rlc_ul_header_egprs_3 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t r:1,
+ si:1,
+ cv:4,
+ tfi_hi:2;
+ uint8_t tfi_lo:3,
+ bsn1_hi:5;
+ uint8_t bsn1_lo:6,
+ cps_hi:2;
+ uint8_t cps_lo:2,
+ spb:2,
+ rsb:1,
+ pi:1,
+ spare:1,
+ dummy:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t tfi_hi:2, cv:4, si:1, r:1;
+ uint8_t bsn1_hi:5, tfi_lo:3;
+ uint8_t cps_hi:2, bsn1_lo:6;
+ uint8_t dummy:1, spare:1, pi:1, rsb:1, spb:2, cps_lo:2;
+#endif
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_1 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t usf:3,
+ es_p:2,
+ rrbp:2,
+ tfi_hi:1;
+ uint8_t tfi_lo:4,
+ pr:2,
+ bsn1_hi:2;
+ uint8_t bsn1_mid:8;
+ uint8_t bsn1_lo:1,
+ bsn2_hi:7;
+ uint8_t bsn2_lo:3,
+ cps:5;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
+ uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
+ uint8_t bsn1_mid:8;
+ uint8_t bsn2_hi:7, bsn1_lo:1;
+ uint8_t cps:5, bsn2_lo:3;
+#endif
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_2 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t usf:3,
+ es_p:2,
+ rrbp:2,
+ tfi_hi:1;
+ uint8_t tfi_lo:4,
+ pr:2,
+ bsn1_hi:2;
+ uint8_t bsn1_mid:8;
+ uint8_t bsn1_lo:1,
+ cps:3,
+ dummy:4;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
+ uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
+ uint8_t bsn1_mid:8;
+ uint8_t dummy:4, cps:3, bsn1_lo:1;
+#endif
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_3 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t usf:3,
+ es_p:2,
+ rrbp:2,
+ tfi_hi:1;
+ uint8_t tfi_lo:4,
+ pr:2,
+ bsn1_hi:2;
+ uint8_t bsn1_mid:8;
+ uint8_t bsn1_lo:1,
+ cps:4,
+ spb:2,
+ dummy:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
+ uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
+ uint8_t bsn1_mid:8;
+ uint8_t dummy:1, spb:2, cps:4, bsn1_lo:1;
+#endif
+} __attribute__ ((packed));
+
+/* TS 44.060 Table 12.24.2
+* Meaning of values documented in TS 23.060 Chapter 6.3.3.1: Network Mode of Operation */
+enum osmo_gprs_nmo {
+ GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */
+ GPRS_NMO_II = 1, /* all paging on CCCH */
+ GPRS_NMO_III = 2, /* no paging coordination */
+};
+
+/* TS 44.060 12.24 */
+struct osmo_gprs_cell_options {
+ enum osmo_gprs_nmo nmo;
+ /* T3168: wait for packet uplink assignment message */
+ uint32_t t3168; /* in milliseconds */
+ /* T3192: wait for release of the TBF after reception of the final block */
+ uint32_t t3192; /* in milliseconds */
+ uint32_t drx_timer_max;/* in seconds */
+ uint32_t bs_cv_max;
+ uint8_t supports_egprs_11bit_rach;
+ bool ctrl_ack_type_use_block; /* use PACKET CONTROL ACKNOWLEDGMENT */
+
+ uint8_t ext_info_present;
+ struct {
+ uint8_t egprs_supported;
+ uint8_t use_egprs_p_ch_req;
+ uint8_t bep_period;
+ uint8_t pfc_supported;
+ uint8_t dtm_supported;
+ uint8_t bss_paging_coordination;
+ bool ccn_active;
+ } ext_info;
+};
+
+/* TS 44.060 Table 12.9.2 */
+struct osmo_gprs_power_ctrl_pars {
+ uint8_t alpha;
+ uint8_t t_avg_w;
+ uint8_t t_avg_t;
+ uint8_t pc_meas_chan;
+ uint8_t n_avg_i;
+};
+
+
+/*! Structure for CPS coding and puncturing scheme (TS 44.060 10.4.8a) */
+struct egprs_cps {
+ uint8_t bits;
+ uint8_t mcs;
+ uint8_t p[2];
+};
+
+/*! CPS puncturing table selection (TS 44.060 10.4.8a) */
+enum egprs_cps_punc {
+ EGPRS_CPS_P1,
+ EGPRS_CPS_P2,
+ EGPRS_CPS_P3,
+ EGPRS_CPS_NONE = -1,
+};
+
+/*! EGPRS header types (TS 44.060 10.0a.2) */
+enum egprs_hdr_type {
+ EGPRS_HDR_TYPE1,
+ EGPRS_HDR_TYPE2,
+ EGPRS_HDR_TYPE3,
+};
+
+enum osmo_gprs_cs {
+ OSMO_GPRS_CS_NONE,
+ OSMO_GPRS_CS1,
+ OSMO_GPRS_CS2,
+ OSMO_GPRS_CS3,
+ OSMO_GPRS_CS4,
+ OSMO_GPRS_MCS1,
+ OSMO_GPRS_MCS2,
+ OSMO_GPRS_MCS3,
+ OSMO_GPRS_MCS4,
+ OSMO_GPRS_MCS5,
+ OSMO_GPRS_MCS6,
+ OSMO_GPRS_MCS7,
+ OSMO_GPRS_MCS8,
+ OSMO_GPRS_MCS9,
+ _NUM_OSMO_GPRS_CS
+};
+
+int egprs_get_cps(struct egprs_cps *cps, uint8_t type, uint8_t bits);
+
+int osmo_gprs_ul_block_size_bits(enum osmo_gprs_cs cs);
+int osmo_gprs_dl_block_size_bits(enum osmo_gprs_cs cs);
+int osmo_gprs_ul_block_size_bytes(enum osmo_gprs_cs cs);
+int osmo_gprs_dl_block_size_bytes(enum osmo_gprs_cs cs);
+enum osmo_gprs_cs osmo_gprs_ul_cs_by_block_bytes(uint8_t block_size);
+enum osmo_gprs_cs osmo_gprs_dl_cs_by_block_bytes(uint8_t block_size);
diff --git a/include/osmocom/gsm/protocol/gsm_44_068.h b/include/osmocom/gsm/protocol/gsm_44_068.h
new file mode 100644
index 00000000..3a33c163
--- /dev/null
+++ b/include/osmocom/gsm/protocol/gsm_44_068.h
@@ -0,0 +1,136 @@
+#pragma once
+#include <stdint.h>
+#include <osmocom/core/utils.h>
+
+/* Group Call Control (GCC) is an ETSI/3GPP standard protocol used between
+ * MS (Mobile Station) and MSC (Mobile Switchting Center) in 2G/GSM-R network.
+ * It is specified in 3GPP TS 44.068.
+ *
+ * (C) 2023 by Sysmocom s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Andreas Eversberg
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/* 9 Information Element Identifiers */
+enum osmo_gsm44068_iei {
+ OSMO_GSM44068_IEI_MOBILE_IDENTITY = 0x17,
+ OSMO_GSM44068_IEI_USER_USER = 0x7E,
+ OSMO_GSM44068_IEI_CALL_STATE = 0xA0,
+ OSMO_GSM44068_IEI_STATE_ATTRIBUTES = 0xB0,
+ OSMO_GSM44068_IEI_TALKER_PRIORITY = 0xC0,
+ OSMO_GSM44068_IEI_SMS_INDICATIONS = 0xD0,
+};
+
+/* 9.3 Message Type */
+enum osmo_gsm44068_msg_type {
+ OSMO_GSM44068_MSGT_IMMEDIATE_SETUP = 0x31,
+ OSMO_GSM44068_MSGT_SETUP = 0x32,
+ OSMO_GSM44068_MSGT_CONNECT = 0x33,
+ OSMO_GSM44068_MSGT_TERMINATION = 0x34,
+ OSMO_GSM44068_MSGT_TERMINATION_REQUEST = 0x35,
+ OSMO_GSM44068_MSGT_TERMINATION_REJECT = 0x36,
+ OSMO_GSM44068_MSGT_STATUS = 0x38,
+ OSMO_GSM44068_MSGT_GET_STATUS = 0x39,
+ OSMO_GSM44068_MSGT_SET_PARAMETER = 0x3a,
+ OSMO_GSM44068_MSGT_IMMEDIATE_SETUP_2 = 0x3b,
+};
+
+/* Table 9.2 priority */
+enum osmo_gsm44068_priority_level {
+ OSMO_GSM44068_PRIO_LEVEL_4 = 0x1,
+ OSMO_GSM44068_PRIO_LEVEL_3 = 0x2,
+ OSMO_GSM44068_PRIO_LEVEL_2 = 0x3,
+ OSMO_GSM44068_PRIO_LEVEL_1 = 0x4,
+ OSMO_GSM44068_PRIO_LEVEL_0 = 0x5,
+ OSMO_GSM44068_PRIO_LEVEL_B = 0x6,
+ OSMO_GSM44068_PRIO_LEVEL_A = 0x7,
+};
+
+/* 9.4.2 Call State */
+enum osmo_gsm44068_call_state {
+ OSMO_GSM44068_CSTATE_U0 = 0x0,
+ OSMO_GSM44068_CSTATE_U1 = 0x1,
+ OSMO_GSM44068_CSTATE_U2sl_U2 = 0x2,
+ OSMO_GSM44068_CSTATE_U3 = 0x3,
+ OSMO_GSM44068_CSTATE_U4 = 0x4,
+ OSMO_GSM44068_CSTATE_U5 = 0x5,
+ OSMO_GSM44068_CSTATE_U0p = 0x6,
+ OSMO_GSM44068_CSTATE_U2wr_U6 = 0x7,
+ OSMO_GSM44068_CSTATE_U2r = 0x8,
+ OSMO_GSM44068_CSTATE_U2ws = 0x9,
+ OSMO_GSM44068_CSTATE_U2sr = 0xa,
+ OSMO_GSM44068_CSTATE_U2nc = 0xb,
+};
+
+/* 9.4.3 Cause */
+enum osmo_gsm44068_cause {
+ OSMO_GSM44068_CAUSE_ILLEGAL_MS = 0x03,
+ OSMO_GSM44068_CAUSE_IMEI_NOT_ACCEPTED = 0x05,
+ OSMO_GSM44068_CAUSE_ILLEGAL_ME = 0x06,
+ OSMO_GSM44068_CAUSE_SERVICE_NOT_AUTHORIZED = 0x08,
+ OSMO_GSM44068_CAUSE_APP_NOT_SUPPORTED_ON_PROTO = 0x09,
+ OSMO_GSM44068_CAUSE_RR_CONNECTION_ABORTED = 0x0a,
+ OSMO_GSM44068_CAUSE_NORMAL_CALL_CLEARING = 0x10,
+ OSMO_GSM44068_CAUSE_NETWORK_FAILURE = 0x11,
+ OSMO_GSM44068_CAUSE_BUSY = 0x14,
+ OSMO_GSM44068_CAUSE_CONGESTION = 0x16,
+ OSMO_GSM44068_CAUSE_USER_NOT_ORIGINATOR = 0x17,
+ OSMO_GSM44068_CAUSE_NET_WANTS_TO_MAINTAIN_CALL = 0x18,
+ OSMO_GSM44068_CAUSE_RESPONSE_TO_GET_STATUS = 0x1e,
+ OSMO_GSM44068_CAUSE_SERVICE_OPTION_NOT_SUBSCR = 0x20,
+ OSMO_GSM44068_CAUSE_REQUESTED_SERVICE_NOT_SUB = 0x21,
+ OSMO_GSM44068_CAUSE_SERVICE_OPTION_OOO = 0x22,
+ OSMO_GSM44068_CAUSE_CALL_CANNOT_BE_IDENTIFIED = 0x26,
+ OSMO_GSM44068_CAUSE_RETRY_UPON_ENTRY_NEW_CALL = 0x30, /* up to 0x3f */
+ OSMO_GSM44068_CAUSE_INVALID_TRANSACTION_ID = 0x51,
+ OSMO_GSM44068_CAUSE_SEMANTICALLY_INCORRECT_MSG = 0x5f,
+ OSMO_GSM44068_CAUSE_INVALID_MANDATORY_INFO = 0x60,
+ OSMO_GSM44068_CAUSE_MESSAGE_TYPE_NON_EXISTENT = 0x61,
+ OSMO_GSM44068_CAUSE_MESSAGE_TYPE_NOT_COMPAT = 0x62,
+ OSMO_GSM44068_CAUSE_IE_NON_EXISTENT = 0x63,
+ OSMO_GSM44068_CAUSE_IE_NOT_COMPAT = 0x64,
+ OSMO_GSM44068_CAUSE_PROTOCOL_ERROR = 0x70,
+};
+
+/* 9.4.4 Originator Indication */
+#define OSMO_GSM44068_OI_MS_IS_ORIGINATOR 0x01
+
+/* 9.4.7 State Attributes */
+#define OSMO_GSM44068_DA_DOWNLINK_ATTACHED 0x08
+#define OSMO_GSM44068_UA_UPLINK_ATTACHED 0x04
+#define OSMO_GSM44068_COMM_T 0x02
+
+/* 9.4.9 Talker Priority */
+enum osmo_gsm44068_talker_priority {
+ OSMO_GSM44068_PRIO_NORMAL = 0x0,
+ OSMO_GSM44068_PRIO_PRIVILEGED = 0x1,
+ OSMO_GSM44068_PRIO_EMERGENCY = 0x2,
+};
+
+/* 9.4.10 SMS Indications */
+#define OSMO_GSM44068_DC_DATA_CONFIDENTALLY_RQD 0x02
+#define OSMO_GSM44068_GP_GUARANTEED_PRIVACY_RQD 0x01
+
+extern const struct value_string osmo_gsm44068_msg_type_names[];
+extern const struct value_string osmo_gsm44068_priority_level_names[];
+extern const struct value_string osmo_gsm44068_cause_names[];
+extern const struct value_string osmo_gsm44068_call_state_names[];
+extern const struct value_string osmo_gsm44068_talker_priority_names[];
+
+extern const struct tlv_definition osmo_gsm44068_att_tlvdef;
diff --git a/include/osmocom/gsm/protocol/gsm_44_318.h b/include/osmocom/gsm/protocol/gsm_44_318.h
index b3942be2..f31a80ae 100644
--- a/include/osmocom/gsm/protocol/gsm_44_318.h
+++ b/include/osmocom/gsm/protocol/gsm_44_318.h
@@ -162,7 +162,7 @@ struct gan_rc_csr_hdr {
uint8_t msg_type;
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint16_t len;
uint8_t skip_ind:4, pdisc:4;
uint8_t msg_type;
@@ -190,7 +190,7 @@ struct gan_cch_desc_ie {
spare2:2;
uint8_t access_class[2];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t mscr:1, att:1, dtm:1, gprs:1, nmo:2, ecmc:1, spare:1;
uint8_t t3212;
uint8_t rac;
diff --git a/include/osmocom/gsm/protocol/gsm_48_071.h b/include/osmocom/gsm/protocol/gsm_48_071.h
index fb9653ab..961211b3 100644
--- a/include/osmocom/gsm/protocol/gsm_48_071.h
+++ b/include/osmocom/gsm/protocol/gsm_48_071.h
@@ -20,10 +20,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
diff --git a/include/osmocom/gsm/protocol/gsm_49_031.h b/include/osmocom/gsm/protocol/gsm_49_031.h
index c6152e17..463fabf5 100644
--- a/include/osmocom/gsm/protocol/gsm_49_031.h
+++ b/include/osmocom/gsm/protocol/gsm_49_031.h
@@ -20,15 +20,13 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
+
+#include <osmocom/core/endian.h>
#include <osmocom/gsm/protocol/gsm_48_071.h>
#include <osmocom/gsm/protocol/gsm_23_032.h>
#include <osmocom/gsm/gsm0808_utils.h>
@@ -62,6 +60,22 @@ struct lcs_cause_ie {
uint8_t diag_val;
};
+/* 3GPP TS 49.031 10.16 LCS QoS IE */
+struct osmo_bssmap_le_lcs_qos {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t vert:1, vel:1, spare1:6;
+ uint8_t ha_val:7, ha_ind:1;
+ uint8_t va_val:7, va_ind:1;
+ uint8_t spare3:6, rt:2;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t spare1:6, vel:1, vert:1;
+ uint8_t ha_ind:1, ha_val:7;
+ uint8_t va_ind:1, va_val:7;
+ uint8_t rt:2, spare3:6;
+#endif
+} __attribute__ ((packed));
+
enum bssap_le_msg_discr {
BSSAP_LE_MSG_DISCR_BSSMAP_LE = 0,
};
@@ -171,7 +185,15 @@ struct bssmap_le_perform_loc_req {
bool apdu_present;
struct bsslap_pdu apdu;
- bool more_items; /*!< always set this to false */
+ bool more_items; /*!< set this to true iff any fields below are used */
+
+ bool lcs_priority_present;
+ uint8_t lcs_priority; /*!< see in 3GPP TS 29.002 */
+
+ bool lcs_qos_present;
+ struct osmo_bssmap_le_lcs_qos lcs_qos;
+
+ bool more_items2; /*!< always set this to false */
};
struct bssmap_le_perform_loc_resp {
diff --git a/include/osmocom/gsm/protocol/ipaccess.h b/include/osmocom/gsm/protocol/ipaccess.h
index 4f1d0b16..51827607 100644
--- a/include/osmocom/gsm/protocol/ipaccess.h
+++ b/include/osmocom/gsm/protocol/ipaccess.h
@@ -39,6 +39,7 @@ enum ipaccess_proto_ext {
IPAC_PROTO_EXT_GSUP = 0x05, /* GSUP GPRS extension */
IPAC_PROTO_EXT_OAP = 0x06, /* Osmocom Authn Protocol */
IPAC_PROTO_EXT_RSPRO = 0x07, /* Remote SIM protocol */
+ IPAC_PROTO_EXT_PCU = 0x08, /* BSC<->BTS<->PCU communication */
};
enum ipaccess_msgtype {
diff --git a/include/osmocom/gsm/rlp.h b/include/osmocom/gsm/rlp.h
new file mode 100644
index 00000000..47b0a6c2
--- /dev/null
+++ b/include/osmocom/gsm/rlp.h
@@ -0,0 +1,81 @@
+/*
+ * GSM RLP (Radio Link Protocol) as used in CSD (3GPP TS 44.022)
+ *
+ * Copyright (C) 2022-2023 Harald Welte <laforge@osmocom.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+#include <osmocom/core/utils.h>
+
+/*! \defgroup rlp GSM RLP (Radio Link Protocol) as used in CSD (3GPP TS 24.022)
+ * @{
+ * \file rlp.h */
+
+/*! RLP frame type as per 3GPP TS 24.022 Section 5.2.1 */
+enum osmo_rlp_ftype {
+ OSMO_RLP_FT_U,
+ OSMO_RLP_FT_S,
+ OSMO_RLP_FT_IS,
+};
+extern const struct value_string osmo_rlp_ftype_vals[];
+
+/*! RLP U-Frame Type as per 3GPP TS 24.022 Section 5.2.1 */
+enum osmo_rlp_u_ftype {
+ OSMO_RLP_U_FT_SABM = 0x07,
+ OSMO_RLP_U_FT_UA = 0x0c,
+ OSMO_RLP_U_FT_DISC = 0x08,
+ OSMO_RLP_U_FT_DM = 0x03,
+ OSMO_RLP_U_FT_NULL = 0x0f,
+ OSMO_RLP_U_FT_UI = 0x00,
+ OSMO_RLP_U_FT_XID = 0x17,
+ OSMO_RLP_U_FT_TEST = 0x1c,
+ OSMO_RLP_U_FT_REMAP = 0x11,
+};
+extern const struct value_string osmo_rlp_ftype_u_vals[];
+
+/*! RLP S-Frame type as per 3GPP TS 24.022 Section 5.2.1 */
+enum osmo_rlp_s_ftype {
+ OSMO_RLP_S_FT_RR = 0,
+ OSMO_RLP_S_FT_REJ = 2,
+ OSMO_RLP_S_FT_RNR = 1,
+ OSMO_RLP_S_FT_SREJ = 3,
+};
+extern const struct value_string osmo_rlp_ftype_s_vals[];
+
+/*! Data structure representing one decoded RLP frame */
+struct osmo_rlp_frame_decoded {
+ uint8_t version;
+ enum osmo_rlp_ftype ftype;
+ enum osmo_rlp_u_ftype u_ftype;
+ enum osmo_rlp_s_ftype s_ftype;
+ bool c_r;
+ bool p_f;
+ uint8_t s_bits;
+ uint16_t n_s;
+ uint16_t n_r;
+ uint32_t fcs;
+ uint8_t info[536/8];
+ uint16_t info_len;
+};
+
+int osmo_rlp_decode(struct osmo_rlp_frame_decoded *out, uint8_t version, const uint8_t *data, size_t data_len);
+int osmo_rlp_encode(uint8_t *out, size_t out_size, const struct osmo_rlp_frame_decoded *in);
+uint32_t osmo_rlp_fcs_compute(const uint8_t *in, size_t in_len);
+
+/*! @} */
diff --git a/include/osmocom/gsm/tlv.h b/include/osmocom/gsm/tlv.h
index 7e6dfb5f..28e897d7 100644
--- a/include/osmocom/gsm/tlv.h
+++ b/include/osmocom/gsm/tlv.h
@@ -322,7 +322,7 @@ static inline uint8_t *v_put(uint8_t *buf, uint8_t val)
}
/*! put (append) a TV field */
-static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag,
+static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag,
uint8_t val)
{
*buf++ = tag;
@@ -344,7 +344,7 @@ static inline uint8_t *tv_fixed_put(uint8_t *buf, uint8_t tag,
* \param[in] tag Tag value
* \param[in] val Value (in host byte order!)
*/
-static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag,
+static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag,
uint16_t val)
{
*buf++ = tag;
@@ -457,6 +457,16 @@ static inline uint8_t *msgb_tv16_push(struct msgb *msg, uint8_t tag, uint16_t va
return buf;
}
+/*! push (prepend) a TV32 field to a \ref msgb
+ * \returns pointer to first byte of newly-pushed information */
+static inline uint8_t *msgb_tv32_push(struct msgb *msg, uint8_t tag, uint32_t val)
+{
+ uint8_t *buf = msgb_push(msg, 5);
+ *buf++ = tag;
+ osmo_store32be(val, buf);
+ return buf;
+}
+
/*! push (prepend) a TvLV field to a \ref msgb
* \returns pointer to first byte of newly-pushed information */
static inline uint8_t *msgb_tvlv_push(struct msgb *msg, uint8_t tag, uint16_t len,
@@ -543,7 +553,7 @@ int tlv_encode(struct msgb *msg, const struct tlv_definition *def, const struct
int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp,
const uint8_t *tag_order, unsigned int tag_order_len);
-#define TLVP_PRESENT(x, y) ((x)->lv[y].val)
+#define TLVP_PRESENT(x, y) (!!((x)->lv[y].val))
#define TLVP_LEN(x, y) (x)->lv[y].len
#define TLVP_VAL(x, y) (x)->lv[y].val
diff --git a/include/osmocom/isdn/Makefile.am b/include/osmocom/isdn/Makefile.am
new file mode 100644
index 00000000..fbc92d8b
--- /dev/null
+++ b/include/osmocom/isdn/Makefile.am
@@ -0,0 +1,8 @@
+osmoisdn_HEADERS = \
+ i460_mux.h \
+ lapd_core.h \
+ v110.h \
+ v110_ta.h \
+ $(NULL)
+
+osmoisdndir = $(includedir)/osmocom/isdn
diff --git a/include/osmocom/isdn/i460_mux.h b/include/osmocom/isdn/i460_mux.h
new file mode 100644
index 00000000..537e3257
--- /dev/null
+++ b/include/osmocom/isdn/i460_mux.h
@@ -0,0 +1,120 @@
+/*! \file i460_mux.h
+ * ITU-T I.460 sub-channel multiplexer + demultiplexer */
+/*
+ * (C) 2020 by Harald Welte <laforge@gnumonks.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#pragma once
+#include <stdint.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/msgb.h>
+
+#define OSMO_I460_NUM_SUBCHAN 8
+
+/* I.460 sub-slot rate */
+enum osmo_i460_rate {
+ OSMO_I460_RATE_NONE, /* disabled */
+ OSMO_I460_RATE_64k,
+ OSMO_I460_RATE_32k,
+ OSMO_I460_RATE_16k,
+ OSMO_I460_RATE_8k,
+};
+
+struct osmo_i460_subchan;
+
+typedef void (*out_cb_bits_t)(struct osmo_i460_subchan *schan, void *user_data,
+ const ubit_t *bits, unsigned int num_bits);
+typedef void (*out_cb_bytes_t)(struct osmo_i460_subchan *schan, void *user_data,
+ const uint8_t *bytes, unsigned int num_bytes);
+
+struct osmo_i460_subchan_demux {
+ /*! bit-buffer for output bits */
+ uint8_t *out_bitbuf;
+ /*! size of out_bitbuf in bytes */
+ unsigned int out_bitbuf_size;
+ /*! offset of next bit to be written in out_bitbuf */
+ unsigned int out_idx;
+ /*! callback to be called once we have received out_bitbuf_size bits */
+ out_cb_bits_t out_cb_bits;
+ out_cb_bytes_t out_cb_bytes;
+ void *user_data;
+};
+
+typedef void (*in_cb_queue_empty_t)(struct osmo_i460_subchan *schan, void *user_data);
+
+struct osmo_i460_subchan_mux {
+ /*! list of to-be-transmitted message buffers */
+ struct llist_head tx_queue;
+ in_cb_queue_empty_t in_cb_queue_empty;
+ void *user_data;
+};
+
+struct osmo_i460_subchan {
+ struct osmo_i460_timeslot *ts; /* back-pointer */
+ enum osmo_i460_rate rate; /* 8/16/32/64k */
+ uint8_t bit_offset; /* bit offset inside each byte of the B-channel */
+ struct osmo_i460_subchan_demux demux;
+ struct osmo_i460_subchan_mux mux;
+};
+
+struct osmo_i460_timeslot {
+ struct osmo_i460_subchan schan[OSMO_I460_NUM_SUBCHAN];
+};
+
+/*! description of a sub-channel; passed by caller */
+struct osmo_i460_schan_desc {
+ enum osmo_i460_rate rate;
+ uint8_t bit_offset;
+ struct {
+ /* size (in bits) of the internal buffer; determines granularity */
+ size_t num_bits;
+ /*! call-back function called whenever we received num_bits */
+ out_cb_bits_t out_cb_bits;
+ /*! out_cb_bytes call-back function called whenever we received num_bits.
+ * The user is usually expected to provide either out_cb_bits or out_cb_bytes. If only
+ * out_cb_bits is provided, output data will always be provided as unpacked bits; if only
+ * out_cb_bytes is provided, output data will always be provided as packet bits (bytes). If
+ * both are provided, it is up to the I.460 multiplex to decide if it calls either of the two,
+ * depending on what can be provided without extra conversion. */
+ out_cb_bytes_t out_cb_bytes;
+ /* opaque user data pointer to pass to out_cb */
+ void *user_data;
+ } demux;
+
+ struct {
+ /* call-back function whenever the muxer requires more input data from the sub-channels,
+ * but has nothing enqueued yet. A typical function would then call osmo_i460_mux_enqueue() */
+ in_cb_queue_empty_t in_cb_queue_empty;
+ /* opaque user data pointer to pass to in_cb */
+ void *user_data;
+ } mux;
+};
+
+void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len);
+
+void osmo_i460_mux_enqueue(struct osmo_i460_subchan *schan, struct msgb *msg);
+int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len);
+
+void osmo_i460_ts_init(struct osmo_i460_timeslot *ts);
+
+struct osmo_i460_subchan *
+osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd);
+
+void osmo_i460_subchan_del(struct osmo_i460_subchan *schan);
+
+int osmo_i460_subchan_count(struct osmo_i460_timeslot *ts);
+
+/*! @} */
diff --git a/include/osmocom/isdn/lapd_core.h b/include/osmocom/isdn/lapd_core.h
new file mode 100644
index 00000000..1e010afe
--- /dev/null
+++ b/include/osmocom/isdn/lapd_core.h
@@ -0,0 +1,192 @@
+/*! \file lapd_core.h
+ * primitive related stuff
+ */
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/prim.h>
+
+/*! \defgroup lapd LAPD implementation common part
+ * @{
+ * \file lapd_core.h
+ */
+
+#define LOGDL(dl, level, fmt, args...) \
+ LOGP(DLLAPD, level, "(%s) " fmt, (dl)->name, ## args)
+
+/*! LAPD related primitives (L2<->L3 SAP)*/
+enum osmo_dl_prim {
+ PRIM_DL_UNIT_DATA, /*!< DL-UNIT-DATA */
+ PRIM_DL_DATA, /*!< DL-DATA */
+ PRIM_DL_EST, /*!< DL-ESTABLISH */
+ PRIM_DL_REL, /*!< DL-RLEEASE */
+ PRIM_DL_SUSP, /*!< DL-SUSPEND */
+ PRIM_DL_RES, /*!< DL-RESUME */
+ PRIM_DL_RECON, /*!< DL-RECONNECT */
+ PRIM_MDL_ERROR, /*!< MDL-ERROR */
+};
+
+/* Uses the same values as RLL, so no conversion for GSM is required. */
+#define MDL_CAUSE_T200_EXPIRED 0x01
+#define MDL_CAUSE_REEST_REQ 0x02
+#define MDL_CAUSE_UNSOL_UA_RESP 0x03
+#define MDL_CAUSE_UNSOL_DM_RESP 0x04
+#define MDL_CAUSE_UNSOL_DM_RESP_MF 0x05
+#define MDL_CAUSE_UNSOL_SPRV_RESP 0x06
+#define MDL_CAUSE_SEQ_ERR 0x07
+#define MDL_CAUSE_UFRM_INC_PARAM 0x08
+#define MDL_CAUSE_SFRM_INC_PARAM 0x09
+#define MDL_CAUSE_IFRM_INC_MBITS 0x0a
+#define MDL_CAUSE_IFRM_INC_LEN 0x0b
+#define MDL_CAUSE_FRM_UNIMPL 0x0c
+#define MDL_CAUSE_SABM_MF 0x0d
+#define MDL_CAUSE_SABM_INFO_NOTALL 0x0e
+#define MDL_CAUSE_FRMR 0x0f
+
+/*! for MDL-ERROR.ind */
+struct mdl_error_ind_param {
+ uint8_t cause; /*!< generic cause value */
+};
+
+/*! for DL-REL.req */
+struct dl_rel_req_param {
+ uint8_t mode; /*!< release mode */
+};
+
+/*! primitive header for LAPD DL-SAP primitives */
+struct osmo_dlsap_prim {
+ struct osmo_prim_hdr oph; /*!< generic primitive header */
+ union {
+ struct mdl_error_ind_param error_ind;
+ struct dl_rel_req_param rel_req;
+ } u; /*!< request-specific data */
+};
+
+/*! LAPD mode/role */
+enum lapd_mode {
+ LAPD_MODE_USER, /*!< behave like user */
+ LAPD_MODE_NETWORK, /*!< behave like network */
+};
+
+/*! LAPD state (Figure B.2/Q.921)*/
+enum lapd_state {
+ LAPD_STATE_NULL = 0,
+ LAPD_STATE_TEI_UNASS,
+ LAPD_STATE_ASS_TEI_WAIT,
+ LAPD_STATE_EST_TEI_WAIT,
+ LAPD_STATE_IDLE,
+ LAPD_STATE_SABM_SENT,
+ LAPD_STATE_DISC_SENT,
+ LAPD_STATE_MF_EST,
+ LAPD_STATE_TIMER_RECOV,
+};
+
+/*! lapd_flags */
+#define LAPD_F_RTS 0x0001
+#define LAPD_F_DROP_2ND_REJ 0x0002
+
+/*! LAPD T200 state in RTS mode */
+enum lapd_t200_rts {
+ LAPD_T200_RTS_OFF = 0,
+ LAPD_T200_RTS_PENDING,
+ LAPD_T200_RTS_RUNNING,
+};
+
+/*! LAPD message format (I / S / U) */
+enum lapd_format {
+ LAPD_FORM_UKN = 0,
+ LAPD_FORM_I,
+ LAPD_FORM_S,
+ LAPD_FORM_U,
+};
+
+/*! LAPD message context */
+struct lapd_msg_ctx {
+ struct lapd_datalink *dl;
+ int n201;
+ /* address */
+ uint8_t cr;
+ uint8_t sapi;
+ uint8_t tei;
+ uint8_t lpd;
+ /* control */
+ uint8_t format;
+ uint8_t p_f; /* poll / final bit */
+ uint8_t n_send;
+ uint8_t n_recv;
+ uint8_t s_u; /* S or repectivly U function bits */
+ /* length */
+ int length;
+ uint8_t more;
+};
+
+struct lapd_cr_ent {
+ uint8_t cmd;
+ uint8_t resp;
+};
+
+struct lapd_history {
+ struct msgb *msg; /* message to be sent / NULL, if histoy is empty */
+ int more; /* if message is fragmented */
+};
+
+/*! LAPD datalink */
+struct lapd_datalink {
+ int (*send_dlsap)(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
+ int (*send_ph_data_req)(struct lapd_msg_ctx *lctx, struct msgb *msg);
+ int (*update_pending_frames)(struct lapd_msg_ctx *lctx);
+ struct {
+ /*! filled-in once we set the lapd_mode above */
+ struct lapd_cr_ent loc2rem;
+ struct lapd_cr_ent rem2loc;
+ } cr;
+ enum lapd_mode mode; /*!< current mode of link */
+ unsigned int lapd_flags; /*!< \ref lapd_flags to change processing */
+ int use_sabme; /*!< use SABME instead of SABM */
+ int reestablish; /*!< enable reestablish support */
+ int n200, n200_est_rel; /*!< number of retranmissions */
+ struct lapd_msg_ctx lctx; /*!< LAPD context */
+ int maxf; /*!< maximum frame size (after defragmentation) */
+ uint8_t k; /*!< maximum number of unacknowledged frames */
+ uint8_t v_range; /*!< range of sequence numbers */
+ uint8_t v_send; /*!< seq nr of next I frame to be transmitted */
+ uint8_t v_ack; /*!< last frame ACKed by peer */
+ uint8_t v_recv; /*!< seq nr of next I frame expected to be received */
+ uint32_t state; /*!< LAPD state (\ref lapd_state) */
+ int seq_err_cond; /*!< condition of sequence error */
+ uint8_t own_busy; /*!< receiver busy on our side */
+ uint8_t peer_busy; /*!< receiver busy on remote side */
+ int t200_sec, t200_usec; /*!< retry timer (default 1 sec) */
+ int t203_sec, t203_usec; /*!< retry timer (default 10 secs) */
+ enum lapd_t200_rts t200_rts; /*!< state of T200 in RTS mode */
+ struct osmo_timer_list t200; /*!< T200 timer */
+ struct osmo_timer_list t203; /*!< T203 timer */
+ uint8_t retrans_ctr; /*!< re-transmission counter */
+ struct llist_head tx_queue; /*!< frames to L1 */
+ struct llist_head send_queue; /*!< frames from L3 */
+ struct msgb *send_buffer; /*!< current frame transmitting */
+ int send_out; /*!< how much was sent from send_buffer */
+ struct lapd_history *tx_hist; /*!< tx history structure array */
+ uint8_t range_hist; /*!< range of history buffer 2..2^n */
+ struct msgb *rcv_buffer; /*!< buffer to assemble the received message */
+ struct msgb *cont_res; /*!< buffer to store content resolution data on network side, to detect multiple phones on same channel */
+ char *name; /*!< user-provided name */
+};
+
+void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf)
+ OSMO_DEPRECATED("Use lapd_dl_init2() instead");
+void lapd_dl_init2(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf, const char *name);
+void lapd_dl_set_name(struct lapd_datalink *dl, const char *name);
+void lapd_dl_exit(struct lapd_datalink *dl);
+void lapd_dl_reset(struct lapd_datalink *dl);
+int lapd_dl_set_flags(struct lapd_datalink *dl, unsigned int flags);
+int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode);
+int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx);
+int lapd_ph_rts_ind(struct lapd_msg_ctx *lctx);
+int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
+int lapd_t200_timeout(struct lapd_datalink *dl);
+
+/*! @} */
diff --git a/include/osmocom/isdn/v110.h b/include/osmocom/isdn/v110.h
new file mode 100644
index 00000000..9555932e
--- /dev/null
+++ b/include/osmocom/isdn/v110.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <osmocom/core/bits.h>
+
+/* See Section 5.1.2.1 of ITU-T V.110 */
+#define MAX_D_BITS 48
+#define MAX_E_BITS 7
+#define MAX_S_BITS 9
+#define MAX_X_BITS 2
+
+/*! a 'decoded' representation of a single V.110 frame. contains unpacket D, E, S and X bits */
+struct osmo_v110_decoded_frame {
+ ubit_t d_bits[MAX_D_BITS];
+ ubit_t e_bits[MAX_E_BITS];
+ ubit_t s_bits[MAX_S_BITS];
+ ubit_t x_bits[MAX_X_BITS];
+};
+
+int osmo_v110_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits);
+int osmo_v110_encode_frame(ubit_t *ra_bits, size_t n_bits, const struct osmo_v110_decoded_frame *fr);
+
+void osmo_v110_ubit_dump(FILE *outf, const ubit_t *fr, size_t in_len);
+
+
+/*! enum for each supported V.110 synchronous RA1 function (one for each user bitrate) */
+enum osmo_v100_sync_ra1_rate {
+ OSMO_V110_SYNC_RA1_600,
+ OSMO_V110_SYNC_RA1_1200,
+ OSMO_V110_SYNC_RA1_2400,
+ OSMO_V110_SYNC_RA1_4800,
+ OSMO_V110_SYNC_RA1_7200,
+ OSMO_V110_SYNC_RA1_9600,
+ OSMO_V110_SYNC_RA1_12000,
+ OSMO_V110_SYNC_RA1_14400,
+ OSMO_V110_SYNC_RA1_19200,
+ OSMO_V110_SYNC_RA1_24000,
+ OSMO_V110_SYNC_RA1_28800,
+ OSMO_V110_SYNC_RA1_38400,
+ _NUM_OSMO_V110_SYNC_RA1
+};
+
+extern const ubit_t osmo_v110_e1e2e3[_NUM_OSMO_V110_SYNC_RA1][3];
+
+#define osmo_v110_e1e2e3_set(e_bits, rate) \
+ memcpy(e_bits, osmo_v110_e1e2e3[rate], 3)
+#define osmo_v110_e1e2e3_cmp(e_bits, rate) \
+ memcmp(e_bits, osmo_v110_e1e2e3[rate], 3)
+
+int osmo_v110_sync_ra1_get_user_data_chunk_bitlen(enum osmo_v100_sync_ra1_rate rate);
+int osmo_v110_sync_ra1_get_user_data_rate(enum osmo_v100_sync_ra1_rate rate);
+int osmo_v110_sync_ra1_get_intermediate_rate(enum osmo_v100_sync_ra1_rate rate);
+
+int osmo_v110_sync_ra1_user_to_ir(enum osmo_v100_sync_ra1_rate rate, struct osmo_v110_decoded_frame *fr,
+ const ubit_t *d_in, size_t in_len);
+
+int osmo_v110_sync_ra1_ir_to_user(enum osmo_v100_sync_ra1_rate rate, ubit_t *d_out, size_t out_len,
+ const struct osmo_v110_decoded_frame *fr);
diff --git a/include/osmocom/isdn/v110_ta.h b/include/osmocom/isdn/v110_ta.h
new file mode 100644
index 00000000..b6bf7b52
--- /dev/null
+++ b/include/osmocom/isdn/v110_ta.h
@@ -0,0 +1,113 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/isdn/v110.h>
+
+/* Definition of this struct is [intentionally] kept private */
+struct osmo_v110_ta;
+
+/*! V.110 5.4.1 Local flow control (DTE-DCE or TE-TA) mode */
+enum osmo_v110_local_flow_ctrl_mode {
+ OSMO_V110_LOCAL_FLOW_CTRL_NONE, /*!< No local flow control */
+ OSMO_V110_LOCAL_FLOW_CTRL_133_106, /*!< 5.4.1.1 133/106 operation */
+ OSMO_V110_LOCAL_FLOW_CTRL_105_106, /*!< 5.4.1.2 105/106 operation */
+ OSMO_V110_LOCAL_FLOW_CTRL_XON_XOFF, /*!< 5.4.1.3 XON/XOFF operation */
+};
+
+/*! Configuration for a V.110 TA instance */
+struct osmo_v110_ta_cfg {
+ /*! Configuration flags (behavior switches and quirks) */
+ unsigned int flags;
+ /*! Synchronous user rate */
+ enum osmo_v100_sync_ra1_rate rate;
+
+ /*! Flow control configuration */
+ struct {
+ /*! Local TA-TE (DTE-DCE) flow control mode */
+ enum osmo_v110_local_flow_ctrl_mode local;
+ /*! End-to-end (TA-to-TA) flow control state */
+ bool end_to_end;
+ } flow_ctrl;
+
+ /*! Opaque application-private data; passed to call-backs. */
+ void *priv;
+
+ /*! Receive call-back of the application.
+ * \param[in] priv opaque application-private data.
+ * \param[in] buf output buffer for writing to be transmitted data.
+ * \param[in] buf_size size of the output buffer. */
+ void (*rx_cb)(void *priv, const ubit_t *buf, size_t buf_size);
+
+ /*! Transmit call-back of the application.
+ * \param[in] priv opaque application-private data.
+ * \param[out] buf output buffer for writing to be transmitted data.
+ * \param[in] buf_size size of the output buffer. */
+ void (*tx_cb)(void *priv, ubit_t *buf, size_t buf_size);
+
+ /*! Modem status line update call-back (optional).
+ * \param[in] priv opaque application-private data.
+ * \param[in] status updated status; bit-mask of OSMO_V110_TA_C_*. */
+ void (*status_update_cb)(void *priv, unsigned int status);
+};
+
+struct osmo_v110_ta *osmo_v110_ta_alloc(void *ctx, const char *name,
+ const struct osmo_v110_ta_cfg *cfg);
+void osmo_v110_ta_free(struct osmo_v110_ta *ta);
+
+/*! Various timers for a V.110 TA instance */
+enum osmo_v110_ta_timer {
+ /*! 7.1.5 Loss of frame synchronization: sync recovery timer.
+ * T-number is not assigned in V.110, so we call it X1. */
+ OSMO_V110_TA_TIMER_X1 = -1,
+ /*! 7.1.2 Connect TA to line: sync establishment timer */
+ OSMO_V110_TA_TIMER_T1 = 1,
+ /*! 7.1.4 Disconnect mode: disconnect confirmation timer */
+ OSMO_V110_TA_TIMER_T2 = 2,
+};
+
+int osmo_v110_ta_set_timer_val_ms(struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_timer timer,
+ unsigned long val_ms);
+
+int osmo_v110_ta_frame_in(struct osmo_v110_ta *ta, const struct osmo_v110_decoded_frame *in);
+int osmo_v110_ta_frame_out(struct osmo_v110_ta *ta, struct osmo_v110_decoded_frame *out);
+
+int osmo_v110_ta_sync_ind(struct osmo_v110_ta *ta);
+int osmo_v110_ta_desync_ind(struct osmo_v110_ta *ta);
+
+/*! ITU-T Table 9 "Interchange circuit" (see also ITU-T V.24 Chapter 3).
+ * XXX: Not all circuits are present here, only those which we actually use.
+ * TODO: add human-friendly abbreviated circuit names. */
+enum osmo_v110_ta_circuit {
+ OSMO_V110_TA_C_105, /*!< DTE->DCE | RTS (Request to Send) */
+ OSMO_V110_TA_C_106, /*!< DTE<-DCE | CTS (Clear to Send) */
+ OSMO_V110_TA_C_107, /*!< DTE<-DCE | DSR (Data Set Ready) */
+ OSMO_V110_TA_C_108, /*!< DTE->DCE | DTR (Data Terminal Ready) */
+ OSMO_V110_TA_C_109, /*!< DTE<-DCE | DCD (Data Carrier Detect) */
+ OSMO_V110_TA_C_133, /*!< DTE->DCE | Ready for receiving */
+};
+
+extern const struct value_string osmo_v110_ta_circuit_names[];
+extern const struct value_string osmo_v110_ta_circuit_descs[];
+
+/*! Get a short name of the given TA's circuit (format: NNN[/ABBR]). */
+static inline const char *osmo_v110_ta_circuit_name(enum osmo_v110_ta_circuit circuit)
+{
+ return get_value_string(osmo_v110_ta_circuit_names, circuit);
+}
+
+/*! Get a brief description of the given TA's circuit. */
+static inline const char *osmo_v110_ta_circuit_desc(enum osmo_v110_ta_circuit circuit)
+{
+ return get_value_string(osmo_v110_ta_circuit_descs, circuit);
+}
+
+unsigned int osmo_v110_ta_get_status(const struct osmo_v110_ta *ta);
+bool osmo_v110_ta_get_circuit(const struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_circuit circuit);
+int osmo_v110_ta_set_circuit(struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_circuit circuit, bool active);
diff --git a/include/osmocom/sim/Makefile.am b/include/osmocom/sim/Makefile.am
new file mode 100644
index 00000000..bcc9459f
--- /dev/null
+++ b/include/osmocom/sim/Makefile.am
@@ -0,0 +1,6 @@
+osmosim_HEADERS = \
+ class_tables.h \
+ sim.h
+ $(NULL)
+
+osmosimdir = $(includedir)/osmocom/sim
diff --git a/include/osmocom/sim/class_tables.h b/include/osmocom/sim/class_tables.h
index d5be39dd..ec9ec490 100644
--- a/include/osmocom/sim/class_tables.h
+++ b/include/osmocom/sim/class_tables.h
@@ -11,10 +11,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
diff --git a/include/osmocom/sim/sim.h b/include/osmocom/sim/sim.h
index 16b9f1fa..639682a0 100644
--- a/include/osmocom/sim/sim.h
+++ b/include/osmocom/sim/sim.h
@@ -2,8 +2,7 @@
* Routines for helping with SIM (ISO/IEC 7816-4 more generally) communication.
*/
-#ifndef _OSMOCOM_SIM_H
-#define _OSMOCOM_SIM_H
+#pragma once
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
@@ -375,6 +374,8 @@ struct osim_reader_ops {
const char *name;
struct osim_reader_hdl *(*reader_open)(int idx, const char *name, void *ctx);
struct osim_card_hdl *(*card_open)(struct osim_reader_hdl *rh, enum osim_proto proto);
+ int (*card_reset)(struct osim_card_hdl *card, bool cold_reset);
+ int (*card_close)(struct osim_card_hdl *card);
int (*transceive)(struct osim_reader_hdl *rh, struct msgb *msg);
};
@@ -441,4 +442,5 @@ int osim_transceive_apdu(struct osim_chan_hdl *st, struct msgb *amsg);
struct osim_reader_hdl *osim_reader_open(enum osim_reader_driver drv, int idx,
const char *name, void *ctx);
struct osim_card_hdl *osim_card_open(struct osim_reader_hdl *rh, enum osim_proto proto);
-#endif /* _OSMOCOM_SIM_H */
+int osim_card_reset(struct osim_card_hdl *card, bool cold_reset);
+int osim_card_close(struct osim_card_hdl *card);
diff --git a/include/osmocom/usb/Makefile.am b/include/osmocom/usb/Makefile.am
new file mode 100644
index 00000000..3b2b8b4d
--- /dev/null
+++ b/include/osmocom/usb/Makefile.am
@@ -0,0 +1,7 @@
+if ENABLE_LIBUSB
+osmousb_HEADERS = \
+ libusb.h \
+ $(NULL)
+endif
+
+osmousbdir = $(includedir)/osmocom/usb
diff --git a/include/osmocom/usb/libusb.h b/include/osmocom/usb/libusb.h
index 9ad3f71a..33caa862 100644
--- a/include/osmocom/usb/libusb.h
+++ b/include/osmocom/usb/libusb.h
@@ -12,10 +12,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <libusb.h>
diff --git a/include/osmocom/vty/Makefile.am b/include/osmocom/vty/Makefile.am
new file mode 100644
index 00000000..adb05dcb
--- /dev/null
+++ b/include/osmocom/vty/Makefile.am
@@ -0,0 +1,17 @@
+if ENABLE_VTY
+osmovty_HEADERS = \
+ buffer.h \
+ command.h \
+ logging.h \
+ stats.h \
+ misc.h \
+ telnet_interface.h \
+ vector.h \
+ vty.h \
+ ports.h \
+ cpu_sched_vty.h \
+ tdef_vty.h \
+ $(NULL)
+endif
+
+osmovtydir = $(includedir)/osmocom/vty
diff --git a/include/osmocom/vty/command.h b/include/osmocom/vty/command.h
index 228dc5b8..61b58815 100644
--- a/include/osmocom/vty/command.h
+++ b/include/osmocom/vty/command.h
@@ -99,7 +99,7 @@ enum node_type {
L_CS7_SCCPADDR_NODE, /*!< SS7 SCCP Address */
L_CS7_SCCPADDR_GT_NODE, /*!< SS7 SCCP Global Title */
- L_CPU_SCHED_NODE, /*!< CPU Sched related options node */
+ L_CPU_SCHED_NODE, /*!< CPU Sched related options node */
L_NS_BIND_NODE, /*!< NS bind node */
L_NS_NSE_NODE, /*!< NS NSE node */
/*
@@ -442,8 +442,8 @@ char *argv_concat(const char **argv, int argc, int shift);
vector cmd_make_strvec(const char *);
int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p);
void cmd_free_strvec(vector);
-vector cmd_describe_command();
-char **cmd_complete_command();
+vector cmd_describe_command(vector vline, struct vty *vty, int *status);
+char **cmd_complete_command(vector vline, struct vty *vty, int *status);
const char *cmd_prompt(enum node_type);
int config_from_file(struct vty *, FILE *);
enum node_type node_parent(enum node_type);
@@ -457,7 +457,7 @@ extern struct cmd_element config_exit_cmd;
extern struct cmd_element config_help_cmd;
extern struct cmd_element config_list_cmd;
extern struct cmd_element config_end_cmd;
-char *host_config_file();
+const char *host_config_file(void);
void host_config_set(const char *);
char *osmo_asciidoc_escape(const char *inp);
@@ -483,4 +483,6 @@ extern const struct value_string vty_ref_gen_mode_desc[];
int vty_dump_xml_ref_mode(FILE *stream, enum vty_ref_gen_mode mode);
int vty_dump_xml_ref(FILE *stream) OSMO_DEPRECATED("Use vty_dump_xml_ref_mode() instead");
+int vty_cmd_range_match(const char *range, const char *str);
+
/*! @} */
diff --git a/include/osmocom/vty/logging.h b/include/osmocom/vty/logging.h
index 90c8fa17..b3ce92c7 100644
--- a/include/osmocom/vty/logging.h
+++ b/include/osmocom/vty/logging.h
@@ -6,7 +6,13 @@
#define FILTER_STR "Filter log messages\n"
struct log_info;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+/* note this undefined argument declaration is intentional. There used
+ * to be an argument until 2017 which we no longer need .*/
void logging_vty_add_cmds();
+#pragma GCC diagnostic pop
void logging_vty_add_deprecated_subsys(void *ctx, const char *name);
struct vty;
struct log_target *osmo_log_vty2tgt(struct vty *vty);
diff --git a/include/osmocom/vty/misc.h b/include/osmocom/vty/misc.h
index ea31c5b0..f031c4ab 100644
--- a/include/osmocom/vty/misc.h
+++ b/include/osmocom/vty/misc.h
@@ -14,15 +14,23 @@ char *vty_cmd_string_from_valstr(void *ctx, const struct value_string *vals,
void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
struct rate_ctr_group *ctrg);
+void vty_out_rate_ctr_group2(struct vty *vty, const char *prefix,
+ struct rate_ctr_group *ctrg, bool skip_zero);
void vty_out_rate_ctr_group_fmt(struct vty *vty, const char *fmt,
struct rate_ctr_group *ctrg);
+void vty_out_rate_ctr_group_fmt2(struct vty *vty, const char *fmt,
+ struct rate_ctr_group *ctrg, bool skip_zero);
+
void vty_out_stat_item_group(struct vty *vty, const char *prefix,
struct osmo_stat_item_group *statg);
+void vty_out_stat_item_group2(struct vty *vty, const char *prefix,
+ struct osmo_stat_item_group *statg, bool skip_zero);
void vty_out_statistics_full(struct vty *vty, const char *prefix);
-void vty_out_statistics_partial(struct vty *vty, const char *prefix,
- int max_level);
+void vty_out_statistics_full2(struct vty *vty, const char *prefix, bool skip_zero);
+void vty_out_statistics_partial(struct vty *vty, const char *prefix, int max_level);
+void vty_out_statistics_partial2(struct vty *vty, const char *prefix, int max_level, bool skip_zero);
struct osmo_fsm;
diff --git a/include/osmocom/vty/ports.h b/include/osmocom/vty/ports.h
index b3550d15..bc001282 100644
--- a/include/osmocom/vty/ports.h
+++ b/include/osmocom/vty/ports.h
@@ -8,33 +8,43 @@
#pragma once
+#define OSMO_VTY_PORT_PCAP_CLIENT 4227
+#define OSMO_VTY_PORT_PCAP_SERVER 4228
/* 4236 used by control interface */
-#define OSMO_VTY_PORT_TRX 4237
+#define OSMO_VTY_PORT_TRX 4237
/* 4238 used by osmo-bts control interface */
-#define OSMO_VTY_PORT_STP 4239
-#define OSMO_VTY_PORT_PCU 4240 /* also: osmo_pcap_client */
-#define OSMO_VTY_PORT_BTS 4241 /* also: osmo_pcap_server */
-#define OSMO_VTY_PORT_NITB_BSC 4242
-#define OSMO_VTY_PORT_BSC_MGCP 4243
-#define OSMO_VTY_PORT_MGW OSMO_VTY_PORT_BSC_MGCP
-#define OSMO_VTY_PORT_BSC_NAT 4244
-#define OSMO_VTY_PORT_SGSN 4245
-#define OSMO_VTY_PORT_GBPROXY 4246
-#define OSMO_VTY_PORT_BB 4247
+#define OSMO_VTY_PORT_STP 4239
+#define OSMO_VTY_PORT_PCU 4240
+#define OSMO_VTY_PORT_BTS 4241
+#define OSMO_VTY_PORT_NITB_BSC 4242
+#define OSMO_VTY_PORT_BSC_MGCP 4243
+#define OSMO_VTY_PORT_MGW OSMO_VTY_PORT_BSC_MGCP
+#define OSMO_VTY_PORT_BSC_NAT 4244
+#define OSMO_VTY_PORT_SGSN 4245
+#define OSMO_VTY_PORT_GBPROXY 4246
+#define OSMO_VTY_PORT_BB 4247
/* 4249-4251 used by control interface */
-#define OSMO_VTY_PORT_BTSMGR 4252
-#define OSMO_VTY_PORT_GTPHUB 4253
-#define OSMO_VTY_PORT_MSC 4254
+#define OSMO_VTY_PORT_BTSMGR 4252
+#define OSMO_VTY_PORT_GTPHUB 4253
+#define OSMO_VTY_PORT_MSC 4254
/* 4255 used by control interface */
-#define OSMO_VTY_PORT_MNCC_SIP 4256
+#define OSMO_VTY_PORT_MNCC_SIP 4256
/* 4257 used by control interface */
-#define OSMO_VTY_PORT_HLR 4258
+#define OSMO_VTY_PORT_HLR 4258
/* 4259 used by control interface */
-#define OSMO_VTY_PORT_GGSN 4260
-#define OSMO_VTY_PORT_HNBGW 4261
+#define OSMO_VTY_PORT_GGSN 4260
+#define OSMO_VTY_PORT_HNBGW 4261
/* 4262-4263 used by control interface */
-#define OSMO_VTY_PORT_CBC 4264
-#define OSMO_VTY_PORT_UECUPS 4268
-#define OSMO_VTY_PORT_E1D 4269
-#define OSMO_VTY_PORT_SMLC 4271
+#define OSMO_VTY_PORT_CBC 4264
+#define OSMO_VTY_PORT_UECUPS 4268
+#define OSMO_VTY_PORT_E1D 4269
+#define OSMO_VTY_PORT_ISDNTAP 4270
+#define OSMO_VTY_PORT_SMLC 4271
+/* 4272 used by control interface */
+#define OSMO_VTY_PORT_HNODEB 4273
+/* 4274 used by control interface */
+#define OSMO_VTY_PORT_UPF 4275
+/* 4276: OSMO_CTRL_PORT_UPF */
+#define OSMO_VTY_PORT_PFCP_TOOL 4277
+/* 4278: OSMO_CTRL_PORT_UPF */
/* When adding/changing port numbers, keep docs and wiki in sync. See above. */
diff --git a/include/osmocom/vty/telnet_interface.h b/include/osmocom/vty/telnet_interface.h
index da7cf839..73b79df1 100644
--- a/include/osmocom/vty/telnet_interface.h
+++ b/include/osmocom/vty/telnet_interface.h
@@ -14,14 +14,11 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
+#include <osmocom/core/defs.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/select.h>
@@ -45,10 +42,13 @@ struct telnet_connection {
struct log_target *dbg;
};
-int telnet_init(void *tall_ctx, void *priv, int port);
-int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port);
int telnet_init_default(void *tall_ctx, void *priv, int default_port);
+int telnet_init(void *tall_ctx, void *priv, int port)
+ OSMO_DEPRECATED("This function ignores dynamic port configuration. Use telnet_init_default() instead");
+int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
+ OSMO_DEPRECATED("This function ignores dynamic port configuration. Use telnet_init_default() instead");
+
void telnet_exit(void);
/*! @} */
diff --git a/include/osmocom/vty/vector.h b/include/osmocom/vty/vector.h
index 0a639ad9..ac524bc3 100644
--- a/include/osmocom/vty/vector.h
+++ b/include/osmocom/vty/vector.h
@@ -14,11 +14,6 @@
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Zebra; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
*/
#pragma once
diff --git a/include/osmocom/vty/vty.h b/include/osmocom/vty/vty.h
index d34433fa..3a2ec6f6 100644
--- a/include/osmocom/vty/vty.h
+++ b/include/osmocom/vty/vty.h
@@ -3,6 +3,7 @@
#include <stdio.h>
#include <stdarg.h>
#include <stdbool.h>
+#include <time.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/defs.h>
@@ -55,20 +56,6 @@ enum vty_type {
VTY_SHELL_SERV
};
-struct vty_parent_node {
- struct llist_head entry;
-
- /*! private data, specified by creator */
- void *priv;
-
- /*! Node status of this vty */
- int node;
-
- /*! When reading from a config file, these are the indenting characters expected for children of
- * this VTY node. */
- char *indent;
-};
-
/*! Internal representation of a single VTY */
struct vty {
/*! underlying file (if any) */
@@ -141,7 +128,7 @@ struct vty {
#define TELNET_NAWS_SB_LEN 5
/*! sub-negotiation buffer */
unsigned char sb_buf[TELNET_NAWS_SB_LEN];
- /*! How many subnegotiation characters have we received?
+ /*! How many subnegotiation characters have we received?
*
* We just drop those that do not fit in the buffer. */
size_t sb_len;
@@ -210,6 +197,7 @@ struct vty_app_info {
/* Prototypes. */
void vty_init(struct vty_app_info *app_info);
int vty_read_config_file(const char *file_name, void *priv);
+int vty_read_config_filep(FILE *confp, void *priv);
void vty_init_vtysh (void);
void vty_reset (void);
struct vty *vty_new (void);
@@ -218,9 +206,11 @@ bool vty_is_active(struct vty *vty);
int vty_out (struct vty *, const char *, ...) VTY_PRINTF_ATTRIBUTE(2, 3);
int vty_out_va(struct vty *vty, const char *format, va_list ap);
int vty_out_newline(struct vty *);
+int vty_out_uptime(struct vty *vty, const struct timespec *starttime);
int vty_read(struct vty *vty);
//void vty_time_print (struct vty *, int);
void vty_close (struct vty *);
+void vty_flush(struct vty *vty);
char *vty_get_cwd (void);
void vty_log (const char *level, const char *proto, const char *fmt, va_list);
int vty_config_lock (struct vty *);
diff --git a/libosmocodec.pc.in b/libosmocodec.pc.in
index 9e058ef7..0c4de8d5 100644
--- a/libosmocodec.pc.in
+++ b/libosmocodec.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom Codec related utilities Library
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmocodec
-Cflags: -I${includedir}/ @TALLOC_CFLAGS@
-
+Requires: talloc, libosmocore
+Libs: -L${libdir} -losmocodec
+Cflags: -I${includedir}/
diff --git a/libosmocoding.pc.in b/libosmocoding.pc.in
index d1d03c42..5b91a606 100644
--- a/libosmocoding.pc.in
+++ b/libosmocoding.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom L1 transcoding Library
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmocoding -losmocodec -losmogsm -losmocore
-Cflags: -I${includedir}/ @TALLOC_CFLAGS@
-
+Requires: talloc, libosmocore, libosmogsm, libosmocodec
+Libs: -L${libdir} -losmocoding
+Cflags: -I${includedir}/
diff --git a/libosmocore.pc.in b/libosmocore.pc.in
index 80f17c8b..6479221e 100644
--- a/libosmocore.pc.in
+++ b/libosmocore.pc.in
@@ -6,6 +6,8 @@ includedir=@includedir@
Name: Osmocom Core Library
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmocore
+Requires: talloc @LIBMNL_PC@
+Requires.private: @LIBSCTP_PC@
+Libs: -L${libdir} -losmocore
Libs.private: @PTHREAD_LIBS@ @LIBSCTP_LIBS@
-Cflags: -I${includedir}/ @TALLOC_CFLAGS@ @PTHREAD_CFLAGS@
+Cflags: -I${includedir}/ @PTHREAD_CFLAGS@
diff --git a/libosmoctrl.pc.in b/libosmoctrl.pc.in
index 4676b31d..e9947dce 100644
--- a/libosmoctrl.pc.in
+++ b/libosmoctrl.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom Control Interface Library
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmoctrl -losmogsm -losmocore
+Requires: talloc, libosmocore, libosmogsm
+Libs: -L${libdir} -losmoctrl
Cflags: -I${includedir}/
-
diff --git a/libosmogb.pc.in b/libosmogb.pc.in
index a163cc42..5573bd98 100644
--- a/libosmogb.pc.in
+++ b/libosmogb.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom GPRS Gb Library
Description: Osmocom GPRS Gb Interface (NS/BSSGP) Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmogb -losmovty -losmocore
+Requires: talloc, libosmocore, libosmovty, libosmogsm
+Libs: -L${libdir} -losmogb
Cflags: -I${includedir}/ -fno-strict-aliasing
-
diff --git a/libosmogsm.pc.in b/libosmogsm.pc.in
index 0160be8d..17b9212a 100644
--- a/libosmogsm.pc.in
+++ b/libosmogsm.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom GSM Core Library
Description: GSM Core Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmogsm -losmocore
+Requires: talloc, libosmocore, libosmoisdn
+Libs: -L${libdir} -losmogsm
Cflags: -I${includedir}/
-
diff --git a/libosmoisdn.pc.in b/libosmoisdn.pc.in
new file mode 100644
index 00000000..fedbb694
--- /dev/null
+++ b/libosmoisdn.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Osmocom ISDN Library
+Description: ISDN Library
+Version: @VERSION@
+Requires: talloc, libosmocore
+Libs: -L${libdir} -losmoisdn
+Cflags: -I${includedir}/
diff --git a/libosmosim.pc.in b/libosmosim.pc.in
index 83777c31..a8f50d6f 100644
--- a/libosmosim.pc.in
+++ b/libosmosim.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom SIM card related utilities Library
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmosim -losmocore
+Requires: talloc, libosmocore
+Libs: -L${libdir} -losmosim
Cflags: -I${includedir}/
-
diff --git a/libosmousb.pc.in b/libosmousb.pc.in
index ce6d2715..dbedb0e5 100644
--- a/libosmousb.pc.in
+++ b/libosmousb.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom libusb (USB) integration
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmousb -losmocore
+Requires: talloc, libusb-1.0, libosmocore
+Libs: -L${libdir} -losmousb
Cflags: -I${includedir}/
-
diff --git a/libosmovty.pc.in b/libosmovty.pc.in
index 6204e455..5d9286a2 100644
--- a/libosmovty.pc.in
+++ b/libosmovty.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom VTY Interface Library
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmovty -losmocore
+Requires: talloc, libosmocore
+Libs: -L${libdir} -losmovty
Cflags: -I${includedir}/
-
diff --git a/osmo-release.sh b/osmo-release.sh
index aecbfd1a..e947fe43 100755
--- a/osmo-release.sh
+++ b/osmo-release.sh
@@ -223,7 +223,7 @@ if [ "z$NEW_VER" = "z" ]; then
exit 1
fi
GIT_TOPDIR="$(git rev-parse --show-toplevel)"
-LIBVERS=`git grep -n LIBVERSION | grep '=' | grep am | grep -v LDFLAGS`
+LIBVERS=`git grep -n LIBVERSION | grep '=' | grep am | grep -v LDFLAGS | grep -v osmo-release.sh`
MAKEMOD=`git diff --cached -GLIBVERSION --stat | grep Makefile.am`
ISODATE=`date -I`
@@ -235,7 +235,9 @@ check_debian_patch_apply
if [ "z$LIBVERS" != "z" ]; then
if [ "z$MAKEMOD" = "z" ] && [ "z$ALLOW_NO_LIBVERSION_CHANGE" = "z0" ]; then
- echo "ERROR: Before releasing, please modify some of the libversions: $LIBVERS"
+ echo "ERROR: Before releasing, please modify some of the libversions:"
+ for l in $LIBVERS; do echo " $l"; done
+ echo "After making changes, add modified file(s) to the index using git-add."
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
@@ -279,7 +281,7 @@ gbp dch \
--new-version="$NEW_VER_WITH_EPOCH"
dch -r -m --distribution "unstable" ""
git add ${GIT_TOPDIR}/debian/changelog
-bumpversion --current-version $VERSION $REL --tag --commit --tag-name $NEW_VER --allow-dirty
+$BUMPVER --current-version $VERSION $REL --tag --commit --tag-name $NEW_VER --allow-dirty
git commit --amend # let the user add extra information to the release commit.
git tag -s $NEW_VER -f -m "Release v$NEW_VER on $ISODATE."
echo "Release $NEW_VER prepared, tagged and signed."
diff --git a/src/Makefile.am b/src/Makefile.am
index 2f18d092..86066466 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,99 +1,13 @@
-# 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=17:0:0
-
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
-AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) $(LIBSCTP_CFLAGS) $(LIBMNL_CFLAGS)
-
-if ENABLE_PSEUDOTALLOC
-AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
-endif
-
-lib_LTLIBRARIES = libosmocore.la
-
-libosmocore_la_LIBADD = $(BACKTRACE_LIB) $(TALLOC_LIBS) $(LIBRARY_RT) $(PTHREAD_LIBS) $(LIBSCTP_LIBS)
-libosmocore_la_SOURCES = context.c timer.c timer_gettimeofday.c timer_clockgettime.c \
- select.c signal.c msgb.c bits.c \
- bitvec.c bitcomp.c counter.c fsm.c \
- write_queue.c utils.c socket.c \
- logging.c logging_syslog.c logging_gsmtap.c rate_ctr.c \
- gsmtap_util.c crc16.c panic.c backtrace.c \
- conv.c application.c rbtree.c strrb.c \
- loggingrb.c crc8gen.c crc16gen.c crc32gen.c crc64gen.c \
- macaddr.c stat_item.c stats.c stats_statsd.c prim.c \
- conv_acc.c conv_acc_generic.c sercomm.c prbs.c \
- isdnhdlc.c \
- tdef.c \
- thread.c \
- sockaddr_str.c \
- use_count.c \
- exec.c \
- it_q.c \
- probes.d \
- $(NULL)
-
-if HAVE_SSSE3
-libosmocore_la_SOURCES += conv_acc_sse.c
-if HAVE_SSE4_1
-conv_acc_sse.lo : AM_CFLAGS += -mssse3 -msse4.1
-else
-conv_acc_sse.lo : AM_CFLAGS += -mssse3
-endif
-
-if HAVE_AVX2
-libosmocore_la_SOURCES += conv_acc_sse_avx.c
-if HAVE_SSE4_1
-conv_acc_sse_avx.lo : AM_CFLAGS += -mssse3 -mavx2 -msse4.1
-else
-conv_acc_sse_avx.lo : AM_CFLAGS += -mssse3 -mavx2
-endif
-endif
-endif
-
-if HAVE_NEON
-libosmocore_la_SOURCES += conv_acc_neon.c
-# conv_acc_neon.lo : AM_CFLAGS += -mfpu=neon no, could as well be vfp with neon
-endif
-
-BUILT_SOURCES = crc8gen.c crc16gen.c crc32gen.c crc64gen.c
-EXTRA_DIST = conv_acc_sse_impl.h conv_acc_neon_impl.h crcXXgen.c.tpl
-
-libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
-
-if ENABLE_PLUGIN
-libosmocore_la_SOURCES += plugin.c
-libosmocore_la_LIBADD += $(LIBRARY_DLOPEN)
-endif
-
-if ENABLE_MSGFILE
-libosmocore_la_SOURCES += msgfile.c
-endif
-
-if ENABLE_SERIAL
-libosmocore_la_SOURCES += serial.c
-endif
-
-if ENABLE_SYSTEMD_LOGGING
-libosmocore_la_SOURCES += logging_systemd.c
-libosmocore_la_LIBADD += $(SYSTEMD_LIBS)
-endif
-
-if ENABLE_LIBMNL
-libosmocore_la_SOURCES += mnl.c
-libosmocore_la_LIBADD += $(LIBMNL_LIBS)
-endif
-
-if ENABLE_SYSTEMTAP
-probes.h: probes.d
- $(DTRACE) -C -h -s $< -o $@
-
-probes.lo: probes.d
- $(LIBTOOL) --mode=compile $(AM_V_lt) --tag=CC env CFLAGS="$(CFLAGS)" $(DTRACE) -C -G -s $< -o $@
-
-BUILT_SOURCES += probes.h probes.lo
-libosmocore_la_LIBADD += probes.lo
-endif
-
-crc%gen.c: crcXXgen.c.tpl
- $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
+SUBDIRS = \
+ core \
+ vty \
+ isdn \
+ codec \
+ gsm \
+ coding \
+ gb \
+ ctrl \
+ pseudotalloc \
+ sim \
+ usb \
+ $(NULL) \ No newline at end of file
diff --git a/src/codec/Makefile.am b/src/codec/Makefile.am
index 778eb2ad..bb01b9d3 100644
--- a/src/codec/Makefile.am
+++ b/src/codec/Makefile.am
@@ -1,10 +1,10 @@
# 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=4:0:0
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS)
-AM_CFLAGS = -Wall
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
if ENABLE_PSEUDOTALLOC
AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
@@ -13,6 +13,14 @@ endif
lib_LTLIBRARIES = libosmocodec.la
-libosmocodec_la_SOURCES = gsm610.c gsm620.c gsm660.c gsm690.c ecu.c ecu_fr.c
+libosmocodec_la_SOURCES = \
+ gsm610.c \
+ gsm620.c \
+ gsm660.c \
+ gsm690.c \
+ ecu.c \
+ ecu_fr.c \
+ ecu_fr_old.c \
+ $(NULL)
libosmocodec_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
-libosmocodec_la_LIBADD = $(top_builddir)/src/libosmocore.la
+libosmocodec_la_LIBADD = $(top_builddir)/src/core/libosmocore.la
diff --git a/src/codec/ecu.c b/src/codec/ecu.c
index db7148ce..cdb3e62e 100644
--- a/src/codec/ecu.c
+++ b/src/codec/ecu.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/* As the developer and copyright holder of the related code, I hereby
@@ -48,7 +44,7 @@ static const struct osmo_ecu_ops *g_ecu_ops[_NUM_OSMO_ECU_CODECS];
/*! initialize an ECU instance for given codec.
* \param[in] ctx talloc context from which to allocate
- * \parma[in] codec codec for which to initialize/create ECU */
+ * \param[in] codec codec for which to initialize/create ECU */
struct osmo_ecu_state *osmo_ecu_init(void *ctx, enum osmo_ecu_codec codec)
{
if (codec >= ARRAY_SIZE(g_ecu_ops))
@@ -100,6 +96,20 @@ int osmo_ecu_frame_out(struct osmo_ecu_state *st, uint8_t *frame_out)
return g_ecu_ops[st->codec]->frame_out(st, frame_out);
}
+/*! check if the current state of this ECU is a DTX pause.
+ * \param[in] st ECU state/instance on which to operate
+ * \return true if DTX pause, false otherwise */
+bool osmo_ecu_is_dtx_pause(struct osmo_ecu_state *st)
+{
+ if (st->codec >= ARRAY_SIZE(g_ecu_ops))
+ return false;
+ if (!g_ecu_ops[st->codec])
+ return false;
+ if (!g_ecu_ops[st->codec]->is_dtx_pause)
+ return false;
+ return g_ecu_ops[st->codec]->is_dtx_pause(st);
+}
+
/***********************************************************************
* low-level API for ECU implementations
***********************************************************************/
diff --git a/src/codec/ecu_fr.c b/src/codec/ecu_fr.c
index 4545172a..45ba0cc8 100644
--- a/src/codec/ecu_fr.c
+++ b/src/codec/ecu_fr.c
@@ -1,9 +1,15 @@
/*
* (C) 2017 by sysmocom - s.f.m.c. GmbH
* (C) 2017 by Philipp Maier <pmaier@sysmocom.de>
- *
* All Rights Reserved
*
+ * Significantly reworked in 2023 by Mother
+ * Mychaela N. Falconia <falcon@freecalypso.org> - however,
+ * Mother Mychaela's contributions are NOT subject to copyright.
+ * No rights reserved, all rights relinquished.
+ * Portions of this code are based on Themyscira libgsmfrp,
+ * a public domain library by the same author.
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -14,10 +20,46 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
+ * The present ECU implementation for GSM-FR is closely based on the
+ * TS 46.011 spec from 3GPP; more specifically, it is based on the
+ * Example solution presented in Chapter 6 of that spec, adapted for
+ * libosmocodec ECU architecture, and comes as close to fulfilling
+ * the spec's officially stated requirements (Chapter 5) as is
+ * possible within this Osmocom-imposed architecture. Please note
+ * the following areas where the present implementation fails to
+ * fulfill the original intent of GSM spec authors:
+ *
+ * - The "lost SID" criterion, defined in GSM 06.31, is based on the
+ * TAF bit from the Radio Subsystem. However, libosmocodec ECU API
+ * does not include this flag, thus spec requirements related to
+ * lost SID conditions cannot be implemented in a strictly compliant
+ * manner. The present implementation improvises its own "lost SID"
+ * detector (not strictly spec-compliant) by counting frame_out()
+ * calls in between good traffic frame inputs via frame_in().
+ *
+ * - In the architecture envisioned and assumed in the GSM specs,
+ * the ECU function of GSM 06.11 was never intended to be a fully
+ * modular component with its own bona fide I/O interfaces - this
+ * approach appears to be an Osmocom invention - instead this ECU
+ * function was intended to be subsumed in the Rx DTX handler
+ * component of GSM 06.31, also incorporating the comfort noise
+ * generator of GSM 06.12 - and unlike the narrower-scope ECU,
+ * this slightly-larger-scope Rx DTX handler is a modular component
+ * with well-defined I/O interfaces. In the case of BFI conditions
+ * following a SID, GSM 06.11 spec was written with the assumption
+ * that the ECU controls the comfort noise generator via internal
+ * signals, as opposed to emitting "corrected" SID frames on a
+ * modular interface going to a CN generator located somewhere else.
+ * Thus the "correct" behavior for a fully modularized ECU is unclear,
+ * and an argument can be made that the very existence of such a
+ * fully modularized ECU is incorrect in itself. The present
+ * implementation re-emits a "rejuvenated" form of the last saved
+ * SID frame during BFI conditions following a SID within the
+ * permitted window of 48 frames, then starts emitting muted SIDs
+ * with Xmaxc decreasing by 4 on each frame, and finally switches
+ * to emitting non-SID silence frames (Table 1 of TS 46.011)
+ * once Xmaxc reaches 0.
*/
#include <stdbool.h>
@@ -25,144 +67,216 @@
#include <stdint.h>
#include <errno.h>
-#include <osmocom/core/bitvec.h>
+#include <osmocom/core/prbs.h>
-#include <osmocom/codec/gsm610_bits.h>
#include <osmocom/codec/codec.h>
#include <osmocom/codec/ecu.h>
+#include <osmocom/core/linuxlist.h>
+
+/* See TS 46.011, Chapter 6 Example solution */
+#define GSM611_XMAXC_REDUCE 4
+
+/* The first 5 bytes of RTP encoding neatly contain the magic nibble
+ * and LARc parameters, which also happens to be the part of SID frames
+ * that needs to be passed through as-is. */
+#define SID_PREFIX_LEN 5
+
+enum ecu_principal_state {
+ STATE_NO_DATA,
+ STATE_SPEECH,
+ STATE_SP_MUTING,
+ STATE_SID,
+ STATE_SID_MUTING,
+};
-/* See also GSM 06.11, chapter 6 Example solution */
-#define GSM610_XMAXC_REDUCE 4
-#define GSM610_XMAXC_LEN 6
+struct fr_ecu_state {
+ struct osmo_ecu_state ecu_state;
+ enum ecu_principal_state pr_state;
+ uint8_t speech_frame[GSM_FR_BYTES];
+ uint8_t sid_prefix[SID_PREFIX_LEN];
+ uint8_t sid_xmaxc;
+ uint8_t sid_reemit_count;
+ struct osmo_prbs_state prng;
+ bool last_input_was_sid;
+};
-/**
- * Reduce the XMAXC field. When the XMAXC field reaches
- * zero the function will return true.
+/* This function is the frame input to the ECU - all inputs to this
+ * function have been received by the Radio Subsystem as good traffic
+ * frames in the GSM 06.31 definition.
*/
-static bool reduce_xmaxcr(struct bitvec *frame_bitvec,
- const unsigned int index)
+static void fr_ecu_input(struct fr_ecu_state *fr, const uint8_t *frame)
{
- unsigned int field_index;
- uint64_t field;
-
- field_index = index;
- field = bitvec_read_field(frame_bitvec, &field_index, GSM610_XMAXC_LEN);
- if (field > GSM610_XMAXC_REDUCE)
- field -= GSM610_XMAXC_REDUCE;
- else
- field = 0;
-
- field_index = index;
- bitvec_write_field(frame_bitvec, &field_index, field, GSM610_XMAXC_LEN);
-
- return field == 0;
+ enum osmo_gsm631_sid_class sidc;
+
+ sidc = osmo_fr_sid_classify(frame);
+ switch (sidc) {
+ case OSMO_GSM631_SID_CLASS_SPEECH:
+ memcpy(fr->speech_frame, frame, GSM_FR_BYTES);
+ fr->pr_state = STATE_SPEECH;
+ fr->last_input_was_sid = false;
+ return;
+ case OSMO_GSM631_SID_CLASS_INVALID:
+ /* GSM 06.31 section 6.1.2 says: "an invalid SID frame
+ * shall be substituted by the last valid SID frame
+ * and the procedure for valid SID frames be applied."
+ * However, libosmocodec ECU architecture prevents us
+ * from doing what the spec says: the frame_in() method
+ * gets a const frame that can't be modified, and
+ * frame_out() will never get called when BFI=0, even
+ * when the "good traffic frame" (in the BFI=0 sense)
+ * is an invalid SID by the bit-counting rule.
+ * Thus there is no place where we can re-emit a cached
+ * copy of the last valid SID upon receiving an invalid SID.
+ *
+ * In the standard GSM architecture this problem never
+ * arises because the ECU is not a separate component
+ * but is coupled with the CN generator, thus the output
+ * from the Rx DTX handler block will be a CN frame,
+ * for both valid-SID and invalid-SID inputs to the block.
+ * But what can we do within the constraints of libosmocodec
+ * ECU framework? We treat the invalid SID almost like a
+ * BFI, doing almost nothing in the frame_in() method,
+ * but we reset sid_reemit_count because by the rules of
+ * GSM 06.31 an invalid SID is still an accepted SID frame
+ * for the purpose of "lost SID" logic. */
+ fr->sid_reemit_count = 0;
+ fr->last_input_was_sid = true;
+ return;
+ case OSMO_GSM631_SID_CLASS_VALID:
+ /* save LARc part */
+ memcpy(fr->sid_prefix, frame, SID_PREFIX_LEN);
+ /* save Xmaxc from the last subframe */
+ fr->sid_xmaxc = ((frame[27] & 0x1F) << 1) | (frame[28] >> 7);
+ fr->pr_state = STATE_SID;
+ fr->sid_reemit_count = 0;
+ fr->last_input_was_sid = true;
+ return;
+ default:
+ /* There are only 3 possible SID classifications per GSM 06.31
+ * section 6.1.1, thus any other return value is a grave error
+ * in the code. */
+ OSMO_ASSERT(0);
+ }
}
-/**
- * Reduce all XMAXC fields in the frame. When all XMAXC fields
- * reach zero, then the function will return true.
+/* Reduce all 4 Xmaxc fields in the frame. When all 4 Xmaxc fields
+ * reach 0, the function will return true for "mute".
*/
-static bool reduce_xmaxcr_all(struct bitvec *frame_bitvec)
+static bool reduce_xmaxc(uint8_t *frame)
{
- bool silent = true;
-
- silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC00);
- silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC10);
- silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC20);
- silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC30);
-
- return silent;
+ bool mute_flag = true;
+ uint8_t sub, xmaxc;
+
+ for (sub = 0; sub < 4; sub++) {
+ xmaxc = ((frame[sub*7+6] & 0x1F) << 1) | (frame[sub*7+7] >> 7);
+ if (xmaxc > GSM611_XMAXC_REDUCE) {
+ xmaxc -= GSM611_XMAXC_REDUCE;
+ mute_flag = false;
+ } else
+ xmaxc = 0;
+ frame[sub*7+6] &= 0xE0;
+ frame[sub*7+6] |= xmaxc >> 1;
+ frame[sub*7+7] &= 0x7F;
+ frame[sub*7+7] |= (xmaxc & 1) << 7;
+ }
+ return mute_flag;
}
-/* Use certain modifications to conceal the errors in a full rate frame */
-static int conceal_frame(uint8_t *frame)
+/* TS 46.011 chapter 6, paragraph 4, last sentence: "The grid position
+ * parameters are chosen randomly between 0 and 3 during this time."
+ * (The "during this time" qualifier refers to the speech muting state.)
+ * This sentence in the spec must have been overlooked by previous ECU
+ * implementors, as this aspect of the muting logic was missing.
+ */
+static void random_grid_pos(struct fr_ecu_state *fr, uint8_t *frame)
{
- struct bitvec *frame_bitvec;
- unsigned int len;
- bool silent;
- int rc = 0;
-
- /* In case we already deal with a silent frame,
- * there is nothing to, we just abort immediately */
- if (osmo_fr_check_sid(frame, GSM_FR_BYTES))
- return 0;
-
- /* Attempt to allocate memory for bitvec */
- frame_bitvec = bitvec_alloc(GSM_FR_BYTES, NULL);
- if (!frame_bitvec)
- return -ENOMEM;
+ uint8_t sub;
- /* Convert a frame to bitvec */
- len = bitvec_unpack(frame_bitvec, frame);
- if (len != GSM_FR_BYTES) {
- rc = -EIO;
- goto leave;
- }
-
- /* Fudge frame parameters */
- silent = reduce_xmaxcr_all(frame_bitvec);
-
- /* If we reached silence level, mute the frame
- * completely, this also means that we can
- * save the bitvec_pack operation */
- if (silent) {
- memset(frame, 0x00, GSM_FR_BYTES);
- frame[0] = 0xd0;
- goto leave;
+ for (sub = 0; sub < 4; sub++) {
+ frame[sub*7+6] &= 0x9F;
+ frame[sub*7+6] |= osmo_prbs_get_ubit(&fr->prng) << 6;
+ frame[sub*7+6] |= osmo_prbs_get_ubit(&fr->prng) << 5;
}
+}
- /* Convert back to packed byte form */
- len = bitvec_pack(frame_bitvec, frame);
- if (len != GSM_FR_BYTES) {
- rc = -EIO;
- goto leave;
+/* Like reduce_xmaxc() above, but for comfort noise rather than speech. */
+static bool reduce_xmaxc_sid(struct fr_ecu_state *fr)
+{
+ if (fr->sid_xmaxc > GSM611_XMAXC_REDUCE) {
+ fr->sid_xmaxc -= GSM611_XMAXC_REDUCE;
+ return false;
}
-
-leave:
- bitvec_free(frame_bitvec);
- return rc;
+ fr->sid_xmaxc = 0;
+ return true;
}
-/*!
- * To be called when a good frame is received.
- * This function will then create a backup of the frame
- * and reset the internal state.
- * \param[in] state The state object for the ECU
- * \param[out] frame The valid frame (GSM_FR_BYTES bytes in RTP payload format)
+/* This function implements the part which is peculiar to the present
+ * "standalone" packaging of GSM-FR ECU, without a directly coupled
+ * comfort noise generator - it re-emits synthetic SID frames during
+ * DTX pauses, initially unchanged from the saved SID and later muted.
*/
-void osmo_ecu_fr_reset(struct osmo_ecu_fr_state *state, const uint8_t *frame)
+static void reemit_sid(struct fr_ecu_state *fr, uint8_t *frame)
{
- state->subsequent_lost_frame = false;
- memcpy(state->frame_backup, frame, GSM_FR_BYTES);
+ uint8_t *p, sub;
+
+ memcpy(frame, fr->sid_prefix, SID_PREFIX_LEN);
+ p = frame + SID_PREFIX_LEN;
+ for (sub = 0; sub < 4; sub++) {
+ *p++ = 0;
+ *p++ = fr->sid_xmaxc >> 1;
+ *p++ = (fr->sid_xmaxc & 1) << 7;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ }
}
-/*!
- * To be called when a bad frame is received.
- * This function will then generate a replacement frame
- * that can be used to conceal the dropout.
- * \param[in] state The state object for the ECU
- * \param[out] frame The buffer to fill with GSM_FR_BYTES of replacement frame
- * \returns 0 if the frame was sucessfully filled
+/* This function is responsible for generating the ECU's output
+ * in the event that the Radio Subsystem does not have a good
+ * traffic frame - conditions corresponding to BFI=1 in the specs.
*/
-int osmo_ecu_fr_conceal(struct osmo_ecu_fr_state *state, uint8_t *frame)
+static void fr_ecu_output(struct fr_ecu_state *fr, uint8_t *frame)
{
- int rc;
-
- /* For subsequent frames we run the error concealment
- * functions on the backed up frame before we restore
- * the backup */
- if (state->subsequent_lost_frame) {
- rc = conceal_frame(state->frame_backup);
- if (rc)
- return rc;
+ bool mute;
+
+ switch (fr->pr_state) {
+ case STATE_NO_DATA:
+ memcpy(frame, osmo_gsm611_silence_frame, GSM_FR_BYTES);
+ return;
+ case STATE_SPEECH:
+ /* TS 46.011 chapter 6: "The first lost speech frame is
+ * replaced at the speech decoder input by the previous
+ * good speech frame." */
+ memcpy(frame, fr->speech_frame, GSM_FR_BYTES);
+ fr->pr_state = STATE_SP_MUTING;
+ return;
+ case STATE_SP_MUTING:
+ mute = reduce_xmaxc(fr->speech_frame);
+ memcpy(frame, fr->speech_frame, GSM_FR_BYTES);
+ random_grid_pos(fr, frame);
+ if (mute)
+ fr->pr_state = STATE_NO_DATA;
+ return;
+ case STATE_SID:
+ fr->sid_reemit_count++;
+ if (fr->sid_reemit_count >= 48) {
+ fr->pr_state = STATE_SID_MUTING;
+ reduce_xmaxc_sid(fr);
+ }
+ reemit_sid(fr, frame);
+ return;
+ case STATE_SID_MUTING:
+ if (reduce_xmaxc_sid(fr)) {
+ fr->pr_state = STATE_NO_DATA;
+ memcpy(frame, osmo_gsm611_silence_frame, GSM_FR_BYTES);
+ } else
+ reemit_sid(fr, frame);
+ return;
+ default:
+ /* a severe bug in the state machine! */
+ OSMO_ASSERT(0);
}
-
- /* Restore the backed up frame and set flag in case
- * we receive even more bad frames */
- memcpy(frame, state->frame_backup, GSM_FR_BYTES);
- state->subsequent_lost_frame = true;
-
- return 0;
}
/***********************************************************************
@@ -171,44 +285,57 @@ int osmo_ecu_fr_conceal(struct osmo_ecu_fr_state *state, uint8_t *frame)
static struct osmo_ecu_state *ecu_fr_init(void *ctx, enum osmo_ecu_codec codec)
{
- struct osmo_ecu_state *st;
- size_t size = sizeof(*st) + sizeof(struct osmo_ecu_fr_state);
+ struct fr_ecu_state *fr;
- st = talloc_named_const(ctx, size, "ecu_state_FR");
- if (!st)
- return NULL;
+ fr = talloc_zero(ctx, struct fr_ecu_state);
+ fr->ecu_state.codec = codec;
+ fr->pr_state = STATE_NO_DATA;
+ osmo_prbs_state_init(&fr->prng, &osmo_prbs15);
- memset(st, 0, size);
- st->codec = codec;
+ return (struct osmo_ecu_state *) fr;
+}
- return st;
+static inline struct fr_ecu_state *_osmo_ecu_state_get_fr(struct osmo_ecu_state *st)
+{
+ return (struct fr_ecu_state *)container_of(st, struct fr_ecu_state, ecu_state);
}
static int ecu_fr_frame_in(struct osmo_ecu_state *st, bool bfi, const uint8_t *frame,
unsigned int frame_bytes)
{
- struct osmo_ecu_fr_state *fr = (struct osmo_ecu_fr_state *) &st->data;
+ struct fr_ecu_state *fr = _osmo_ecu_state_get_fr(st);
+
if (bfi)
return 0;
+ if (frame_bytes != GSM_FR_BYTES)
+ return 0;
+ if ((frame[0] & 0xF0) != 0xD0)
+ return 0;
- osmo_ecu_fr_reset(fr, frame);
+ fr_ecu_input(fr, frame);
return 0;
}
static int ecu_fr_frame_out(struct osmo_ecu_state *st, uint8_t *frame_out)
{
- struct osmo_ecu_fr_state *fr = (struct osmo_ecu_fr_state *) &st->data;
+ struct fr_ecu_state *fr = _osmo_ecu_state_get_fr(st);
+
+ fr_ecu_output(fr, frame_out);
+ return GSM_FR_BYTES;
+}
+
+static bool ecu_fr_is_dtx_pause(struct osmo_ecu_state *st)
+{
+ struct fr_ecu_state *fr = _osmo_ecu_state_get_fr(st);
- if (osmo_ecu_fr_conceal(fr, frame_out) == 0)
- return GSM_FR_BYTES;
- else
- return -1;
+ return fr->last_input_was_sid;
}
static const struct osmo_ecu_ops osmo_ecu_ops_fr = {
.init = ecu_fr_init,
.frame_in = ecu_fr_frame_in,
.frame_out = ecu_fr_frame_out,
+ .is_dtx_pause = ecu_fr_is_dtx_pause,
};
static __attribute__((constructor)) void on_dso_load_ecu_fr(void)
diff --git a/src/codec/ecu_fr_old.c b/src/codec/ecu_fr_old.c
new file mode 100644
index 00000000..cfefce14
--- /dev/null
+++ b/src/codec/ecu_fr_old.c
@@ -0,0 +1,166 @@
+/*
+ * (C) 2017 by sysmocom - s.f.m.c. GmbH
+ * (C) 2017 by Philipp Maier <pmaier@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * This module implements legacy, deprecated osmo_ecu_fr_reset() and
+ * osmo_ecu_fr_conceal() functions only - see ecu_fr.c for the new
+ * GSM-FR ECU implementation.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/bitvec.h>
+
+#include <osmocom/codec/gsm610_bits.h>
+#include <osmocom/codec/codec.h>
+#include <osmocom/codec/ecu.h>
+
+/* See also GSM 06.11, chapter 6 Example solution */
+#define GSM610_XMAXC_REDUCE 4
+#define GSM610_XMAXC_LEN 6
+
+/**
+ * Reduce the XMAXC field. When the XMAXC field reaches
+ * zero the function will return true.
+ */
+static bool reduce_xmaxcr(struct bitvec *frame_bitvec,
+ const unsigned int index)
+{
+ unsigned int field_index;
+ uint64_t field;
+
+ field_index = index;
+ field = bitvec_read_field(frame_bitvec, &field_index, GSM610_XMAXC_LEN);
+ if (field > GSM610_XMAXC_REDUCE)
+ field -= GSM610_XMAXC_REDUCE;
+ else
+ field = 0;
+
+ field_index = index;
+ bitvec_write_field(frame_bitvec, &field_index, field, GSM610_XMAXC_LEN);
+
+ return field == 0;
+}
+
+/**
+ * Reduce all XMAXC fields in the frame. When all XMAXC fields
+ * reach zero, then the function will return true.
+ */
+static bool reduce_xmaxcr_all(struct bitvec *frame_bitvec)
+{
+ bool silent = true;
+
+ silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC00);
+ silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC10);
+ silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC20);
+ silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC30);
+
+ return silent;
+}
+
+/* Use certain modifications to conceal the errors in a full rate frame */
+static int conceal_frame(uint8_t *frame)
+{
+ struct bitvec *frame_bitvec;
+ unsigned int len;
+ bool silent;
+ int rc = 0;
+
+ /* In case we already deal with a silent frame,
+ * there is nothing to, we just abort immediately */
+ if (osmo_fr_check_sid(frame, GSM_FR_BYTES))
+ return 0;
+
+ /* Attempt to allocate memory for bitvec */
+ frame_bitvec = bitvec_alloc(GSM_FR_BYTES, NULL);
+ if (!frame_bitvec)
+ return -ENOMEM;
+
+ /* Convert a frame to bitvec */
+ len = bitvec_unpack(frame_bitvec, frame);
+ if (len != GSM_FR_BYTES) {
+ rc = -EIO;
+ goto leave;
+ }
+
+ /* Fudge frame parameters */
+ silent = reduce_xmaxcr_all(frame_bitvec);
+
+ /* If we reached silence level, mute the frame
+ * completely, this also means that we can
+ * save the bitvec_pack operation */
+ if (silent) {
+ memset(frame, 0x00, GSM_FR_BYTES);
+ frame[0] = 0xd0;
+ goto leave;
+ }
+
+ /* Convert back to packed byte form */
+ len = bitvec_pack(frame_bitvec, frame);
+ if (len != GSM_FR_BYTES) {
+ rc = -EIO;
+ goto leave;
+ }
+
+leave:
+ bitvec_free(frame_bitvec);
+ return rc;
+}
+
+/*!
+ * To be called when a good frame is received.
+ * This function will then create a backup of the frame
+ * and reset the internal state.
+ * \param[in] state The state object for the ECU
+ * \param[out] frame The valid frame (GSM_FR_BYTES bytes in RTP payload format)
+ */
+void osmo_ecu_fr_reset(struct osmo_ecu_fr_state *state, const uint8_t *frame)
+{
+ state->subsequent_lost_frame = false;
+ memcpy(state->frame_backup, frame, GSM_FR_BYTES);
+}
+
+/*!
+ * To be called when a bad frame is received.
+ * This function will then generate a replacement frame
+ * that can be used to conceal the dropout.
+ * \param[in] state The state object for the ECU
+ * \param[out] frame The buffer to fill with GSM_FR_BYTES of replacement frame
+ * \returns 0 if the frame was successfully filled
+ */
+int osmo_ecu_fr_conceal(struct osmo_ecu_fr_state *state, uint8_t *frame)
+{
+ int rc;
+
+ /* For subsequent frames we run the error concealment
+ * functions on the backed up frame before we restore
+ * the backup */
+ if (state->subsequent_lost_frame) {
+ rc = conceal_frame(state->frame_backup);
+ if (rc)
+ return rc;
+ }
+
+ /* Restore the backed up frame and set flag in case
+ * we receive even more bad frames */
+ memcpy(frame, state->frame_backup, GSM_FR_BYTES);
+ state->subsequent_lost_frame = true;
+
+ return 0;
+}
diff --git a/src/codec/gsm610.c b/src/codec/gsm610.c
index a05eabab..5cc4f148 100644
--- a/src/codec/gsm610.c
+++ b/src/codec/gsm610.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
@@ -300,6 +296,35 @@ const uint16_t gsm610_bitorder[260] = {
29, /* LARc5:0 */
};
+/*
+ * Table 1 in section 6 of 3GPP TS 46.011 (substitution and muting of lost
+ * frames) specifies a silence frame in the form of GSM 06.10 parameters;
+ * the following const datum is this GSM 06.11 silence frame in GSM-FR
+ * RTP encoding.
+ */
+const uint8_t osmo_gsm611_silence_frame[GSM_FR_BYTES] = {
+ 0xDA, 0xA7, 0xAA, 0xA5, 0x1A,
+ 0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
+ 0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
+ 0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
+ 0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
+};
+
+static const uint16_t sid_code_word_bits[95] = {
+ /* bit numbers are relative to the RTP frame beginning,
+ * with signature bits included in the count. */
+ 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73,
+ 75, 76, 78, 79, 81, 82, 84, 85, 87, 88, 90, 91,
+ 93, 94, 113, 114, 116, 117, 119, 120, 122, 123,
+ 125, 126, 128, 129, 131, 132, 134, 135, 137,
+ 138, 140, 141, 143, 144, 146, 147, 149, 150,
+ 169, 170, 172, 173, 175, 176, 178, 179, 181,
+ 182, 184, 185, 187, 188, 190, 191, 193, 194,
+ 196, 197, 199, 200, 202, 203, 205, 206, 225,
+ 226, 228, 229, 231, 232, 234, 235, 237, 240,
+ 243, 246, 249, 252, 255, 258, 261
+};
+
/*! Check whether RTP frame contains FR SID code word according to
* TS 101 318 §5.1.2
* \param[in] rtp_payload Buffer with RTP payload
@@ -309,16 +334,7 @@ const uint16_t gsm610_bitorder[260] = {
bool osmo_fr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
{
struct bitvec bv;
- uint16_t i, z_bits[] = { 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73,
- 75, 76, 78, 79, 81, 82, 84, 85, 87, 88, 90, 91,
- 93, 94, 113, 114, 116, 117, 119, 120, 122, 123,
- 125, 126, 128, 129, 131, 132, 134, 135, 137,
- 138, 140, 141, 143, 144, 146, 147, 149, 150,
- 169, 170, 172, 173, 175, 176, 178, 179, 181,
- 182, 184, 185, 187, 188, 190, 191, 193, 194,
- 196, 197, 199, 200, 202, 203, 205, 206, 225,
- 226, 228, 229, 231, 232, 234, 235, 237, 240,
- 243, 246, 249, 252, 255, 258, 261 };
+ uint16_t i;
/* signature does not match Full Rate SID */
if ((rtp_payload[0] >> 4) != 0xD)
@@ -327,10 +343,120 @@ bool osmo_fr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
bv.data = (uint8_t *) rtp_payload;
bv.data_len = payload_len;
- /* code word is all 0 at given bits, numbered from 1 */
- for (i = 0; i < ARRAY_SIZE(z_bits); i++)
- if (bitvec_get_bit_pos(&bv, z_bits[i]) != ZERO)
+ /* code word is all 0 at given bits */
+ for (i = 0; i < ARRAY_SIZE(sid_code_word_bits); i++) {
+ if (bitvec_get_bit_pos(&bv, sid_code_word_bits[i]) != ZERO)
return false;
+ }
return true;
}
+
+/*! Classify potentially-SID FR codec frame in RTP format according
+ * to the rules of GSM 06.31 §6.1.1
+ * \param[in] rtp_payload Buffer with RTP payload
+ * \returns enum osmo_gsm631_sid_class, with symbolic values
+ * OSMO_GSM631_SID_CLASS_SPEECH, OSMO_GSM631_SID_CLASS_INVALID or
+ * OSMO_GSM631_SID_CLASS_VALID corresponding to the 3 possible bit-counting
+ * classifications prescribed by the spec.
+ *
+ * Differences between the more familiar osmo_fr_check_sid() and the present
+ * function are:
+ *
+ * 1. osmo_fr_check_sid() returns true only if the SID frame is absolutely
+ * perfect, with all 95 bits of the SID code word zeroed. However, the
+ * rules of GSM 06.31 §6.1.1 allow up to one bit to be in error,
+ * and the frame is still accepted as valid SID.
+ *
+ * 2. The third possible state of invalid SID is not handled at all by the
+ * simpler osmo_fr_check_sid() function.
+ *
+ * 3. osmo_fr_check_sid() includes a check for 0xD RTP signature, and returns
+ * false if that signature nibble is wrong. That check is not included
+ * in the present version because there is no place for it in the
+ * ETSI-prescribed classification, it is neither speech nor SID. The
+ * assumption is that this function is used to classify the bit content
+ * of received codec frames, not their RTP encoding - the latter needs
+ * to be validated beforehand.
+ *
+ * Which function should one use? The answer depends on the specific
+ * circumstances, and needs to be addressed on a case-by-case basis.
+ */
+enum osmo_gsm631_sid_class osmo_fr_sid_classify(const uint8_t *rtp_payload)
+{
+ struct bitvec bv;
+ uint16_t i, n;
+
+ bv.data = (uint8_t *) rtp_payload;
+ bv.data_len = GSM_FR_BYTES;
+
+ /* count not-SID-matching bits per the spec */
+ n = 0;
+ for (i = 0; i < ARRAY_SIZE(sid_code_word_bits); i++) {
+ if (bitvec_get_bit_pos(&bv, sid_code_word_bits[i]) != ZERO)
+ n++;
+ if (n >= 16)
+ return OSMO_GSM631_SID_CLASS_SPEECH;
+ }
+ if (n >= 2)
+ return OSMO_GSM631_SID_CLASS_INVALID;
+ else
+ return OSMO_GSM631_SID_CLASS_VALID;
+}
+
+/*! Reset the SID field and the unused bits of a potentially corrupted,
+ * but still valid GSM-FR SID frame in RTP encoding to their pristine state.
+ * \param[in] rtp_payload Buffer with RTP payload - must be writable!
+ *
+ * Per GSM 06.12 section 5.2, a freshly minted SID frame carries 60 bits
+ * of comfort noise parameters (LARc and 4 times Xmaxc), while the remaining
+ * 200 bits are all zeros; the latter 200 all-0 bits further break down into
+ * 95 bits of SID field (checked by receivers to detect SID) and 105 unused
+ * bits which receivers are told to ignore. Network elements that receive
+ * SID frames from call leg A uplink and need to retransmit them on leg B
+ * downlink should "rejuvenate" received SID frames prior to retransmission;
+ * this function does the job.
+ */
+void osmo_fr_sid_reset(uint8_t *rtp_payload)
+{
+ uint8_t *p, sub;
+
+ p = rtp_payload + 5; /* skip magic+LARc */
+ for (sub = 0; sub < 4; sub++) {
+ *p++ = 0;
+ *p++ &= 0x1F; /* upper 5 bits of Xmaxc field */
+ *p++ &= 0x80; /* and the lsb spilling into the next byte */
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ }
+}
+
+/*! Preen potentially-SID FR codec frame in RTP format, ensuring that it is
+ * either a speech frame or a valid SID, and if the latter, making it a
+ * perfect, error-free SID frame.
+ * \param[in] rtp_payload Buffer with RTP payload - must be writable!
+ * \returns true if the frame is good, false otherwise
+ */
+bool osmo_fr_sid_preen(uint8_t *rtp_payload)
+{
+ enum osmo_gsm631_sid_class sidc;
+
+ sidc = osmo_fr_sid_classify(rtp_payload);
+ switch (sidc) {
+ case OSMO_GSM631_SID_CLASS_SPEECH:
+ return true;
+ case OSMO_GSM631_SID_CLASS_INVALID:
+ return false;
+ case OSMO_GSM631_SID_CLASS_VALID:
+ /* "Rejuvenate" this SID frame, correcting any errors */
+ osmo_fr_sid_reset(rtp_payload);
+ return true;
+ default:
+ /* There are only 3 possible SID classifications per GSM 06.31
+ * section 6.1.1, thus any other return value is a grave error
+ * in the code. */
+ OSMO_ASSERT(0);
+ }
+}
diff --git a/src/codec/gsm620.c b/src/codec/gsm620.c
index 282781fd..ef1d3b9b 100644
--- a/src/codec/gsm620.c
+++ b/src/codec/gsm620.c
@@ -17,14 +17,11 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
#include <stdbool.h>
+#include <string.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/core/utils.h>
@@ -268,12 +265,6 @@ const uint16_t gsm620_voiced_bitorder[112] = {
81, /* Code 3:7 */
};
-static inline uint16_t mask(const uint8_t msb)
-{
- const uint16_t m = (uint16_t)1 << (msb - 1);
- return (m - 1) ^ m;
-}
-
/*! Check whether RTP frame contains HR SID code word according to
* TS 101 318 §5.2.2
* \param[in] rtp_payload Buffer with RTP payload
@@ -282,16 +273,44 @@ static inline uint16_t mask(const uint8_t msb)
*/
bool osmo_hr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
{
- uint8_t i, bits[] = { 1, 2, 8, 9, 5, 4, 9, 5, 4, 9, 5, 4, 9, 5 };
- struct bitvec bv;
- bv.data = (uint8_t *) rtp_payload;
- bv.data_len = payload_len;
- bv.cur_bit = 33;
+ struct bitvec bv = {
+ .data = (uint8_t *)rtp_payload,
+ .data_len = payload_len,
+ };
- /* code word is all 1 at given bits, numbered from 1, MODE is always 3 */
- for (i = 0; i < ARRAY_SIZE(bits); i++)
- if (bitvec_get_uint(&bv, bits[i]) != mask(bits[i]))
+ /* A SID frame is identified by a SID codeword consisting of 79 bits which are all 1,
+ * so we basically check if all bits in range r34..r112 (inclusive) are 1. */
+ for (bv.cur_bit = 33; bv.cur_bit < bv.data_len * 8; bv.cur_bit++)
+ if (bitvec_get_bit_pos(&bv, bv.cur_bit) != ONE)
return false;
return true;
}
+
+/*! Reset the SID field of a potentially corrupted, but still valid GSM-HR
+ * SID frame in TS 101 318 format to its pristine state (full SID codeword).
+ * \param[in] rtp_payload Buffer with RTP payload - must be writable!
+ *
+ * Per GSM 06.22 section 5.3, a freshly minted SID frame consists of 33 bits
+ * of comfort noise parameters and 79 bits of SID codeword (all 1s). Network
+ * elements that receive SID frames from call leg A uplink and need to
+ * retransmit them on leg B downlink should "rejuvenate" received SID frames
+ * prior to retransmission by resetting the SID field to its pristine state
+ * of all 1s; this function does the job.
+ *
+ * Important note: because of HR-specific quirks (lack of exact bit counting
+ * rules in GSM 06.41 spec compared to 06.31 & 06.81, plus the fact that such
+ * bit counting can only be done efficiently in the GSM 05.03 channel decoder
+ * prior to bit reordering based on voiced or unvoiced mode), a generic
+ * (usable from any network element) SID classification function similar to
+ * osmo_{fr,efr}_sid_classify() unfortunately cannot exist for HR. Therefore,
+ * the triggering condition for invoking this SID rejuvenation/reset function
+ * can only be an out-of-band SID indication, as in GSM 08.61 TRAU frames
+ * or RFC 5993 ToC octet.
+ */
+void osmo_hr_sid_reset(uint8_t *rtp_payload)
+{
+ /* set all 79 SID codeword bits to 1 */
+ rtp_payload[4] |= 0x7F;
+ memset(rtp_payload + 5, 0xFF, 9);
+}
diff --git a/src/codec/gsm660.c b/src/codec/gsm660.c
index 4f7bb099..b15bdf37 100644
--- a/src/codec/gsm660.c
+++ b/src/codec/gsm660.c
@@ -17,13 +17,13 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/utils.h>
#include <osmocom/codec/codec.h>
/* GSM EFR - subjective importance bit ordering */
@@ -257,3 +257,161 @@ const uint16_t gsm660_bitorder[260] = {
243, /* 258 -> PULSE 4_9: b0 */
246, /* 259 -> PULSE 4_10: b0 */
};
+
+static const uint8_t sid_code_word_bits[95] = {
+ /* bit numbers are relative to "pure" EFR frame beginning,
+ * not counting the signature bits. */
+ 45, 46, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 67, 68, 94, 95, 96, 98, 99, 100, 101,
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
+ 161, 162, 163, 164, 165, 166, 167, 168, 169, 170,
+ 171, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 205, 206, 207, 208, 209, 212, 213, 214, 215, 216,
+ 217, 218, 219, 220, 221
+};
+
+/*! Check whether RTP frame contains EFR SID code word according to
+ * TS 101 318 §5.3.2
+ * \param[in] rtp_payload Buffer with RTP payload
+ * \param[in] payload_len Length of payload
+ * \returns true if code word is found, false otherwise
+ */
+bool osmo_efr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
+{
+ struct bitvec bv;
+ uint16_t i;
+
+ /* signature does not match Enhanced Full Rate SID */
+ if ((rtp_payload[0] >> 4) != 0xC)
+ return false;
+
+ bv.data = (uint8_t *) rtp_payload;
+ bv.data_len = payload_len;
+
+ /* code word is all 1 at given bits */
+ for (i = 0; i < ARRAY_SIZE(sid_code_word_bits); i++) {
+ if (bitvec_get_bit_pos(&bv, sid_code_word_bits[i]+4) != ONE)
+ return false;
+ }
+
+ return true;
+}
+
+/*! Classify potentially-SID EFR codec frame in RTP format according
+ * to the rules of GSM 06.81 §6.1.1
+ * \param[in] rtp_payload Buffer with RTP payload
+ * \returns enum osmo_gsm631_sid_class, with symbolic values
+ * OSMO_GSM631_SID_CLASS_SPEECH, OSMO_GSM631_SID_CLASS_INVALID or
+ * OSMO_GSM631_SID_CLASS_VALID corresponding to the 3 possible bit-counting
+ * classifications prescribed by the spec.
+ *
+ * Differences between the more familiar osmo_efr_check_sid() and the present
+ * function are:
+ *
+ * 1. osmo_efr_check_sid() returns true only if the SID frame is absolutely
+ * perfect, with all 95 bits of the SID code word set. However, the
+ * rules of GSM 06.81 §6.1.1 allow up to one bit to be in error,
+ * and the frame is still accepted as valid SID.
+ *
+ * 2. The third possible state of invalid SID is not handled at all by the
+ * simpler osmo_efr_check_sid() function.
+ *
+ * 3. osmo_efr_check_sid() includes a check for 0xC RTP signature, and returns
+ * false if that signature nibble is wrong. That check is not included
+ * in the present version because there is no place for it in the
+ * ETSI-prescribed classification, it is neither speech nor SID. The
+ * assumption is that this function is used to classify the bit content
+ * of received codec frames, not their RTP encoding - the latter needs
+ * to be validated beforehand.
+ *
+ * Which function should one use? The answer depends on the specific
+ * circumstances, and needs to be addressed on a case-by-case basis.
+ */
+enum osmo_gsm631_sid_class osmo_efr_sid_classify(const uint8_t *rtp_payload)
+{
+ struct bitvec bv;
+ uint16_t i, n;
+
+ bv.data = (uint8_t *) rtp_payload;
+ bv.data_len = GSM_EFR_BYTES;
+
+ /* count not-SID-matching bits per the spec */
+ n = 0;
+ for (i = 0; i < ARRAY_SIZE(sid_code_word_bits); i++) {
+ if (bitvec_get_bit_pos(&bv, sid_code_word_bits[i]+4) != ONE)
+ n++;
+ if (n >= 16)
+ return OSMO_GSM631_SID_CLASS_SPEECH;
+ }
+ if (n >= 2)
+ return OSMO_GSM631_SID_CLASS_INVALID;
+ else
+ return OSMO_GSM631_SID_CLASS_VALID;
+}
+
+/*! Reset the SID field of a potentially corrupted, but still valid GSM-EFR
+ * SID frame in RTP encoding to its pristine state (full SID code word).
+ * \param[in] rtp_payload Buffer with RTP payload - must be writable!
+ *
+ * Per GSM 06.62 section 5.3, a freshly minted SID frame consists of 58 bits
+ * of comfort noise parameters (LSF and 4 times fixed codebook gain), 95 bits
+ * of SID code word (all 1s) and 91 unused bits (all 0s). Network elements
+ * that receive SID frames from call leg A uplink and need to retransmit them
+ * on leg B downlink should "rejuvenate" received SID frames prior to
+ * retransmission by resetting the SID field to its pristine state of all 1s;
+ * this function does the job.
+ *
+ * Potential TODO: it would be nice to also zero out the remaining 91 bits
+ * which the spec leaves as reserved, clearing out leg A radio bit errors -
+ * but do we really need to?
+ */
+void osmo_efr_sid_reset(uint8_t *rtp_payload)
+{
+ /* set all 95 SID code word bits to 1 */
+ rtp_payload[6] |= 0x6F;
+ rtp_payload[7] = 0xFF;
+ rtp_payload[8] = 0xFF;
+ rtp_payload[9] |= 0x80;
+ rtp_payload[12] |= 0x3B;
+ rtp_payload[13] = 0xFF;
+ rtp_payload[14] = 0xFF;
+ rtp_payload[15] |= 0xE0;
+ rtp_payload[19] = 0xFF;
+ rtp_payload[20] = 0xFF;
+ rtp_payload[21] = 0xFF;
+ rtp_payload[25] = 0xFF;
+ rtp_payload[26] |= 0xFC;
+ rtp_payload[27] = 0xFF;
+ rtp_payload[28] |= 0xC0;
+}
+
+/*! Preen potentially-SID EFR codec frame in RTP format, ensuring that it is
+ * either a speech frame or a valid SID, and if the latter, making it a
+ * perfect, error-free SID frame.
+ * \param[in] rtp_payload Buffer with RTP payload - must be writable!
+ * \returns true if the frame is good, false otherwise
+ */
+bool osmo_efr_sid_preen(uint8_t *rtp_payload)
+{
+ enum osmo_gsm631_sid_class sidc;
+
+ sidc = osmo_efr_sid_classify(rtp_payload);
+ switch (sidc) {
+ case OSMO_GSM631_SID_CLASS_SPEECH:
+ return true;
+ case OSMO_GSM631_SID_CLASS_INVALID:
+ return false;
+ case OSMO_GSM631_SID_CLASS_VALID:
+ /* "Rejuvenate" this SID frame, correcting any errors */
+ osmo_efr_sid_reset(rtp_payload);
+ return true;
+ default:
+ /* There are only 3 possible SID classifications per GSM 06.81
+ * section 6.1.1, thus any other return value is a grave error
+ * in the code. */
+ OSMO_ASSERT(0);
+ }
+}
diff --git a/src/codec/gsm690.c b/src/codec/gsm690.c
index cc6cdf0c..3b2e6694 100644
--- a/src/codec/gsm690.c
+++ b/src/codec/gsm690.c
@@ -18,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
diff --git a/src/coding/Makefile.am b/src/coding/Makefile.am
index b023668e..0cab66ff 100644
--- a/src/coding/Makefile.am
+++ b/src/coding/Makefile.am
@@ -1,13 +1,14 @@
# 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:1:1
+LIBVERSION=3:0:3
AM_CPPFLAGS = \
-I"$(top_srcdir)/include" \
-I"$(top_builddir)/include" \
- $(TALLOC_CFLAGS)
-AM_CFLAGS = -Wall
+ -I"$(top_builddir)" \
+ $(NULL)
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
if ENABLE_PSEUDOTALLOC
AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
@@ -24,13 +25,15 @@ libosmocoding_la_SOURCES = \
gsm0503_amr_dtx.c
libosmocoding_la_LDFLAGS = \
$(LTLDFLAGS_OSMOCODING) \
- -version-info \
- $(LIBVERSION) \
+ -version-info $(LIBVERSION) \
-no-undefined \
- $(TALLOC_LIBS)
+ $(NULL)
+
libosmocoding_la_LIBADD = \
- ../libosmocore.la \
- ../gsm/libosmogsm.la \
- ../codec/libosmocodec.la
+ $(top_builddir)/src/core/libosmocore.la \
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(top_builddir)/src/codec/libosmocodec.la \
+ $(NULL)
EXTRA_DIST = libosmocoding.map
+EXTRA_libosmocoding_la_DEPENDENCIES = libosmocoding.map
diff --git a/src/coding/gsm0503_amr_dtx.c b/src/coding/gsm0503_amr_dtx.c
index 7069b967..7de3b281 100644
--- a/src/coding/gsm0503_amr_dtx.c
+++ b/src/coding/gsm0503_amr_dtx.c
@@ -1,5 +1,5 @@
/*
- * (C) 2020 by sysmocom - s.f.m.c. GmbH, Author: Philipp Maier
+ * (C) 2020-2022 by sysmocom - s.f.m.c. GmbH, Author: Philipp Maier
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
@@ -32,6 +32,8 @@
#include <osmocom/coding/gsm0503_parity.h>
#include <osmocom/gsm/gsm0503.h>
+#define S2U(b) ((b) < 0)
+
/* See also: 3GPP TS 05.03, chapter 3.10.1.3, 3.10.5.2 Identification marker */
static const ubit_t id_marker_1[] = { 1, 0, 1, 1, 0, 0, 0, 0, 1 };
@@ -60,7 +62,7 @@ const struct value_string gsm0503_amr_dtx_frame_names[] = {
{ 0, NULL }
};
-static bool detect_afs_id_marker(int *n_errors, int *n_bits_total, const ubit_t * ubits, uint8_t offset, uint8_t count,
+static bool detect_afs_id_marker(int *n_errors, int *n_bits_total, const sbit_t *sbits, uint8_t offset, uint8_t count,
const ubit_t * id_marker, uint8_t id_marker_len)
{
unsigned int i, k;
@@ -69,20 +71,20 @@ static bool detect_afs_id_marker(int *n_errors, int *n_bits_total, const ubit_t
int bits = 0;
/* Override coded in-band data */
- ubits += offset;
+ sbits += offset;
/* Check for identification marker bits */
for (i = 0; i < count; i++) {
for (k = 0; k < 4; k++) {
- if (id_marker[id_bit_nr % id_marker_len] != *ubits)
+ if (*sbits == 0 || id_marker[id_bit_nr % id_marker_len] != S2U(*sbits))
errors++;
id_bit_nr++;
- ubits++;
+ sbits++;
bits++;
}
/* Jump to the next block of 4 bits */
- ubits += 4;
+ sbits += 4;
}
*n_errors = errors;
@@ -92,30 +94,30 @@ static bool detect_afs_id_marker(int *n_errors, int *n_bits_total, const ubit_t
return *n_errors < *n_bits_total / 8;
}
-static bool detect_ahs_id_marker(int *n_errors, int *n_bits_total, const ubit_t * ubits, const ubit_t * id_marker)
+static bool detect_ahs_id_marker(int *n_errors, int *n_bits_total, const sbit_t *sbits, const ubit_t *id_marker)
{
unsigned int i, k;
int errors = 0;
int bits = 0;
/* Override coded in-band data */
- ubits += 16;
+ sbits += 16;
/* Check first identification marker bits (23*9 bits) */
for (i = 0; i < 23; i++) {
for (k = 0; k < 9; k++) {
- if (id_marker[k] != *ubits)
+ if (*sbits == 0 || id_marker[k] != S2U(*sbits))
errors++;
- ubits++;
+ sbits++;
bits++;
}
}
/* Check remaining identification marker bits (5 bits) */
for (k = 0; k < 5; k++) {
- if (id_marker[k] != *ubits)
+ if (*sbits == 0 || id_marker[k] != S2U(*sbits))
errors++;
- ubits++;
+ sbits++;
bits++;
}
@@ -126,7 +128,7 @@ static bool detect_ahs_id_marker(int *n_errors, int *n_bits_total, const ubit_t
return *n_errors < *n_bits_total / 8;
}
-static bool detect_interleaved_ahs_id_marker(int *n_errors, int *n_bits_total, const ubit_t * ubits, uint8_t offset,
+static bool detect_interleaved_ahs_id_marker(int *n_errors, int *n_bits_total, const sbit_t *sbits, uint8_t offset,
uint8_t n_bits, const ubit_t * id_marker, uint8_t id_marker_len)
{
unsigned int i, k;
@@ -136,23 +138,23 @@ static bool detect_interleaved_ahs_id_marker(int *n_errors, int *n_bits_total, c
uint8_t remainder = n_bits % id_marker_len;
/* Override coded in-band data */
- ubits += offset;
+ sbits += offset;
/* Check first identification marker bits (23*9 bits) */
for (i = 0; i < full_rounds; i++) {
for (k = 0; k < id_marker_len; k++) {
- if (id_marker[k] != *ubits)
+ if (*sbits == 0 || id_marker[k] != S2U(*sbits))
errors++;
- ubits += 2;
+ sbits += 2;
bits++;
}
}
/* Check remaining identification marker bits (5 bits) */
for (k = 0; k < remainder; k++) {
- if (id_marker[k] != *ubits)
+ if (*sbits == 0 || id_marker[k] != S2U(*sbits))
errors++;
- ubits += 2;
+ sbits += 2;
bits++;
}
@@ -163,126 +165,128 @@ static bool detect_interleaved_ahs_id_marker(int *n_errors, int *n_bits_total, c
return *n_errors < *n_bits_total / 8;
}
-/* Detect a an FR AMR SID_FIRST frame by its identifcation marker */
-static bool detect_afs_sid_first(int *n_errors, int *n_bits_total, const ubit_t * ubits)
+/* Detect an FR AMR SID_FIRST frame by its identifcation marker */
+static bool detect_afs_sid_first(int *n_errors, int *n_bits_total, const sbit_t *sbits)
{
- return detect_afs_id_marker(n_errors, n_bits_total, ubits, 32, 53, id_marker_0, 9);
+ return detect_afs_id_marker(n_errors, n_bits_total, sbits, 32, 53, id_marker_0, 9);
}
-/* Detect an FR AMR SID_FIRST frame by its identification marker */
-static bool detect_afs_sid_update(int *n_errors, int *n_bits_total, const ubit_t * ubits)
+/* Detect an FR AMR SID_UPDATE frame by its identification marker */
+static bool detect_afs_sid_update(int *n_errors, int *n_bits_total, const sbit_t *sbits)
{
- return detect_afs_id_marker(n_errors, n_bits_total, ubits, 36, 53, id_marker_0, 9);
+ return detect_afs_id_marker(n_errors, n_bits_total, sbits, 36, 53, id_marker_0, 9);
}
-/* Detect an FR AMR SID_FIRST frame by its repeating coded inband data */
-static bool detect_afs_onset(int *n_errors, int *n_bits_total, const ubit_t * ubits)
+/* Detect an FR AMR ONSET frame by its repeating coded inband data */
+static int detect_afs_onset(int *n_errors, int *n_bits_total, const sbit_t *sbits)
{
bool rc;
- rc = detect_afs_id_marker(n_errors, n_bits_total, ubits, 4, 57, codec_mode_1_sid, 16);
+ rc = detect_afs_id_marker(n_errors, n_bits_total, sbits, 4, 57, codec_mode_1_sid, 16);
if (rc)
- return true;
+ return 0;
- rc = detect_afs_id_marker(n_errors, n_bits_total, ubits, 4, 57, codec_mode_2_sid, 16);
+ rc = detect_afs_id_marker(n_errors, n_bits_total, sbits, 4, 57, codec_mode_2_sid, 16);
if (rc)
- return true;
+ return 1;
- rc = detect_afs_id_marker(n_errors, n_bits_total, ubits, 4, 57, codec_mode_3_sid, 16);
+ rc = detect_afs_id_marker(n_errors, n_bits_total, sbits, 4, 57, codec_mode_3_sid, 16);
if (rc)
- return true;
+ return 2;
- rc = detect_afs_id_marker(n_errors, n_bits_total, ubits, 4, 57, codec_mode_4_sid, 16);
+ rc = detect_afs_id_marker(n_errors, n_bits_total, sbits, 4, 57, codec_mode_4_sid, 16);
if (rc)
- return true;
+ return 3;
- return false;
+ return -1;
}
/* Detect an HR AMR SID UPDATE frame by its identification marker */
-static bool detect_ahs_sid_update(int *n_errors, int *n_bits_total, const ubit_t * ubits)
+static bool detect_ahs_sid_update(int *n_errors, int *n_bits_total, const sbit_t *sbits)
{
- return detect_ahs_id_marker(n_errors, n_bits_total, ubits, id_marker_1);
+ return detect_ahs_id_marker(n_errors, n_bits_total, sbits, id_marker_1);
}
/* Detect an HR AMR SID FIRST (part 1) frame by its identification marker */
-static bool detect_ahs_sid_first_p1(int *n_errors, int *n_bits_total, const ubit_t * ubits)
+static bool detect_ahs_sid_first_p1(int *n_errors, int *n_bits_total, const sbit_t *sbits)
{
- return detect_ahs_id_marker(n_errors, n_bits_total, ubits, id_marker_0);
+ return detect_ahs_id_marker(n_errors, n_bits_total, sbits, id_marker_0);
}
/* Detect an HR AMR SID FIRST (part 2) frame by its repeating coded inband data */
-static bool detect_ahs_sid_first_p2(int *n_errors, int *n_bits_total, const ubit_t * ubits)
+static int detect_ahs_sid_first_p2(int *n_errors, int *n_bits_total, const sbit_t *sbits)
{
bool rc;
- rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 0, 114, codec_mode_1_sid, 16);
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 0, 114, codec_mode_1_sid, 16);
if (rc)
- return true;
+ return 0;
- rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 0, 114, codec_mode_2_sid, 16);
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 0, 114, codec_mode_2_sid, 16);
if (rc)
- return true;
+ return 1;
- rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 0, 114, codec_mode_3_sid, 16);
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 0, 114, codec_mode_3_sid, 16);
if (rc)
- return true;
+ return 2;
- rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 0, 114, codec_mode_4_sid, 16);
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 0, 114, codec_mode_4_sid, 16);
if (rc)
- return true;
+ return 3;
- return false;
+ return -1;
}
/* Detect an HR AMR ONSET frame by its repeating coded inband data */
-static bool detect_ahs_onset(int *n_errors, int *n_bits_total, const ubit_t * ubits)
+static int detect_ahs_onset(int *n_errors, int *n_bits_total, const sbit_t *sbits)
{
bool rc;
- rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 1, 114, codec_mode_1_sid, 16);
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 1, 114, codec_mode_1_sid, 16);
if (rc)
- return true;
+ return 0;
- rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 1, 114, codec_mode_2_sid, 16);
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 1, 114, codec_mode_2_sid, 16);
if (rc)
- return true;
+ return 1;
- rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 1, 114, codec_mode_3_sid, 16);
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 1, 114, codec_mode_3_sid, 16);
if (rc)
- return true;
+ return 2;
- rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 1, 114, codec_mode_4_sid, 16);
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 1, 114, codec_mode_4_sid, 16);
if (rc)
- return true;
+ return 3;
- return false;
+ return -1;
}
/* Detect an HR AMR SID FIRST INHIBIT frame by its identification marker */
-static bool detect_ahs_sid_first_inh(int *n_errors, int *n_bits_total, const ubit_t * ubits)
+static bool detect_ahs_sid_first_inh(int *n_errors, int *n_bits_total, const sbit_t *sbits)
{
- return detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 33, 212, id_marker_1, 9);
+ return detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 33, 212, id_marker_1, 9);
}
/* Detect an HR AMR SID UPDATE INHIBIT frame by its identification marker */
-static bool detect_ahs_sid_update_inh(int *n_errors, int *n_bits_total, const ubit_t * ubits)
+static bool detect_ahs_sid_update_inh(int *n_errors, int *n_bits_total, const sbit_t *sbits)
{
- return detect_interleaved_ahs_id_marker(n_errors, n_bits_total, ubits, 33, 212, id_marker_0, 9);
+ return detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 33, 212, id_marker_0, 9);
}
/*! Detect FR AMR DTX frame in unmapped, deinterleaved frame bits.
- * \param[in] ubits input bits (456 bit).
* \param[out] n_errors number of errornous bits.
* \param[out] n_bits_total number of checked bits.
+ * \param[out] mode_id codec mode ID (0..3 or -1).
+ * \param[in] sbits input soft-bits (456 bit).
* \returns dtx frame type. */
-enum gsm0503_amr_dtx_frames gsm0503_detect_afs_dtx_frame(int *n_errors, int *n_bits_total, const ubit_t * ubits)
+enum gsm0503_amr_dtx_frames gsm0503_detect_afs_dtx_frame2(int *n_errors, int *n_bits_total,
+ int *mode_id, const sbit_t *sbits)
{
- if (detect_afs_sid_first(n_errors, n_bits_total, ubits))
+ if (detect_afs_sid_first(n_errors, n_bits_total, sbits))
return AFS_SID_FIRST;
- if (detect_afs_sid_update(n_errors, n_bits_total, ubits))
+ if (detect_afs_sid_update(n_errors, n_bits_total, sbits))
return AFS_SID_UPDATE;
- if (detect_afs_onset(n_errors, n_bits_total, ubits))
+ if ((*mode_id = detect_afs_onset(n_errors, n_bits_total, sbits)) != -1)
return AFS_ONSET;
*n_errors = 0;
@@ -290,27 +294,62 @@ enum gsm0503_amr_dtx_frames gsm0503_detect_afs_dtx_frame(int *n_errors, int *n_b
return AMR_OTHER;
}
-/*! Detect HR AMR DTX frame in unmapped, deinterleaved frame bits.
+/*! Detect FR AMR DTX frame in unmapped, deinterleaved frame bits.
+ * DEPRECATED: use gsm0503_detect_afs_dtx_frame2() instead.
+ * \param[out] n_errors number of errornous bits.
+ * \param[out] n_bits_total number of checked bits.
* \param[in] ubits input bits (456 bit).
+ * \returns dtx frame type. */
+enum gsm0503_amr_dtx_frames gsm0503_detect_afs_dtx_frame(int *n_errors, int *n_bits_total,
+ const ubit_t *ubits)
+{
+ int mode_id; /* unused */
+ sbit_t sbits[456];
+
+ osmo_ubit2sbit(&sbits[0], &ubits[0], sizeof(sbits));
+ return gsm0503_detect_afs_dtx_frame2(n_errors, n_bits_total, &mode_id, &sbits[0]);
+}
+
+/*! Detect HR AMR DTX frame in unmapped, deinterleaved frame bits.
* \param[out] n_errors number of errornous bits.
* \param[out] n_bits_total number of checked bits.
+ * \param[out] mode_id codec ID (CMI or CMC/CMR).
+ * \param[out] mode_id codec mode ID (0..3 or -1).
+ * \param[in] sbits input soft-bits (456 bit).
* \returns dtx frame type, */
-enum gsm0503_amr_dtx_frames gsm0503_detect_ahs_dtx_frame(int *n_errors, int *n_bits_total, const ubit_t * ubits)
+enum gsm0503_amr_dtx_frames gsm0503_detect_ahs_dtx_frame2(int *n_errors, int *n_bits_total,
+ int *mode_id, const sbit_t *sbits)
{
- if (detect_ahs_sid_update(n_errors, n_bits_total, ubits))
+ if (detect_ahs_sid_update(n_errors, n_bits_total, sbits))
return AHS_SID_UPDATE;
- if (detect_ahs_sid_first_inh(n_errors, n_bits_total, ubits))
+ if (detect_ahs_sid_first_inh(n_errors, n_bits_total, sbits))
return AHS_SID_FIRST_INH;
- if (detect_ahs_sid_update_inh(n_errors, n_bits_total, ubits))
+ if (detect_ahs_sid_update_inh(n_errors, n_bits_total, sbits))
return AHS_SID_UPDATE_INH;
- if (detect_ahs_sid_first_p1(n_errors, n_bits_total, ubits))
+ if (detect_ahs_sid_first_p1(n_errors, n_bits_total, sbits))
return AHS_SID_FIRST_P1;
- if (detect_ahs_sid_first_p2(n_errors, n_bits_total, ubits))
+ if ((*mode_id = detect_ahs_sid_first_p2(n_errors, n_bits_total, sbits)) != -1)
return AHS_SID_FIRST_P2;
- if (detect_ahs_onset(n_errors, n_bits_total, ubits))
+ if ((*mode_id = detect_ahs_onset(n_errors, n_bits_total, sbits)) != -1)
return AHS_ONSET;
*n_errors = 0;
*n_bits_total = 0;
return AMR_OTHER;
}
+
+/*! Detect HR AMR DTX frame in unmapped, deinterleaved frame bits.
+ * DEPRECATED: use gsm0503_detect_ahs_dtx_frame2() instead.
+ * \param[out] n_errors number of errornous bits.
+ * \param[out] n_bits_total number of checked bits.
+ * \param[in] ubits input bits (456 bit).
+ * \returns dtx frame type, */
+enum gsm0503_amr_dtx_frames gsm0503_detect_ahs_dtx_frame(int *n_errors, int *n_bits_total,
+ const ubit_t *ubits)
+{
+ int mode_id; /* unused */
+ sbit_t sbits[456];
+
+ osmo_ubit2sbit(&sbits[0], &ubits[0], sizeof(sbits));
+ return gsm0503_detect_ahs_dtx_frame2(n_errors, n_bits_total, &mode_id, &sbits[0]);
+}
diff --git a/src/coding/gsm0503_coding.c b/src/coding/gsm0503_coding.c
index 1bec56ea..022f4656 100644
--- a/src/coding/gsm0503_coding.c
+++ b/src/coding/gsm0503_coding.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
@@ -35,9 +31,7 @@
#include <osmocom/core/crcgen.h>
#include <osmocom/core/endian.h>
-#include <osmocom/gprs/protocol/gsm_04_60.h>
-#include <osmocom/gprs/gprs_rlc.h>
-
+#include <osmocom/gsm/protocol/gsm_44_060.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm0503.h>
#include <osmocom/codec/codec.h>
@@ -542,24 +536,29 @@ static int osmo_conv_decode_ber_punctured(const struct osmo_conv_code *code,
int *n_errors, int *n_bits_total,
const uint8_t *data_punc)
{
- int res, i, coded_len;
+ int res, coded_len;
ubit_t recoded[EGPRS_DATA_C_MAX];
res = osmo_conv_decode(code, input, output);
- if (n_bits_total || n_errors) {
- coded_len = osmo_conv_encode(code, output, recoded);
- OSMO_ASSERT(sizeof(recoded) / sizeof(recoded[0]) >= coded_len);
- }
+ if (!n_bits_total && !n_errors)
+ return res;
+
+ coded_len = osmo_conv_encode(code, output, recoded);
+ OSMO_ASSERT(ARRAY_SIZE(recoded) >= coded_len);
/* Count bit errors */
if (n_errors) {
*n_errors = 0;
- for (i = 0; i < coded_len; i++) {
- if (((!data_punc) || (data_punc && !data_punc[i])) &&
- !((recoded[i] && input[i] < 0) ||
- (!recoded[i] && input[i] > 0)) )
- *n_errors += 1;
+ for (unsigned int i = 0; i < coded_len; i++) {
+ /* punctured bits do not count as bit errors */
+ if (data_punc != NULL && data_punc[i])
+ continue;
+ if (recoded[i] == 1 && input[i] < 0)
+ continue;
+ if (recoded[i] == 0 && input[i] > 0)
+ continue;
+ *n_errors += 1;
}
}
@@ -611,7 +610,7 @@ static int _xcch_decode_cB(uint8_t *l2_data, const sbit_t *cB,
/*! convenience wrapper for encoding to coded bits
* \param[out] cB caller-allocated buffer for 456 coded bits as per TS 05.03 4.1.3
- * \param[out] l2_data to-be-encoded L2 Frame
+ * \param[in] l2_data to-be-encoded L2 Frame
* \returns 0 */
static int _xcch_encode_cB(ubit_t *cB, const uint8_t *l2_data)
{
@@ -926,10 +925,10 @@ static int egprs_decode_data(uint8_t *l2_data, const sbit_t *c,
* \param[out] l2_data caller-allocated buffer for L2 Frame
* \param[in] bursts burst input data as soft unpacked bits
* \param[in] nbits number of bits in \a bursts
- * \param usf_p unused argument ?!?
+ * \param usf_p Uplink State Flag, FIXME: not implemented
* \param[out] n_errors number of detected bit-errors
* \param[out] n_bits_total total number of decoded bits
- * \returns 0 on success; negative on error */
+ * \returns number of bytes decoded; negative on error */
int gsm0503_pdtch_egprs_decode(uint8_t *l2_data, const sbit_t *bursts, uint16_t nbits,
uint8_t *usf_p, int *n_errors, int *n_bits_total)
{
@@ -1015,10 +1014,10 @@ int gsm0503_pdtch_egprs_decode(uint8_t *l2_data, const sbit_t *bursts, uint16_t
/*! Decode GPRS PDTCH
* \param[out] l2_data caller-allocated buffer for L2 Frame
* \param[in] bursts burst input data as soft unpacked bits
- * \param[out] usf_p uplink stealing flag
+ * \param[out] usf_p Uplink State Flag, only relevant for DL blocks
* \param[out] n_errors number of detected bit-errors
- * \param[out] n_bits_total total number of dcoded bits
- * \returns 0 on success; negative on error */
+ * \param[out] n_bits_total total number of decoded bits
+ * \returns number of bytes decoded; negative on error */
int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
int *n_errors, int *n_bits_total)
{
@@ -1047,6 +1046,10 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
osmo_conv_decode_ber(&gsm0503_xcch, cB,
conv, n_errors, n_bits_total);
+ /* the three USF bits d(0),d(1),d(2) are *not* precoded */
+ if (usf_p)
+ *usf_p = (conv[0] << 2) | (conv[1] << 1) | (conv[2] << 0);
+
rv = osmo_crc64gen_check_bits(&gsm0503_fire_crc40,
conv, 184, conv + 184);
if (rv)
@@ -1056,6 +1059,7 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
return 23;
case 2:
+ /* reorder, set punctured bits to 0 (unknown state) */
for (i = 587, j = 455; i >= 0; i--) {
if (!gsm0503_puncture_cs2[i])
cB[i] = cB[j--];
@@ -1063,9 +1067,15 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
cB[i] = 0;
}
- osmo_conv_decode_ber(&gsm0503_cs2_np, cB,
- conv, n_errors, n_bits_total);
+ /* decode as if puncturing was not employed (note '_np') */
+ osmo_conv_decode_ber_punctured(&gsm0503_cs2_np, cB, conv,
+ n_errors, NULL,
+ gsm0503_puncture_cs2);
+ /* indicate the actual amount of coded bits (excluding punctured ones) */
+ if (n_bits_total != NULL)
+ *n_bits_total = 456;
+ /* 5.1.2.2 a) the three USF bits d(0),d(1),d(2) are precoded into six bits */
for (i = 0; i < 8; i++) {
for (j = 0, k = 0; j < 6; j++)
k += abs(((int)gsm0503_usf2six[i][j]) - ((int)conv[j]));
@@ -1091,6 +1101,7 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
return 34;
case 3:
+ /* reorder, set punctured bits to 0 (unknown state) */
for (i = 675, j = 455; i >= 0; i--) {
if (!gsm0503_puncture_cs3[i])
cB[i] = cB[j--];
@@ -1098,9 +1109,15 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
cB[i] = 0;
}
- osmo_conv_decode_ber(&gsm0503_cs3_np, cB,
- conv, n_errors, n_bits_total);
+ /* decode as if puncturing was not employed (note '_np') */
+ osmo_conv_decode_ber_punctured(&gsm0503_cs3_np, cB, conv,
+ n_errors, NULL,
+ gsm0503_puncture_cs3);
+ /* indicate the actual amount of coded bits (excluding punctured ones) */
+ if (n_bits_total != NULL)
+ *n_bits_total = 456;
+ /* 5.1.3.2 a) the three USF bits d(0),d(1),d(2) are precoded into six bits */
for (i = 0; i < 8; i++) {
for (j = 0, k = 0; j < 6; j++)
k += abs(((int)gsm0503_usf2six[i][j]) - ((int)conv[j]));
@@ -1129,6 +1146,7 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
for (i = 12; i < 456; i++)
conv[i] = (cB[i] < 0) ? 1 : 0;
+ /* 5.1.4.2 a) the three USF bits d(0),d(1),d(2) are precoded into twelve bits */
for (i = 0; i < 8; i++) {
for (j = 0, k = 0; j < 12; j++)
k += abs(((int)gsm0503_usf2twelve_sbit[i][j]) - ((int)cB[j]));
@@ -1577,24 +1595,22 @@ static void tch_fr_disassemble(ubit_t *b_bits,
}
}
-/* assemble a HR codec frame in format as used inside RTP */
+/* assemble a HR codec frame in the canonical format of ETSI TS 101 318 */
static void tch_hr_reassemble(uint8_t *tch_data, const ubit_t *b_bits)
{
- int i, j;
-
- tch_data[0] = 0x00; /* F = 0, FT = 000 */
- memset(tch_data + 1, 0, 14);
+ int i;
- for (i = 0, j = 8; i < 112; i++, j++)
- tch_data[j >> 3] |= (b_bits[i] << (7 - (j & 7)));
+ memset(tch_data, 0, GSM_HR_BYTES);
+ for (i = 0; i < 112; i++)
+ tch_data[i >> 3] |= (b_bits[i] << (7 - (i & 7)));
}
static void tch_hr_disassemble(ubit_t *b_bits, const uint8_t *tch_data)
{
- int i, j;
+ int i;
- for (i = 0, j = 8; i < 112; i++, j++)
- b_bits[i] = (tch_data[j >> 3] >> (7 - (j & 7))) & 1;
+ for (i = 0; i < 112; i++)
+ b_bits[i] = (tch_data[i >> 3] >> (7 - (i & 7))) & 1;
}
/* assemble a EFR codec frame in format as used inside RTP */
@@ -1809,7 +1825,7 @@ static void tch_efr_unreorder(ubit_t *s, ubit_t *p, const ubit_t *w)
s[119] = (sum >= 2);
memcpy(s + 121, w + 125, 53);
sum = s[172] + w[178] + w[179];
- s[172] = (sum > 2);
+ s[172] = (sum >= 2);
memcpy(s + 174, w + 180, 50);
sum = s[222] + w[230] + w[231];
s[222] = (sum >= 2);
@@ -1865,7 +1881,7 @@ int gsm0503_tch_fr_decode(uint8_t *tch_data, const sbit_t *bursts,
return -1;
}
- return 23;
+ return GSM_MACBLOCK_LEN;
}
osmo_conv_decode_ber(&gsm0503_tch_fr, cB, conv, n_errors, n_bits_total);
@@ -1932,40 +1948,39 @@ int gsm0503_tch_fr_encode(ubit_t *bursts, const uint8_t *tch_data,
switch (len) {
case GSM_EFR_BYTES: /* TCH EFR */
-
tch_efr_disassemble(s, tch_data);
-
tch_efr_protected(s, b);
-
osmo_crc8gen_set_bits(&gsm0503_tch_efr_crc8, b, 65, p);
-
tch_efr_reorder(w, s, p);
-
tch_efr_w_to_d(d, w);
-
goto coding_efr_fr;
case GSM_FR_BYTES: /* TCH FR */
tch_fr_disassemble(w, tch_data, net_order);
-
tch_fr_b_to_d(d, w);
-
coding_efr_fr:
osmo_crc8gen_set_bits(&gsm0503_tch_fr_crc3, d, 50, p);
-
tch_fr_reorder(conv, d, p);
-
memcpy(cB + 378, d + 182, 78);
-
osmo_conv_encode(&gsm0503_tch_fr, conv, cB);
-
h = 0;
-
+ break;
+ case 0: /* no data, induce BFI in the receiver */
+ /* Do the same thing that sysmoBTS PHY does when fed a 0-length
+ * payload for DL: set all u(k) bits to 0, and do the same
+ * with all class 2 bits. This operation is NOT the same as
+ * an FR codec frame of all zero bits: with all-zeros d(k) input
+ * the CRC3 function will produce 111 output, whereas we
+ * transmit 000 in those parity bits too. The result will be
+ * an induced BFI (bad frame indication) condition in the
+ * receiver, for both TCH/FS and TCH/EFS decoders. */
+ memset(conv, 0, sizeof(conv));
+ memset(cB + 378, 0, 78);
+ osmo_conv_encode(&gsm0503_tch_fr, conv, cB);
+ h = 0;
break;
case GSM_MACBLOCK_LEN: /* FACCH */
_xcch_encode_cB(cB, tch_data);
-
h = 1;
-
break;
default:
return -1;
@@ -1982,13 +1997,13 @@ coding_efr_fr:
}
/*! Perform channel decoding of a HR(v1) channel according TS 05.03
- * \param[out] tch_data Codec frame in RTP payload format
- * \param[in] bursts buffer containing the symbols of 8 bursts
+ * \param[out] tch_data Codec frame in TS 101 318 canonical format
+ * \param[in] bursts buffer containing the symbols of 6 bursts
* \param[in] odd Odd (1) or even (0) frame number
* \param[out] n_errors Number of detected bit errors
* \param[out] n_bits_total Total number of bits
* \returns length of bytes used in \a tch_data output buffer; negative on error */
-int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
+int gsm0503_tch_hr_decode2(uint8_t *tch_data, const sbit_t *bursts, int odd,
int *n_errors, int *n_bits_total)
{
sbit_t iB[912], cB[456], h;
@@ -2002,7 +2017,7 @@ int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
steal -= h;
}
- for (i = 2; i < 5; i++) {
+ for (i = 2; i < 6; i++) {
gsm0503_tch_burst_unmap(NULL, &bursts[i * 116], &h, 1);
steal -= h;
}
@@ -2055,7 +2070,35 @@ int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
tch_hr_reassemble(tch_data, b);
- return 15;
+ return GSM_HR_BYTES;
+}
+
+/*! Perform channel decoding of a HR(v1) channel according TS 05.03,
+ * deprecated legacy API.
+ * \param[out] tch_data Codec frame in pseudo-RFC5993 format
+ * \param[in] bursts buffer containing the symbols of 6 bursts
+ * \param[in] odd Odd (1) or even (0) frame number
+ * \param[out] n_errors Number of detected bit errors
+ * \param[out] n_bits_total Total number of bits
+ * \returns length of bytes used in \a tch_data output buffer; negative on error
+ *
+ * The HR1 codec frame format returned by this function is pseudo-RFC5993,
+ * not true RFC 5993, as there is no SID classification being done
+ * and the FT bits in the ToC octet are always set to 0 - but this
+ * arguably-bogus format is the legacy public API.
+ */
+int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
+ int *n_errors, int *n_bits_total)
+{
+ int rc;
+
+ rc = gsm0503_tch_hr_decode2(tch_data, bursts, odd, n_errors,
+ n_bits_total);
+ if (rc != GSM_HR_BYTES)
+ return rc;
+ memmove(tch_data + 1, tch_data, GSM_HR_BYTES);
+ tch_data[0] = 0x00; /* FT=0, note absence of SID classification */
+ return GSM_HR_BYTES_RTP_RFC5993;
}
/*! Perform channel encoding on a TCH/HS channel according to TS 05.03
@@ -2070,46 +2113,41 @@ int gsm0503_tch_hr_encode(ubit_t *bursts, const uint8_t *tch_data, int len)
int i;
switch (len) {
- case 15: /* TCH HR */
+ case GSM_HR_BYTES_RTP_RFC5993: /* TCH HR with RFC 5993 prefix */
+ tch_data++;
+ /* fall-through */
+ case GSM_HR_BYTES: /* TCH HR in "pure" form */
tch_hr_disassemble(b, tch_data);
-
tch_hr_b_to_d(d, b);
-
osmo_crc8gen_set_bits(&gsm0503_tch_fr_crc3, d + 73, 22, p);
-
tch_hr_reorder(conv, d, p);
-
- osmo_conv_encode(&gsm0503_tch_hr, conv, cB);
-
memcpy(cB + 211, d + 95, 17);
-
+hr_conv_coding:
+ osmo_conv_encode(&gsm0503_tch_hr, conv, cB);
h = 0;
-
gsm0503_tch_hr_interleave(cB, iB);
-
for (i = 0; i < 4; i++) {
gsm0503_tch_burst_map(&iB[i * 114],
&bursts[i * 116], &h, i >> 1);
}
-
break;
+ case 0: /* no data, induce BFI in the receiver */
+ /* see comments in gsm0503_tch_fr_encode() - same deal here */
+ memset(conv, 0, sizeof(conv));
+ memset(cB + 211, 0, 17);
+ goto hr_conv_coding;
case GSM_MACBLOCK_LEN: /* FACCH */
_xcch_encode_cB(cB, tch_data);
-
h = 1;
-
gsm0503_tch_fr_interleave(cB, iB);
-
for (i = 0; i < 6; i++) {
gsm0503_tch_burst_map(&iB[i * 114],
&bursts[i * 116], &h, i >> 2);
}
-
for (i = 2; i < 4; i++) {
gsm0503_tch_burst_map(&iB[i * 114 + 456],
&bursts[i * 116], &h, 1);
}
-
break;
default:
return -1;
@@ -2118,6 +2156,26 @@ int gsm0503_tch_hr_encode(ubit_t *bursts, const uint8_t *tch_data, int len)
return 0;
}
+/* TCH/AFS: parse codec ID (CMI or CMC/CMR) from coded in-band data (16 bit) */
+static uint8_t gsm0503_tch_afs_decode_inband(const sbit_t *cB)
+{
+ unsigned int id = 0, best = 0;
+ unsigned int i, j, k;
+
+ for (i = 0; i < 4; i++) {
+ /* FIXME: why not using remaining (16 - 8) soft-bits here? */
+ for (j = 0, k = 0; j < 8; j++)
+ k += abs(((int)gsm0503_afs_ic_sbit[i][j]) - ((int)cB[j]));
+
+ if (i == 0 || k < best) {
+ best = k;
+ id = i;
+ }
+ }
+
+ return id;
+}
+
/*! Perform channel decoding of a TCH/AFS channel according TS 05.03
* \param[out] tch_data Codec frame in RTP payload format
* \param[in] bursts buffer containing the symbols of 8 bursts
@@ -2160,12 +2218,10 @@ int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts,
{
sbit_t iB[912], cB[456], h;
ubit_t d[244], p[6], conv[250];
- int i, j, k, best = 0, rv, len, steal = 0, id = 0;
- ubit_t cBd[456];
+ int i, rv, len, steal = 0, id = -1;
*n_errors = 0; *n_bits_total = 0;
static ubit_t sid_first_dummy[64] = { 0 };
sbit_t sid_update_enc[256];
- uint8_t dtx_prev;
for (i=0; i<8; i++) {
gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, i >> 2);
@@ -2175,6 +2231,12 @@ int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts,
gsm0503_tch_fr_deinterleave(cB, iB);
if (steal > 0) {
+ /* If not NULL, dtx indicates type of previously decoded TCH/AFS frame.
+ * It's normally updated by gsm0503_detect_afs_dtx_frame2(), which is not
+ * reached in case of FACCH. Reset it here to avoid FACCH/F frames being
+ * misinterpreted as AMR's special DTX frames. */
+ if (dtx != NULL)
+ *dtx = AMR_OTHER;
rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total);
if (rv) {
/* Error decoding FACCH frame */
@@ -2186,16 +2248,20 @@ int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts,
/* Determine the DTX frame type (SID_UPDATE, ONSET etc...) */
if (dtx) {
- osmo_sbit2ubit(cBd, cB, 456);
- dtx_prev = *dtx;
- *dtx = gsm0503_detect_afs_dtx_frame(n_errors, n_bits_total, cBd);
+ const enum gsm0503_amr_dtx_frames dtx_prev = *dtx;
+
+ *dtx = gsm0503_detect_afs_dtx_frame2(n_errors, n_bits_total, &id, cB);
- if (dtx_prev == AFS_SID_UPDATE && *dtx == AMR_OTHER) {
+ switch (*dtx) {
+ case AMR_OTHER:
/* NOTE: The AFS_SID_UPDATE frame is splitted into
* two half rate frames. If the id marker frame
* (AFS_SID_UPDATE) is detected the following frame
* contains the actual comfort noised data part of
* (AFS_SID_UPDATE_CN). */
+ if (dtx_prev != AFS_SID_UPDATE)
+ break;
+ /* TODO: parse CMI _and_ CMC/CMR (16 + 16 bit) */
*dtx = AFS_SID_UPDATE_CN;
extract_afs_sid_update(sid_update_enc, cB);
@@ -2211,35 +2277,28 @@ int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts,
tch_amr_sid_update_append(conv, 1,
(codec_mode_req) ? codec[*ft]
- : codec[id]);
+ : codec[id > 0 ? id : 0]);
tch_amr_reassemble(tch_data, conv, 39);
len = 5;
goto out;
- } else if (*dtx == AFS_SID_FIRST) {
+ case AFS_SID_FIRST: /* TODO: parse CMI or CMC/CMR (16 bit) */
tch_amr_sid_update_append(sid_first_dummy, 0,
(codec_mode_req) ? codec[*ft]
- : codec[id]);
- tch_amr_reassemble(tch_data, conv, 39);
+ : codec[id > 0 ? id : 0]);
+ tch_amr_reassemble(tch_data, sid_first_dummy, 39);
len = 5;
goto out;
- } else if (*dtx == AFS_ONSET) {
+ case AFS_SID_UPDATE: /* TODO: parse CMI _and_ CMC/CMR (16 + 16 bit) */
+ case AFS_ONSET:
len = 0;
goto out;
+ default:
+ break;
}
}
- for (i = 0; i < 4; i++) {
- for (j = 0, k = 0; j < 8; j++)
- k += abs(((int)gsm0503_afs_ic_sbit[i][j]) - ((int)cB[j]));
-
- if (i == 0 || k < best) {
- best = k;
- id = i;
- }
- }
-
- /* Check if indicated codec fits into range of codecs */
- if (id >= codecs) {
+ /* Parse codec ID (CMI or CMC/CMR) and check if it fits into range of codecs */
+ if ((id = gsm0503_tch_afs_decode_inband(&cB[0])) >= codecs) {
/* Codec mode out of range, return id */
return id;
}
@@ -2390,10 +2449,12 @@ int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts,
out:
/* Change codec request / indication, if frame is valid */
- if (codec_mode_req)
- *cmr = id;
- else
- *ft = id;
+ if (id != -1) {
+ if (codec_mode_req)
+ *cmr = id;
+ else
+ *ft = id;
+ }
return len;
}
@@ -2401,7 +2462,7 @@ out:
/*! Perform channel encoding on a TCH/AFS channel according to TS 05.03
* \param[out] bursts caller-allocated output buffer for bursts bits
* \param[in] tch_data Codec input data in RTP payload format
- * \param[in] len Length of \a tch_data in bytes
+ * \param[in] len Length of \a tch_data in bytes or 0 to generate a bad frame
* \param[in] codec_mode_req Use CMR (1) or FT (0)
* \param[in] codec Array of codecs (active codec set)
* \param[in] codecs Number of entries in \a codec
@@ -2409,7 +2470,7 @@ out:
* \param[in] cmr Codec Mode Request (used in codec_mode_req = 1 only)
* \returns 0 in case of success; negative on error */
int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
- int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft,
+ int codec_mode_req, const uint8_t *codec, int codecs, uint8_t ft,
uint8_t cmr)
{
ubit_t iB[912], cB[456], h;
@@ -2427,28 +2488,27 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
h = 0;
- if (codec_mode_req) {
- if (cmr >= codecs) {
- /* FIXME: CMR ID is not in codec list! */
- return -1;
- }
- id = cmr;
- } else {
- if (ft >= codecs) {
- /* FIXME: FT ID is not in codec list! */
- return -1;
- }
- id = ft;
- }
+ id = codec_mode_req ? cmr : ft;
+ if (OSMO_UNLIKELY(id >= ARRAY_SIZE(gsm0503_afs_ic_ubit)))
+ return -1;
+ if (OSMO_UNLIKELY(ft >= codecs))
+ return -1;
switch (codec[ft]) {
case 7: /* TCH/AFS12.2 */
- if (len != 31)
- goto invalid_length;
-
- tch_amr_disassemble(d, tch_data, 244);
-
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 81, p);
+ if (!len) {
+ /* No data, induce BFI in the receiver by inverted CRC bits.
+ * The data bit are all 0, so the correct parity bits would be 111111. */
+ memset(d, 0, 244);
+ memset(p, 0, 6);
+ } else {
+ if (len != 31)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 244);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 81, p);
+ }
tch_amr_merge(conv, d, p, 244, 81);
@@ -2456,12 +2516,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 6: /* TCH/AFS10.2 */
- if (len != 26)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 204);
+ memset(p, 0, 6);
+ } else {
+ if (len != 26)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 204);
+ tch_amr_disassemble(d, tch_data, 204);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 65, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 65, p);
+ }
tch_amr_merge(conv, d, p, 204, 65);
@@ -2469,12 +2535,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 5: /* TCH/AFS7.95 */
- if (len != 20)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 159);
+ memset(p, 0, 6);
+ } else {
+ if (len != 20)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 159);
+ tch_amr_disassemble(d, tch_data, 159);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 75, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 75, p);
+ }
tch_amr_merge(conv, d, p, 159, 75);
@@ -2482,12 +2554,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 4: /* TCH/AFS7.4 */
- if (len != 19)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 148);
+ memset(p, 0, 6);
+ } else {
+ if (len != 19)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 148);
+ tch_amr_disassemble(d, tch_data, 148);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 61, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 61, p);
+ }
tch_amr_merge(conv, d, p, 148, 61);
@@ -2495,12 +2573,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 3: /* TCH/AFS6.7 */
- if (len != 17)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 134);
+ memset(p, 0, 6);
+ } else {
+ if (len != 17)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 134);
+ tch_amr_disassemble(d, tch_data, 134);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ }
tch_amr_merge(conv, d, p, 134, 55);
@@ -2508,12 +2592,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 2: /* TCH/AFS5.9 */
- if (len != 15)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 118);
+ memset(p, 0, 6);
+ } else {
+ if (len != 15)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 118);
+ tch_amr_disassemble(d, tch_data, 118);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ }
tch_amr_merge(conv, d, p, 118, 55);
@@ -2521,12 +2611,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 1: /* TCH/AFS5.15 */
- if (len != 13)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 103);
+ memset(p, 0, 6);
+ } else {
+ if (len != 13)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 103);
+ tch_amr_disassemble(d, tch_data, 103);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 49, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 49, p);
+ }
tch_amr_merge(conv, d, p, 103, 49);
@@ -2534,12 +2630,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 0: /* TCH/AFS4.75 */
- if (len != 12)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 95);
+ memset(p, 0, 6);
+ } else {
+ if (len != 12)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 95);
+ tch_amr_disassemble(d, tch_data, 95);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 39, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 39, p);
+ }
tch_amr_merge(conv, d, p, 95, 39);
@@ -2568,9 +2670,29 @@ invalid_length:
return -1;
}
-/*! Perform channel decoding of a TCH/AFS channel according TS 05.03
+/* TCH/AHS: parse codec ID (CMI or CMC/CMR) from coded in-band data (16 bit) */
+static uint8_t gsm0503_tch_ahs_decode_inband(const sbit_t *cB)
+{
+ unsigned int id = 0, best = 0;
+ unsigned int i, j, k;
+
+ for (i = 0, k = 0; i < 4; i++) {
+ /* FIXME: why not using remaining (16 - 4) soft-bits here? */
+ for (j = 0, k = 0; j < 4; j++)
+ k += abs(((int)gsm0503_ahs_ic_sbit[i][j]) - ((int)cB[j]));
+
+ if (i == 0 || k < best) {
+ best = k;
+ id = i;
+ }
+ }
+
+ return id;
+}
+
+/*! Perform channel decoding of a TCH/AHS channel according TS 05.03
* \param[out] tch_data Codec frame in RTP payload format
- * \param[in] bursts buffer containing the symbols of 8 bursts
+ * \param[in] bursts buffer containing the symbols of 6 bursts
* \param[in] odd Is this an odd (1) or even (0) frame number?
* \param[in] codec_mode_req is this CMR (1) or CMC (0)
* \param[in] codec array of active codecs (active codec set)
@@ -2591,9 +2713,9 @@ int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
n_bits_total, NULL);
}
-/*! Perform channel decoding of a TCH/AFS channel according TS 05.03
+/*! Perform channel decoding of a TCH/AHS channel according TS 05.03
* \param[out] tch_data Codec frame in RTP payload format
- * \param[in] bursts buffer containing the symbols of 8 bursts
+ * \param[in] bursts buffer containing the symbols of 6 bursts
* \param[in] odd Is this an odd (1) or even (0) frame number?
* \param[in] codec_mode_req is this CMR (1) or CMC (0)
* \param[in] codec array of active codecs (active codec set)
@@ -2612,10 +2734,8 @@ int gsm0503_tch_ahs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, int odd,
{
sbit_t iB[912], cB[456], h;
ubit_t d[244], p[6], conv[135];
- int i, j, k, best = 0, rv, len, steal = 0, id = 0;
- ubit_t cBd[456];
+ int i, rv, len, steal = 0, id = -1;
static ubit_t sid_first_dummy[64] = { 0 };
- uint8_t dtx_prev;
/* only unmap the stealing bits */
if (!odd) {
@@ -2631,6 +2751,13 @@ int gsm0503_tch_ahs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, int odd,
/* if we found a stole FACCH, but only at correct alignment */
if (steal > 0) {
+ /* If not NULL, dtx indicates type of previously decoded TCH/AHS frame.
+ * It's normally updated by gsm0503_detect_ahs_dtx_frame2(), which is not
+ * reached in case of FACCH. Reset it here to avoid FACCH/H frames being
+ * misinterpreted as AMR's special DTX frames. */
+ if (dtx != NULL)
+ *dtx = AMR_OTHER;
+
for (i = 0; i < 6; i++) {
gsm0503_tch_burst_unmap(&iB[i * 114],
&bursts[i * 116], NULL, i >> 2);
@@ -2661,21 +2788,37 @@ int gsm0503_tch_ahs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, int odd,
/* Determine the DTX frame type (SID_UPDATE, ONSET etc...) */
if (dtx) {
- osmo_sbit2ubit(cBd, cB, 456);
- dtx_prev = *dtx;
- *dtx = gsm0503_detect_ahs_dtx_frame(n_errors, n_bits_total, cBd);
-
- if (dtx_prev == AHS_SID_UPDATE && *dtx == AMR_OTHER) {
- /* NOTE: The AHS_SID_UPDATE frame is splitted into
- * two half rate frames. If the id marker frame
- * (AHS_SID_UPDATE) is detected the following frame
- * contains the actual comfort noised data part of
- * (AHS_SID_UPDATE_CN). */
+ int n_bits_total_sid;
+ int n_errors_sid;
+
+ *dtx = gsm0503_detect_ahs_dtx_frame2(n_errors, n_bits_total, &id, cB);
+ /* TODO: detect and handle AHS_SID_UPDATE + AHS_SID_UPDATE_INH */
+
+ switch (*dtx) {
+ case AHS_SID_UPDATE: /* TODO: parse CMI _and_ CMC/CMR (16 + 16 bit) */
+ /* cB[] contains 16 bits of coded in-band data and 212 bits containing
+ * the identification marker. We need to unmap/deinterleave 114 odd
+ * bits from the last two blocks, 114 even bits from the first two
+ * blocks and combine them together. */
+ gsm0503_tch_burst_unmap(&iB[0 * 114], &bursts[2 * 116], NULL, 0);
+ gsm0503_tch_burst_unmap(&iB[1 * 114], &bursts[3 * 116], NULL, 0);
+ gsm0503_tch_burst_unmap(&iB[2 * 114], &bursts[0 * 116], NULL, 1);
+ gsm0503_tch_burst_unmap(&iB[3 * 114], &bursts[1 * 116], NULL, 1);
+ gsm0503_tch_hr_deinterleave(cB, iB);
+
+ /* cB[] is expected to contain 16 bits of coded in-band data and
+ * 212 bits containing the coded data (53 bits coded at 1/4 rate). */
*dtx = AHS_SID_UPDATE_CN;
osmo_conv_decode_ber(&gsm0503_tch_axs_sid_update,
- cB + 16, conv, n_errors,
- n_bits_total);
+ cB + 16, conv, &n_errors_sid,
+ &n_bits_total_sid);
+ /* gsm0503_detect_ahs_dtx_frame2() calculates BER for the marker,
+ * osmo_conv_decode_ber() calculates BER for the coded data. */
+ if (n_errors != NULL)
+ *n_errors += n_errors_sid;
+ if (n_bits_total != NULL)
+ *n_bits_total += n_bits_total_sid;
rv = osmo_crc16gen_check_bits(&gsm0503_amr_crc14, conv,
35, conv + 35);
if (rv != 0) {
@@ -2685,38 +2828,30 @@ int gsm0503_tch_ahs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, int odd,
tch_amr_sid_update_append(conv, 1,
(codec_mode_req) ? codec[*ft]
- : codec[id]);
+ : codec[id > 0 ? id : 0]);
tch_amr_reassemble(tch_data, conv, 39);
len = 5;
goto out;
- } else if (*dtx == AHS_SID_FIRST_P2) {
+ case AHS_SID_FIRST_P2:
tch_amr_sid_update_append(sid_first_dummy, 0,
(codec_mode_req) ? codec[*ft]
- : codec[id]);
+ : codec[id > 0 ? id : 0]);
tch_amr_reassemble(tch_data, sid_first_dummy, 39);
len = 5;
goto out;
- } else if (*dtx == AHS_SID_UPDATE || *dtx == AHS_ONSET
- || *dtx == AHS_SID_FIRST_INH
- || *dtx == AHS_SID_UPDATE_INH
- || *dtx == AHS_SID_FIRST_P1) {
+ case AHS_ONSET:
+ case AHS_SID_FIRST_INH: /* TODO: parse CMI or CMC/CMR (16 bit) */
+ case AHS_SID_UPDATE_INH: /* TODO: parse CMI or CMC/CMR (16 bit) */
+ case AHS_SID_FIRST_P1: /* TODO: parse CMI or CMC/CMR (16 bit) */
len = 0;
goto out;
+ default:
+ break;
}
}
- for (i = 0; i < 4; i++) {
- for (j = 0, k = 0; j < 4; j++)
- k += abs(((int)gsm0503_ahs_ic_sbit[i][j]) - ((int)cB[j]));
-
- if (i == 0 || k < best) {
- best = k;
- id = i;
- }
- }
-
- /* Check if indicated codec fits into range of codecs */
- if (id >= codecs) {
+ /* Parse codec ID (CMI or CMC/CMR) and check if it fits into range of codecs */
+ if ((id = gsm0503_tch_ahs_decode_inband(&cB[0])) >= codecs) {
/* Codec mode out of range, return id */
return id;
}
@@ -2851,10 +2986,12 @@ int gsm0503_tch_ahs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, int odd,
out:
/* Change codec request / indication, if frame is valid */
- if (codec_mode_req)
- *cmr = id;
- else
- *ft = id;
+ if (id != -1) {
+ if (codec_mode_req)
+ *cmr = id;
+ else
+ *ft = id;
+ }
return len;
}
@@ -2862,7 +2999,7 @@ out:
/*! Perform channel encoding on a TCH/AHS channel according to TS 05.03
* \param[out] bursts caller-allocated output buffer for bursts bits
* \param[in] tch_data Codec input data in RTP payload format
- * \param[in] len Length of \a tch_data in bytes
+ * \param[in] len Length of \a tch_data in bytes or 0 to generate a bad frame
* \param[in] codec_mode_req Use CMR (1) or FT (0)
* \param[in] codec Array of codecs (active codec set)
* \param[in] codecs Number of entries in \a codec
@@ -2870,7 +3007,7 @@ out:
* \param[in] cmr Codec Mode Request (used in codec_mode_req = 1 only)
* \returns 0 in case of success; negative on error */
int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
- int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft,
+ int codec_mode_req, const uint8_t *codec, int codecs, uint8_t ft,
uint8_t cmr)
{
ubit_t iB[912], cB[456], h;
@@ -2897,28 +3034,27 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
h = 0;
- if (codec_mode_req) {
- if (cmr >= codecs) {
- /* FIXME: CMR ID %d not in codec list */
- return -1;
- }
- id = cmr;
- } else {
- if (ft >= codecs) {
- /* FIXME: FT ID %d not in codec list */
- return -1;
- }
- id = ft;
- }
+ id = codec_mode_req ? cmr : ft;
+ if (OSMO_UNLIKELY(id >= ARRAY_SIZE(gsm0503_ahs_ic_ubit)))
+ return -1;
+ if (OSMO_UNLIKELY(ft >= codecs))
+ return -1;
switch (codec[ft]) {
case 5: /* TCH/AHS7.95 */
- if (len != 20)
- goto invalid_length;
-
- tch_amr_disassemble(d, tch_data, 159);
-
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 67, p);
+ if (!len) {
+ /* No data, induce BFI in the receiver by inverted CRC bits.
+ * The data bit are all 0, so the correct parity bits would be 111111. */
+ memset(d, 0, 159);
+ memset(p, 0, 6);
+ } else {
+ if (len != 20)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 159);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 67, p);
+ }
tch_amr_merge(conv, d, p, 123, 67);
@@ -2928,12 +3064,18 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 4: /* TCH/AHS7.4 */
- if (len != 19)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 148);
+ memset(p, 0, 6);
+ } else {
+ if (len != 19)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 148);
+ tch_amr_disassemble(d, tch_data, 148);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 61, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 61, p);
+ }
tch_amr_merge(conv, d, p, 120, 61);
@@ -2943,12 +3085,18 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 3: /* TCH/AHS6.7 */
- if (len != 17)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 134);
+ memset(p, 0, 6);
+ } else {
+ if (len != 17)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 134);
+ tch_amr_disassemble(d, tch_data, 134);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ }
tch_amr_merge(conv, d, p, 110, 55);
@@ -2958,12 +3106,18 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 2: /* TCH/AHS5.9 */
- if (len != 15)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 118);
+ memset(p, 0, 6);
+ } else {
+ if (len != 15)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 118);
+ tch_amr_disassemble(d, tch_data, 118);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ }
tch_amr_merge(conv, d, p, 102, 55);
@@ -2973,12 +3127,18 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 1: /* TCH/AHS5.15 */
- if (len != 13)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 103);
+ memset(p, 0, 6);
+ } else {
+ if (len != 13)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 103);
+ tch_amr_disassemble(d, tch_data, 103);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 49, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 49, p);
+ }
tch_amr_merge(conv, d, p, 91, 49);
@@ -2988,12 +3148,18 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 0: /* TCH/AHS4.75 */
- if (len != 12)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 95);
+ memset(p, 0, 6);
+ } else {
+ if (len != 12)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 95);
+ tch_amr_disassemble(d, tch_data, 95);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 39, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 39, p);
+ }
tch_amr_merge(conv, d, p, 83, 39);
@@ -3007,7 +3173,7 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
return -1;
}
- memcpy(cB, gsm0503_afs_ic_ubit[id], 4);
+ memcpy(cB, gsm0503_ahs_ic_ubit[id], 4);
gsm0503_tch_hr_interleave(cB, iB);
@@ -3212,4 +3378,491 @@ int gsm0503_sch_encode(ubit_t *burst, const uint8_t *sb_info)
return 0;
}
+/*
+ * GSM CSD transcoding
+ */
+
+static inline void _tch_csd_burst_map(ubit_t *burst, const ubit_t *iB)
+{
+ unsigned int i;
+
+ /* hu(B): copy *even* numbered bits if not stolen by FACCH */
+ if (burst[58] == 0) {
+ for (i = 0; i < 57; i += 2)
+ burst[i] |= iB[i];
+ for (i = 58; i < 114; i += 2)
+ burst[i + 2] |= iB[i];
+ }
+
+ /* hl(B): copy *odd* numbered bits if not stolen by FACCH */
+ if (burst[57] == 0) {
+ for (i = 1; i < 57; i += 2)
+ burst[i] |= iB[i];
+ for (i = 57; i < 114; i += 2)
+ burst[i + 2] |= iB[i];
+ }
+}
+
+/*! Perform channel encoding of a TCH/F9.6 channel as per section 3.3.
+ * \param[out] bursts Caller-allocated buffer for symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[in] data Data to be encoded (240 unpacked bits).
+ * \returns 0 in case of success; negative on error. */
+int gsm0503_tch_fr96_encode(ubit_t *bursts, const ubit_t *data)
+{
+ ubit_t iB[22 * 114], cB[4 * 114];
+ ubit_t conv[4 * 60 + 4];
+
+ /* 3.3.2 Block code: b1(60) + b2(60) + b3(60) + b4(60) + pad(4) */
+ memcpy(&conv[0], &data[0], 4 * 60);
+ /* pad(4) is set to 0 by osmo_conv_encode() below */
+
+ /* 3.3.3 Convolutional encoder */
+ osmo_conv_encode(&gsm0503_tch_f96, &conv[0], &cB[0]);
+
+ /* 3.3.4 Interleaving */
+ memset(&iB[0], 0, sizeof(iB));
+ gsm0503_tch_f96_interleave(&cB[0], &iB[0]);
+
+ /* 3.3.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++)
+ _tch_csd_burst_map(&bursts[i * 116], &iB[i * 114]);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a TCH/F9.6 channel as per section 3.3.
+ * \param[out] data Caller-allocated buffer for decoded data (240 unpacked bits).
+ * \param[in] bursts Buffer containing the symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of unpacked bits used in the output buffer; negative on error. */
+int gsm0503_tch_fr96_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[22 * 114], cB[4 * 114];
+ ubit_t conv[4 * 60 + 4];
+
+ /* 3.3.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++) {
+ memcpy(&iB[i * 114], &bursts[i * 116], 57);
+ memcpy(&iB[i * 114 + 57], &bursts[i * 116 + 59], 57);
+ }
+
+ /* 3.3.4 Interleaving */
+ gsm0503_tch_f96_deinterleave(&cB[0], &iB[0]);
+
+ /* 3.3.3 Convolutional encoder */
+ osmo_conv_decode_ber(&gsm0503_tch_f96, &cB[0], &conv[0], n_errors, n_bits_total);
+
+ /* 3.3.2 Block code: b1(60) + b2(60) + b3(60) + b4(60) + pad(4) */
+ memcpy(&data[0], &conv[0], 4 * 60);
+
+ return 4 * 60;
+}
+
+/*! Perform channel encoding of a TCH/F4.8 channel as per section 3.4.
+ * \param[out] bursts Caller-allocated buffer for symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[in] data Data to be encoded (120 unpacked bits).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_fr48_encode(ubit_t *bursts, const ubit_t *data)
+{
+ ubit_t iB[22 * 114], cB[4 * 114];
+ ubit_t conv[2 * 60 + 32];
+
+ /* 3.4.2 Block code:
+ *
+ * Sixteen bits equal to 0 are added to the 60 information bits, the result
+ * being a block of 76 bits, {u(0),u(1),...,u(75)}, with:
+ *
+ * u(19k+p) = d(15k+p) for k = 0,1,2,3 and p = 0,1,...,14;
+ * u(19k+p) = 0 for k = 0,1,2,3 and p = 15,16,17,18.
+ *
+ * Two such blocks forming a block of 152 bits: u1 + u2. */
+ for (unsigned int k = 0; k < 2 * 4; k++) {
+ memcpy(&conv[19 * k], &data[15 * k], 15);
+ memset(&conv[19 * k + 15], 0, 4);
+ }
+
+ /* 3.4.3 Convolutional encoder */
+ osmo_conv_encode(&gsm0503_tch_f48, &conv[0], &cB[0]);
+
+ /* 3.4.4 Interleaving: as specified for the TCH/F9.6 in subclause 3.3.4 */
+ memset(&iB[0], 0, sizeof(iB));
+ gsm0503_tch_f96_interleave(&cB[0], &iB[0]);
+
+ /* 3.4.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++)
+ _tch_csd_burst_map(&bursts[i * 116], &iB[i * 114]);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a TCH/F4.8 channel as per section 3.4.
+ * \param[out] data Caller-allocated buffer for decoded data (120 unpacked bits).
+ * \param[in] bursts Buffer containing the symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of unpacked bits used in the output buffer; negative on error. */
+int gsm0503_tch_fr48_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[22 * 114], cB[4 * 114];
+ ubit_t conv[2 * 60 + 32];
+
+ /* 3.4.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++) {
+ memcpy(&iB[i * 114], &bursts[i * 116], 57);
+ memcpy(&iB[i * 114 + 57], &bursts[i * 116 + 59], 57);
+ }
+
+ /* 3.4.4 Interleaving: as specified for the TCH/F9.6 in subclause 3.3.4 */
+ gsm0503_tch_f96_deinterleave(&cB[0], &iB[0]);
+
+ /* 3.4.3 Convolutional encoder */
+ osmo_conv_decode_ber(&gsm0503_tch_f48, &cB[0], &conv[0], n_errors, n_bits_total);
+
+ /* 3.4.2 Block code:
+ *
+ * Sixteen bits equal to 0 are added to the 60 information bits, the result
+ * being a block of 76 bits, {u(0),u(1),...,u(75)}, with:
+ *
+ * u(19k+p) = d(15k+p) for k = 0,1,2,3 and p = 0,1,...,14;
+ * u(19k+p) = 0 for k = 0,1,2,3 and p = 15,16,17,18.
+ *
+ * Two such blocks forming a block of 152 bits: u1 + u2. */
+ for (unsigned int k = 0; k < 2 * 4; k++)
+ memcpy(&data[15 * k], &conv[19 * k], 15);
+
+ return 2 * 60;
+}
+
+/*! Perform channel encoding of a TCH/H4.8 channel as per section 3.5.
+ * The algorithm is identical to TCH/F9.6, so it's just a wrapper.
+ * \param[out] bursts Caller-allocated buffer for symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[in] data Data to be encoded (240 unpacked bits).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_hr48_encode(ubit_t *bursts, const ubit_t *data)
+{
+ return gsm0503_tch_fr96_encode(bursts, data);
+}
+
+/*! Perform channel decoding of a TCH/H4.8 channel as per section 3.5.
+ * The algorithm is identical to TCH/F9.6, so it's just a wrapper.
+ * \param[out] data Caller-allocated buffer for decoded data (240 unpacked bits).
+ * \param[in] bursts Buffer containing the symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of unpacked bits used in the output buffer; negative on error. */
+int gsm0503_tch_hr48_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ return gsm0503_tch_fr96_decode(data, bursts, n_errors, n_bits_total);
+}
+
+/*! Perform channel encoding of a TCH/F2.4 channel as per section 3.6.
+ * \param[out] bursts Caller-allocated buffer for symbols of 8 bursts,
+ * 8 * 2 * 58 == 928 bits total.
+ * \param[in] data Data to be encoded (72 unpacked bits).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_fr24_encode(ubit_t *bursts, const ubit_t *data)
+{
+ ubit_t iB[8 * 114], cB[4 * 114];
+ const ubit_t h = 0;
+
+ /* 3.6.{1-3} Block code and Convolutional encoder */
+ osmo_conv_encode(&gsm0503_tch_f24, &data[0], &cB[0]);
+
+ /* 3.6.4 Interleaving: as specified for the TCH/FS in subclause 3.1.3 */
+ gsm0503_tch_fr_interleave(&cB[0], &iB[0]);
+
+ /* 3.6.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 8; i++)
+ gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], &h, i >> 2);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a TCH/F2.4 channel as per section 3.6.
+ * \param[out] data Caller-allocated buffer for decoded data (72 unpacked bits).
+ * \param[in] bursts Buffer containing the symbols of 8 bursts,
+ * 8 * 2 * 58 == 928 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of unpacked bits used in the output buffer; negative on error. */
+int gsm0503_tch_fr24_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[8 * 114], cB[4 * 114];
+
+ /* 3.6.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 8; i++)
+ gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], NULL, i >> 2);
+
+ /* 3.6.4 Interleaving: as specified for the TCH/FS in subclause 3.1.3 */
+ gsm0503_tch_fr_deinterleave(&cB[0], &iB[0]);
+
+ /* 3.6.{1-3} Block code and Convolutional encoder */
+ osmo_conv_decode_ber(&gsm0503_tch_f24, &cB[0], &data[0], n_errors, n_bits_total);
+
+ return 72;
+}
+
+/*! Perform channel encoding of a TCH/H2.4 channel as per section 3.7.
+ * \param[out] bursts Caller-allocated buffer for symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[in] data Data to be encoded (144 unpacked bits).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_hr24_encode(ubit_t *bursts, const ubit_t *data)
+{
+ ubit_t iB[22 * 114], cB[4 * 114];
+
+ /* 3.7.{1-3} Block code and Convolutional encoder */
+ osmo_conv_encode(&gsm0503_tch_h24, &data[ 0], &cB[ 0]);
+ osmo_conv_encode(&gsm0503_tch_h24, &data[72], &cB[228]);
+
+ /* 3.7.4 Interleaving: as specified for the TCH/F9.6 in subclause 3.3.4 */
+ memset(&iB[0], 0, sizeof(iB));
+ gsm0503_tch_f96_interleave(&cB[0], &iB[0]);
+
+ /* 3.7.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++)
+ _tch_csd_burst_map(&bursts[i * 116], &iB[i * 114]);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a TCH/H2.4 channel as per section 3.7.
+ * \param[out] data Caller-allocated buffer for decoded data (144 unpacked bits).
+ * \param[in] bursts Buffer containing the symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of unpacked bits used in the output buffer; negative on error. */
+int gsm0503_tch_hr24_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ int n_errors_l[2], n_bits_total_l[2];
+ sbit_t iB[22 * 114], cB[4 * 114];
+
+ /* 3.7.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++) {
+ memcpy(&iB[i * 114], &bursts[i * 116], 57);
+ memcpy(&iB[i * 114 + 57], &bursts[i * 116 + 59], 57);
+ }
+
+ /* 3.7.4 Interleaving: as specified for the TCH/F9.6 in subclause 3.3.4 */
+ gsm0503_tch_f96_deinterleave(&cB[0], &iB[0]);
+
+ /* 3.7.{1-3} Block code and Convolutional encoder */
+ osmo_conv_decode_ber(&gsm0503_tch_h24, &cB[ 0], &data[ 0], &n_errors_l[0], &n_bits_total_l[0]);
+ osmo_conv_decode_ber(&gsm0503_tch_h24, &cB[228], &data[72], &n_errors_l[1], &n_bits_total_l[1]);
+
+ if (n_errors)
+ *n_errors = n_errors_l[0] + n_errors_l[1];
+
+ if (n_bits_total)
+ *n_bits_total = n_bits_total_l[0] + n_bits_total_l[1];
+
+ return 2 * 72;
+}
+
+/*! Perform channel encoding of a TCH/F14.4 channel as per section 3.8.
+ * \param[out] bursts Caller-allocated buffer for symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[in] data Data to be encoded (290 unpacked bits).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_fr144_encode(ubit_t *bursts, const ubit_t *data)
+{
+ ubit_t iB[22 * 114], cB[4 * 114];
+ ubit_t conv[290 + 4];
+
+ /* 3.8.2 Block code: b(290) + pad(4) */
+ memcpy(&conv[0], &data[0], 290);
+ /* pad(4) is set to 0 by osmo_conv_encode() below */
+
+ /* 3.8.3 Convolutional encoder */
+ osmo_conv_encode(&gsm0503_tch_f144, &conv[0], &cB[0]);
+
+ /* 3.8.4 Interleaving */
+ memset(&iB[0], 0, sizeof(iB));
+ gsm0503_tch_f96_interleave(&cB[0], &iB[0]);
+
+ /* 3.8.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++)
+ _tch_csd_burst_map(&bursts[i * 116], &iB[i * 114]);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a TCH/14.4 channel as per section 3.8.
+ * \param[out] data Caller-allocated buffer for decoded data (290 unpacked bits).
+ * \param[in] bursts Buffer containing the symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of unpacked bits used in the output buffer; negative on error. */
+int gsm0503_tch_fr144_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[22 * 114], cB[4 * 114];
+ ubit_t conv[294];
+
+ /* 3.8.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++) {
+ memcpy(&iB[i * 114], &bursts[i * 116], 57);
+ memcpy(&iB[i * 114 + 57], &bursts[i * 116 + 59], 57);
+ }
+
+ /* 3.8.4 Interleaving: as specified for the TCH/F9.6 in subclause 3.3.4 */
+ gsm0503_tch_f96_deinterleave(&cB[0], &iB[0]);
+
+ /* 3.8.3 Convolutional encoder */
+ osmo_conv_decode_ber(&gsm0503_tch_f144, &cB[0], &conv[0], n_errors, n_bits_total);
+
+ /* 3.8.2 Block code: b(290) + pad(4) */
+ memcpy(&data[0], &conv[0], 290);
+
+ return 290;
+}
+
+/*
+ * FACCH/[FH] transcoding
+ */
+
+/*! Perform channel encoding of a FACCH/F data as per section 4.2.
+ * \param[out] bursts Caller-allocated buffer for symbols of 8 bursts,
+ * 8 * 2 * 58 == 928 bits total.
+ * \param[in] data FACCH MAC block to be encoded (GSM_MACBLOCK_LEN).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_fr_facch_encode(ubit_t *bursts, const uint8_t *data)
+{
+ ubit_t iB[8 * 114], cB[4 * 114];
+ const ubit_t h = 1;
+
+ /* 4.2.1-3 as specified for the SACCH in 4.1.1-3 */
+ _xcch_encode_cB(&cB[0], &data[0]);
+
+ /* 4.2.4 Interleaving: as specified for the TCH/FS in subclause 3.1.3 */
+ gsm0503_tch_fr_interleave(&cB[0], &iB[0]);
+
+ /* 4.2.5 Mapping on a Burst:
+ * - hu(B)=1 the even numbered bits in the first 4 bursts and
+ * - hl(B)=1 the odd numbered bits of the last 4 bursts are stolen. */
+ for (unsigned int i = 0; i < 8; i++)
+ gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], &h, i >> 2);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a FACCH/F data as per section 4.2.
+ * \param[out] data Caller-allocated buffer for decoded FACCH (GSM_MACBLOCK_LEN).
+ * \param[in] bursts Buffer containing the symbols of 8 bursts,
+ * 8 * 2 * 58 == 928 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of bytes used in the output buffer; negative on error. */
+int gsm0503_tch_fr_facch_decode(uint8_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[8 * 114], cB[4 * 114];
+ int steal = 0;
+
+ /* FACCH decision: sum of 4 first hu(B) and 4 last hl(B) soft-bits */
+ for (unsigned int i = 0; i < 4; i++)
+ steal -= bursts[i * 116 + 58]; /* hu(B) */
+ for (unsigned int i = 4; i < 8; i++)
+ steal -= bursts[i * 116 + 57]; /* hl(B) */
+ if (steal <= 0)
+ return -1;
+
+ /* 4.2.5 Mapping on a Burst:
+ * - hu(B)=1 the even numbered bits in the first 4 bursts and
+ * - hl(B)=1 the odd numbered bits of the last 4 bursts are stolen. */
+ for (unsigned int i = 0; i < 8; i++)
+ gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], NULL, i >> 2);
+
+ /* 4.2.4 Interleaving: as specified for the TCH/FS in subclause 3.1.3 */
+ gsm0503_tch_fr_deinterleave(&cB[0], &iB[0]);
+
+ /* 4.2.1-3 as specified for the SACCH in 4.1.1-3 */
+ if (_xcch_decode_cB(&data[0], &cB[0], n_errors, n_bits_total) != 0)
+ return -1;
+
+ return GSM_MACBLOCK_LEN;
+}
+
+/*! Perform channel encoding of a FACCH/H data as per section 4.3.
+ * \param[out] bursts Caller-allocated buffer for symbols of 6 bursts,
+ * 6 * 2 * 58 == 696 bits total.
+ * \param[in] data FACCH MAC block to be encoded (GSM_MACBLOCK_LEN).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_hr_facch_encode(ubit_t *bursts, const uint8_t *data)
+{
+ ubit_t iB[8 * 114], cB[4 * 114];
+ const ubit_t h = 1;
+
+ /* 4.3.1-3 as specified for the SACCH in 4.1.1-3 */
+ _xcch_encode_cB(&cB[0], &data[0]);
+
+ /* 4.3.4 Interleaving */
+ gsm0503_tch_fr_interleave(&cB[0], &iB[0]);
+
+ /* 4.3.5 Mapping on a Burst:
+ * - hu(B)=1 the even numbered bits of the first 2 bursts,
+ * - hu(B)=1 & hl(B)=1 all bits of the middle 2 bursts and
+ * - hl(B)=1 the odd numbered bits of the last 2 bursts are stolen. */
+ for (unsigned int i = 0; i < 6; i++)
+ gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], &h, i >> 2);
+ for (unsigned int i = 2; i < 4; i++)
+ gsm0503_tch_burst_map(&iB[i * 114 + 456], &bursts[i * 116], &h, 1);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a FACCH/H data as per section 4.3.
+ * \param[out] data Caller-allocated buffer for decoded FACCH (GSM_MACBLOCK_LEN).
+ * \param[in] bursts Buffer containing the symbols of 6 bursts,
+ * 6 * 2 * 58 == 696 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of bytes used in the output buffer; negative on error. */
+int gsm0503_tch_hr_facch_decode(uint8_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[8 * 114], cB[4 * 114];
+ int steal = 0;
+
+ /* FACCH decision: sum of 4 first hu(B) and 4 last hl(B) soft-bits */
+ for (unsigned int i = 0; i < 4; i++)
+ steal -= bursts[i * 116 + 58]; /* hu(B) */
+ for (unsigned int i = 2; i < 6; i++)
+ steal -= bursts[i * 116 + 57]; /* hl(B) */
+ if (steal <= 0)
+ return -1;
+
+ /* 4.3.5 Mapping on a Burst:
+ * - hu(B)=1 the even numbered bits of the first 2 bursts,
+ * - hu(B)=1 & hl(B)=1 all bits of the middle 2 bursts and
+ * - hl(B)=1 the odd numbered bits of the last 2 bursts are stolen. */
+ for (unsigned int i = 0; i < 6; i++)
+ gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], NULL, i >> 2);
+ for (unsigned int i = 2; i < 4; i++)
+ gsm0503_tch_burst_unmap(&iB[i * 114 + 456], &bursts[i * 116], NULL, 1);
+
+ /* 4.3.4 Interleaving */
+ gsm0503_tch_fr_deinterleave(&cB[0], &iB[0]);
+
+ /* 4.3.1-3 as specified for the SACCH in 4.1.1-3 */
+ if (_xcch_decode_cB(&data[0], &cB[0], n_errors, n_bits_total) != 0)
+ return -1;
+
+ return GSM_MACBLOCK_LEN;
+}
+
/*! @} */
diff --git a/src/coding/gsm0503_interleaving.c b/src/coding/gsm0503_interleaving.c
index d5008d07..570d65aa 100644
--- a/src/coding/gsm0503_interleaving.c
+++ b/src/coding/gsm0503_interleaving.c
@@ -16,10 +16,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
@@ -682,4 +678,56 @@ void gsm0503_tch_hr_interleave(const ubit_t *cB, ubit_t *iB)
}
}
+/* 3GPP TS 45.003 Section 3.3.4
+ * The coded bits are reordered and interleaved according to the following rule:
+ * i(B,j) = c(n,k) for k = 0,1,...,455
+ * n = 0,1,...,N,N + 1,...
+ * B = B0 +4n + (k mod 19) + (k div 114)
+ * j = (k mod 19) + 19 (k mod 6)
+ *
+ * The result of the interleaving is a distribution of the reordered 114
+ * bit of a given data block, n = N, over 19 blocks, 6 bits equally
+ * distributed in each block, in a diagonal way over consecutive blocks.
+ *
+ * Or in other words the interleaving is a distribution of the encoded,
+ * reordered 456 bits from four given input data blocks, which taken
+ * together give n = N, over 22 bursts, 6 bits equally distributed in
+ * the first and 22 nd bursts, 12 bits distributed in the second and 21
+ * st bursts, 18 bits distributed in the third and 20 th bursts and 24
+ * bits distributed in the other 16 bursts.
+ *
+ * The block of coded data is interleaved "diagonal", where a new block
+ * of coded data starts with every fourth burst and is distributed over
+ * 22 bursts.
+ *
+ * Also used for TCH/F4.8, TCH/H4.8, and TCH/H2.4 and TCH/F14.4 */
+void gsm0503_tch_f96_interleave(const ubit_t *cB, ubit_t *iB)
+{
+ int j, k, B;
+
+ for (k = 0; k < 456; k++) {
+ /* upper bound for B: 4*n + 18 + 4 = 4*n + 22 */
+ B = /* B0 + 4n + */ (k % 19) + (k / 114);
+ /* upper bound for j: 18 + 19*5 = 113 */
+ j = (k % 19) + 19 * (k % 6);
+ /* upper iB index: 4*n+23*114-1 */
+ iB[B * 114 + j] = cB[k];
+ }
+}
+
+void gsm0503_tch_f96_deinterleave(sbit_t *cB, const sbit_t *iB)
+{
+ int j, k, B;
+
+ for (k = 0; k < 456; k++) {
+ /* upper bound for B: 4*n + 18 + 4 = 4*n + 22 */
+ B = /* B0 + 4n + */ (k % 19) + (k / 114);
+ /* upper bound for j: 18 + 19*5 = 113 */
+ j = (k % 19) + 19 * (k % 6);
+ /* upper iB index: 4*n+23*114-1 */
+ cB[k] = iB[B * 114 + j];
+ }
+}
+
+
/*! @} */
diff --git a/src/coding/gsm0503_mapping.c b/src/coding/gsm0503_mapping.c
index f7532eb2..04acfd07 100644
--- a/src/coding/gsm0503_mapping.c
+++ b/src/coding/gsm0503_mapping.c
@@ -15,10 +15,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
diff --git a/src/coding/gsm0503_parity.c b/src/coding/gsm0503_parity.c
index a8daacc7..ef71d351 100644
--- a/src/coding/gsm0503_parity.c
+++ b/src/coding/gsm0503_parity.c
@@ -15,10 +15,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
diff --git a/src/coding/gsm0503_tables.c b/src/coding/gsm0503_tables.c
index df0abeed..25ea2fa3 100644
--- a/src/coding/gsm0503_tables.c
+++ b/src/coding/gsm0503_tables.c
@@ -15,10 +15,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
diff --git a/src/coding/libosmocoding.map b/src/coding/libosmocoding.map
index 325b6d80..0444690e 100644
--- a/src/coding/libosmocoding.map
+++ b/src/coding/libosmocoding.map
@@ -76,6 +76,8 @@ gsm0503_xcch_deinterleave;
gsm0503_xcch_interleave;
gsm0503_tch_fr_deinterleave;
gsm0503_tch_fr_interleave;
+gsm0503_tch_f96_deinterleave;
+gsm0503_tch_f96_interleave;
gsm0503_tch_hr_deinterleave;
gsm0503_tch_hr_interleave;
gsm0503_mcs1_ul_deinterleave;
@@ -105,6 +107,7 @@ gsm0503_tch_fr_encode;
gsm0503_tch_fr_decode;
gsm0503_tch_hr_encode;
gsm0503_tch_hr_decode;
+gsm0503_tch_hr_decode2;
gsm0503_tch_afs_encode;
gsm0503_tch_afs_decode;
gsm0503_tch_afs_decode_dtx;
@@ -123,6 +126,26 @@ gsm0503_amr_dtx_frame_names;
gsm0503_amr_dtx_frame_name;
gsm0503_detect_afs_dtx_frame;
gsm0503_detect_ahs_dtx_frame;
+gsm0503_detect_afs_dtx_frame2;
+gsm0503_detect_ahs_dtx_frame2;
+
+gsm0503_tch_fr96_encode;
+gsm0503_tch_fr96_decode;
+gsm0503_tch_fr48_encode;
+gsm0503_tch_fr48_decode;
+gsm0503_tch_hr48_encode;
+gsm0503_tch_hr48_decode;
+gsm0503_tch_fr24_encode;
+gsm0503_tch_fr24_decode;
+gsm0503_tch_hr24_encode;
+gsm0503_tch_hr24_decode;
+gsm0503_tch_fr144_encode;
+gsm0503_tch_fr144_decode;
+
+gsm0503_tch_fr_facch_encode;
+gsm0503_tch_fr_facch_decode;
+gsm0503_tch_hr_facch_encode;
+gsm0503_tch_hr_facch_decode;
local: *;
};
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
new file mode 100644
index 00000000..2efebd8d
--- /dev/null
+++ b/src/core/Makefile.am
@@ -0,0 +1,166 @@
+# 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=21:0:0
+
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) $(LIBSCTP_CFLAGS) $(LIBMNL_CFLAGS) $(URING_CFLAGS)
+
+if ENABLE_PSEUDOTALLOC
+AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
+endif
+
+lib_LTLIBRARIES = libosmocore.la
+
+libosmocore_la_LIBADD = \
+ $(BACKTRACE_LIB) \
+ $(TALLOC_LIBS) \
+ $(LIBRARY_RT) \
+ $(PTHREAD_LIBS) \
+ $(LIBSCTP_LIBS) \
+ $(URING_LIBS) \
+ $(NULL)
+
+libosmocore_la_SOURCES = \
+ application.c \
+ backtrace.c \
+ base64.c \
+ bits.c \
+ bitvec.c \
+ bitcomp.c \
+ context.c \
+ conv.c \
+ conv_acc.c \
+ conv_acc_generic.c \
+ counter.c \
+ crc16.c \
+ crc8gen.c \
+ crc16gen.c \
+ crc32gen.c \
+ crc64gen.c \
+ exec.c \
+ fsm.c \
+ gsmtap_util.c \
+ isdnhdlc.c \
+ it_q.c \
+ logging.c \
+ logging_syslog.c \
+ logging_gsmtap.c \
+ loggingrb.c \
+ macaddr.c \
+ msgb.c \
+ netdev.c \
+ netns.c \
+ osmo_io.c \
+ osmo_io_poll.c \
+ panic.c \
+ prbs.c \
+ prim.c \
+ rate_ctr.c \
+ rbtree.c \
+ select.c \
+ sercomm.c \
+ signal.c \
+ sockaddr_str.c \
+ socket.c \
+ soft_uart.c \
+ stat_item.c \
+ stats.c \
+ stats_statsd.c \
+ stats_tcp.c \
+ strrb.c \
+ tdef.c \
+ thread.c \
+ time_cc.c \
+ timer.c \
+ timer_gettimeofday.c \
+ timer_clockgettime.c \
+ tun.c \
+ use_count.c \
+ utils.c \
+ write_queue.c \
+ probes.d \
+ $(NULL)
+
+if HAVE_SSSE3
+libosmocore_la_SOURCES += conv_acc_sse.c
+if HAVE_SSE4_1
+conv_acc_sse.lo : AM_CFLAGS += -mssse3 -msse4.1
+else
+conv_acc_sse.lo : AM_CFLAGS += -mssse3
+endif
+
+if HAVE_AVX2
+libosmocore_la_SOURCES += conv_acc_sse_avx.c
+if HAVE_SSE4_1
+conv_acc_sse_avx.lo : AM_CFLAGS += -mssse3 -mavx2 -msse4.1
+else
+conv_acc_sse_avx.lo : AM_CFLAGS += -mssse3 -mavx2
+endif
+endif
+endif
+
+if HAVE_NEON
+libosmocore_la_SOURCES += conv_acc_neon.c
+# conv_acc_neon.lo : AM_CFLAGS += -mfpu=neon no, could as well be vfp with neon
+endif
+
+BUILT_SOURCES = crc8gen.c crc16gen.c crc32gen.c crc64gen.c
+
+EXTRA_DIST = \
+ conv_acc_sse_impl.h \
+ conv_acc_neon_impl.h \
+ crcXXgen.c.tpl \
+ osmo_io_internal.h \
+ stat_item_internal.h \
+ libosmocore.map \
+ $(NULL)
+
+EXTRA_libosmocore_la_DEPENDENCIES = libosmocore.map
+
+libosmocore_la_LDFLAGS = \
+ $(LTLDFLAGS_OSMOCORE) \
+ -version-info \
+ $(LIBVERSION) \
+ -no-undefined
+
+if ENABLE_PLUGIN
+libosmocore_la_SOURCES += plugin.c
+libosmocore_la_LIBADD += $(LIBRARY_DLOPEN)
+endif
+
+if ENABLE_MSGFILE
+libosmocore_la_SOURCES += msgfile.c
+endif
+
+if ENABLE_SERIAL
+libosmocore_la_SOURCES += serial.c
+endif
+
+if ENABLE_SYSTEMD_LOGGING
+libosmocore_la_SOURCES += logging_systemd.c
+libosmocore_la_LIBADD += $(SYSTEMD_LIBS)
+endif
+
+if ENABLE_LIBMNL
+libosmocore_la_SOURCES += mnl.c
+libosmocore_la_LIBADD += $(LIBMNL_LIBS)
+endif
+
+if ENABLE_SYSTEMTAP
+probes.h: probes.d
+ $(DTRACE) -C -h -s $< -o $@
+
+probes.lo: probes.d
+ $(LIBTOOL) --mode=compile $(AM_V_lt) --tag=CC env CFLAGS="$(CFLAGS)" $(DTRACE) -C -G -s $< -o $@
+
+BUILT_SOURCES += probes.h probes.lo
+libosmocore_la_LIBADD += probes.lo
+endif
+
+if ENABLE_URING
+libosmocore_la_SOURCES += osmo_io_uring.c
+endif
+
+crc%gen.c: crcXXgen.c.tpl
+ $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
diff --git a/src/application.c b/src/core/application.c
index 7fd62808..f7e5816f 100644
--- a/src/application.c
+++ b/src/core/application.c
@@ -18,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \mainpage libosmocore Documentation
diff --git a/src/backtrace.c b/src/core/backtrace.c
index a18bde02..60bd2381 100644
--- a/src/backtrace.c
+++ b/src/core/backtrace.c
@@ -18,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
diff --git a/src/core/base64.c b/src/core/base64.c
new file mode 100644
index 00000000..0c161ceb
--- /dev/null
+++ b/src/core/base64.c
@@ -0,0 +1,195 @@
+/*
+ * RFC 1521 base64 encoding/decoding
+ *
+ * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *
+ * This file is part of mbed TLS (https://tls.mbed.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <osmocom/core/base64.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+
+static const unsigned char base64_enc_map[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+ 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '+', '/'
+};
+
+static const unsigned char base64_dec_map[128] = {
+ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 62, 127, 127, 127, 63, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 127, 127,
+ 127, 64, 127, 127, 127, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 127, 127, 127, 127, 127, 127, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 127, 127, 127, 127, 127
+};
+
+/*
+ * Encode a buffer into base64 format
+ */
+int osmo_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
+ const unsigned char *src, size_t slen)
+{
+ size_t i, n;
+ int C1, C2, C3;
+ unsigned char *p;
+
+ if (slen == 0) {
+ *olen = 0;
+ return 0;
+ }
+
+ n = (slen << 3) / 6;
+
+ switch ((slen << 3) - (n * 6)) {
+ case 2:
+ n += 3;
+ break;
+ case 4:
+ n += 2;
+ break;
+ default:
+ break;
+ }
+
+ if (dlen < n + 1) {
+ *olen = n + 1;
+ return -ENOBUFS;
+ }
+
+ n = (slen / 3) * 3;
+
+ for (i = 0, p = dst; i < n; i += 3) {
+ C1 = *src++;
+ C2 = *src++;
+ C3 = *src++;
+
+ *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
+ *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
+ *p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F];
+ *p++ = base64_enc_map[C3 & 0x3F];
+ }
+
+ if (i < slen) {
+ C1 = *src++;
+ C2 = ((i + 1) < slen) ? *src++ : 0;
+
+ *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
+ *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
+
+ if ((i + 1) < slen)
+ *p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F];
+ else
+ *p++ = '=';
+
+ *p++ = '=';
+ }
+
+ *olen = p - dst;
+ *p = 0;
+
+ return 0;
+}
+
+/*
+ * Decode a base64-formatted buffer
+ */
+int osmo_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
+ const unsigned char *src, size_t slen)
+{
+ size_t i, n;
+ uint32_t j, x;
+ unsigned char *p;
+
+ /* First pass: check for validity and get output length */
+ for (i = n = j = 0; i < slen; i++) {
+ /* Skip spaces before checking for EOL */
+ x = 0;
+ while (i < slen && src[i] == ' ') {
+ ++i;
+ ++x;
+ }
+
+ /* Spaces at end of buffer are OK */
+ if (i == slen)
+ break;
+
+ if ((slen - i) >= 2 && src[i] == '\r' && src[i + 1] == '\n')
+ continue;
+
+ if (src[i] == '\n')
+ continue;
+
+ /* Space inside a line is an error */
+ if (x != 0)
+ return -EINVAL;
+
+ if (src[i] == '=' && ++j > 2)
+ return -EINVAL;
+
+ if (src[i] > 127 || base64_dec_map[src[i]] == 127)
+ return -EINVAL;
+
+ if (base64_dec_map[src[i]] < 64 && j != 0)
+ return -EINVAL;
+
+ n++;
+ }
+
+ if (n == 0)
+ return 0;
+
+ n = ((n * 6) + 7) >> 3;
+ n -= j;
+
+ if (dst == NULL || dlen < n) {
+ *olen = n;
+ return -ENOBUFS;
+ }
+
+ for (j = 3, n = x = 0, p = dst; i > 0; i--, src++) {
+ if (*src == '\r' || *src == '\n' || *src == ' ')
+ continue;
+
+ j -= (base64_dec_map[*src] == 64);
+ x = (x << 6) | (base64_dec_map[*src] & 0x3F);
+
+ if (++n == 4) {
+ n = 0;
+ if (j > 0)
+ *p++ = (unsigned char)(x >> 16);
+ if (j > 1)
+ *p++ = (unsigned char)(x >> 8);
+ if (j > 2)
+ *p++ = (unsigned char)(x);
+ }
+ }
+
+ *olen = p - dst;
+
+ return 0;
+}
diff --git a/src/bitcomp.c b/src/core/bitcomp.c
index 6f2fb62b..5fb2cba2 100644
--- a/src/bitcomp.c
+++ b/src/core/bitcomp.c
@@ -18,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \defgroup bitcomp Bit compression
diff --git a/src/bits.c b/src/core/bits.c
index aa117531..3da7d9b9 100644
--- a/src/bits.c
+++ b/src/core/bits.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
@@ -185,14 +181,14 @@ int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits)
* \param[in] in input buffer of unpacked bits
* \param[in] in_ofs offset into input buffer
* \param[in] num_bits number of bits
- * \param[in] lsb_mode Encode bits in LSB orde instead of MSB
+ * \param[in] lsb_mode Encode bits in LSB order instead of MSB
* \returns length in bytes (max written offset of output buffer + 1)
*/
int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs,
const ubit_t *in, unsigned int in_ofs,
unsigned int num_bits, int lsb_mode)
{
- int i, op, bn;
+ unsigned int i, op, bn;
for (i=0; i<num_bits; i++) {
op = out_ofs + i;
bn = lsb_mode ? (op&7) : (7-(op&7));
@@ -210,14 +206,14 @@ int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs,
* \param[in] in input buffer of packed bits
* \param[in] in_ofs offset into input buffer
* \param[in] num_bits number of bits
- * \param[in] lsb_mode Encode bits in LSB orde instead of MSB
+ * \param[in] lsb_mode Encode bits in LSB order instead of MSB
* \returns length in bytes (max written offset of output buffer + 1)
*/
int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs,
const pbit_t *in, unsigned int in_ofs,
unsigned int num_bits, int lsb_mode)
{
- int i, ip, bn;
+ unsigned int i, ip, bn;
for (i=0; i<num_bits; i++) {
ip = in_ofs + i;
bn = lsb_mode ? (ip&7) : (7-(ip&7));
@@ -308,7 +304,7 @@ uint32_t osmo_revbytebits_8(uint8_t x)
*/
void osmo_revbytebits_buf(uint8_t *buf, int len)
{
- unsigned int i;
+ int i;
for (i = 0; i < len; i++)
buf[i] = flip_table[buf[i]];
diff --git a/src/bitvec.c b/src/core/bitvec.c
index 2b4e8c98..ac702b94 100644
--- a/src/bitvec.c
+++ b/src/core/bitvec.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup bitvec
@@ -201,7 +197,8 @@ int bitvec_get_bit_high(struct bitvec *bv)
* \return 0 on success; negative in case of error */
int bitvec_set_bits(struct bitvec *bv, const enum bit_value *bits, unsigned int count)
{
- int i, rc;
+ unsigned int i;
+ int rc;
for (i = 0; i < count; i++) {
rc = bitvec_set_bit(bv, bits[i]);
@@ -264,7 +261,7 @@ int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits)
* \return integer value retrieved from bit vector */
int bitvec_get_uint(struct bitvec *bv, unsigned int num_bits)
{
- int i;
+ unsigned int i;
unsigned int ui = 0;
for (i = 0; i < num_bits; i++) {
@@ -398,7 +395,7 @@ int bitvec_set_bytes(struct bitvec *bv, const uint8_t *bytes, unsigned int count
* \param[in] size Number of bytes in the vector
* \param[in] ctx Context from which to allocate
* \return pointer to allocated vector; NULL in case of error */
-struct bitvec *bitvec_alloc(unsigned int size, TALLOC_CTX *ctx)
+struct bitvec *bitvec_alloc(unsigned int size, void *ctx)
{
struct bitvec *bv = talloc(ctx, struct bitvec);
if (!bv)
@@ -472,19 +469,29 @@ int bitvec_unhex(struct bitvec *bv, const char *src)
* \param[in] bv The boolean vector to work on
* \param[in,out] read_index Where reading supposed to start in the vector
* \param[in] len How many bits to read from vector
- * \returns read bits or negative value on error
+ * \returns An integer made up of the bits read.
+ *
+ * In case of an error, errno is set to a non-zero value. Otherwise it holds 0.
*/
uint64_t bitvec_read_field(struct bitvec *bv, unsigned int *read_index, unsigned int len)
{
unsigned int i;
uint64_t ui = 0;
+
+ /* Prevent bitvec overrun due to incorrect index and/or length */
+ if (len && bytenum_from_bitnum(*read_index + len - 1) >= bv->data_len) {
+ errno = EOVERFLOW;
+ return 0;
+ }
+
bv->cur_bit = *read_index;
+ errno = 0;
for (i = 0; i < len; i++) {
- int bit = bitvec_get_bit_pos((const struct bitvec *)bv, bv->cur_bit);
- if (bit < 0)
- return bit;
- if (bit)
+ unsigned int bytenum = bytenum_from_bitnum(bv->cur_bit);
+ unsigned int bitnum = 7 - (bv->cur_bit % 8);
+
+ if (bv->data[bytenum] & (1 << bitnum))
ui |= ((uint64_t)1 << (len - i - 1));
bv->cur_bit++;
}
@@ -533,13 +540,11 @@ char bit_value_to_char(enum bit_value v)
*/
void bitvec_to_string_r(const struct bitvec *bv, char *str)
{
- unsigned i, pos = 0;
char *cur = str;
- for (i = 0; i < bv->cur_bit; i++) {
+ for (unsigned int i = 0; i < bv->cur_bit; i++) {
if (0 == i % 8)
*cur++ = ' ';
*cur++ = bit_value_to_char(bitvec_get_bit_pos(bv, i));
- pos++;
}
*cur = 0;
}
@@ -597,7 +602,7 @@ unsigned bitvec_rl(const struct bitvec *bv, bool b)
* \returns Number of consecutive bits of \p b in \p bv and cur_bit will
* \go to cur_bit + number of consecutive bit
*/
-unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits)
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, unsigned int max_bits)
{
unsigned i = 0;
unsigned j = 8;
diff --git a/src/context.c b/src/core/context.c
index bad012bd..a0b3a555 100644
--- a/src/context.c
+++ b/src/core/context.c
@@ -2,7 +2,7 @@
* talloc context handling.
*
* (C) 2019 by Harald Welte <laforge@gnumonks.org>
- * All Rights Reserverd.
+ * All Rights Reserved.
*
* SPDX-License-Identifier: GPL-2.0+
*
@@ -15,11 +15,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
*/
#include <string.h>
#include <errno.h>
@@ -44,7 +39,7 @@ int osmo_ctx_init(const char *id)
}
/* initialize osmo_ctx on main tread */
-static __attribute__((constructor)) void on_dso_load_ctx(void)
+static __attribute__((constructor(101))) void on_dso_load_ctx(void)
{
OSMO_ASSERT(osmo_ctx_init("main") == 0);
}
diff --git a/src/conv.c b/src/core/conv.c
index 06c4299b..8963018b 100644
--- a/src/conv.c
+++ b/src/core/conv.c
@@ -16,10 +16,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*! \addtogroup conv
@@ -67,7 +63,7 @@ osmo_conv_get_output_length(const struct osmo_conv_code *code, int len)
/* Count punctured bits */
if (code->puncture) {
- for (pbits=0; code->puncture[pbits] >= 0; pbits++);
+ for (pbits = 0; code->puncture[pbits] >= 0; pbits++) {}
out_len -= pbits;
}
@@ -85,7 +81,7 @@ osmo_conv_get_output_length(const struct osmo_conv_code *code, int len)
*/
void
osmo_conv_encode_init(struct osmo_conv_encoder *encoder,
- const struct osmo_conv_code *code)
+ const struct osmo_conv_code *code)
{
memset(encoder, 0x00, sizeof(struct osmo_conv_encoder));
OSMO_ASSERT(code != NULL);
@@ -94,12 +90,12 @@ osmo_conv_encode_init(struct osmo_conv_encoder *encoder,
void
osmo_conv_encode_load_state(struct osmo_conv_encoder *encoder,
- const ubit_t *input)
+ const ubit_t *input)
{
int i;
uint8_t state = 0;
- for (i=0; i<(encoder->code->K-1); i++)
+ for (i = 0; i < (encoder->code->K - 1); i++)
state = (state << 1) | input[i];
encoder->state = state;
@@ -107,15 +103,14 @@ osmo_conv_encode_load_state(struct osmo_conv_encoder *encoder,
static inline int
_conv_encode_do_output(struct osmo_conv_encoder *encoder,
- uint8_t out, ubit_t *output)
+ uint8_t out, ubit_t *output)
{
const struct osmo_conv_code *code = encoder->code;
int o_idx = 0;
int j;
if (code->puncture) {
- for (j=0; j<code->N; j++)
- {
+ for (j = 0; j < code->N; j++) {
int bit_no = code->N - j - 1;
int r_idx = encoder->i_idx * code->N + j;
@@ -125,8 +120,7 @@ _conv_encode_do_output(struct osmo_conv_encoder *encoder,
output[o_idx++] = (out >> bit_no) & 1;
}
} else {
- for (j=0; j<code->N; j++)
- {
+ for (j = 0; j < code->N; j++) {
int bit_no = code->N - j - 1;
output[o_idx++] = (out >> bit_no) & 1;
}
@@ -137,7 +131,7 @@ _conv_encode_do_output(struct osmo_conv_encoder *encoder,
int
osmo_conv_encode_raw(struct osmo_conv_encoder *encoder,
- const ubit_t *input, ubit_t *output, int n)
+ const ubit_t *input, ubit_t *output, int n)
{
const struct osmo_conv_code *code = encoder->code;
uint8_t state;
@@ -147,11 +141,11 @@ osmo_conv_encode_raw(struct osmo_conv_encoder *encoder,
o_idx = 0;
state = encoder->state;
- for (i=0; i<n; i++) {
+ for (i = 0; i < n; i++) {
int bit = input[i];
uint8_t out;
- out = code->next_output[state][bit];
+ out = code->next_output[state][bit];
state = code->next_state[state][bit];
o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]);
@@ -165,8 +159,7 @@ osmo_conv_encode_raw(struct osmo_conv_encoder *encoder,
}
int
-osmo_conv_encode_flush(struct osmo_conv_encoder *encoder,
- ubit_t *output)
+osmo_conv_encode_flush(struct osmo_conv_encoder *encoder, ubit_t *output)
{
const struct osmo_conv_code *code = encoder->code;
uint8_t state;
@@ -179,14 +172,14 @@ osmo_conv_encode_flush(struct osmo_conv_encoder *encoder,
o_idx = 0;
state = encoder->state;
- for (i=0; i<n; i++) {
+ for (i = 0; i < n; i++) {
uint8_t out;
if (code->next_term_output) {
- out = code->next_term_output[state];
+ out = code->next_term_output[state];
state = code->next_term_state[state];
} else {
- out = code->next_output[state][0];
+ out = code->next_output[state][0];
state = code->next_state[state][0];
}
@@ -212,7 +205,7 @@ osmo_conv_encode_flush(struct osmo_conv_encoder *encoder,
*/
int
osmo_conv_encode(const struct osmo_conv_code *code,
- const ubit_t *input, ubit_t *output)
+ const ubit_t *input, ubit_t *output)
{
struct osmo_conv_encoder encoder;
int l;
@@ -242,17 +235,18 @@ osmo_conv_encode(const struct osmo_conv_code *code,
/* Forward declaration for accerlated decoding with certain codes */
int
osmo_conv_decode_acc(const struct osmo_conv_code *code,
- const sbit_t *input, ubit_t *output);
+ const sbit_t *input, ubit_t *output);
void
osmo_conv_decode_init(struct osmo_conv_decoder *decoder,
- const struct osmo_conv_code *code, int len, int start_state)
+ const struct osmo_conv_code *code, int len,
+ int start_state)
{
int n_states;
/* Init */
if (len <= 0)
- len = code->len;
+ len = code->len;
n_states = 1 << (code->K - 1);
@@ -263,7 +257,7 @@ osmo_conv_decode_init(struct osmo_conv_decoder *decoder,
decoder->len = len;
/* Allocate arrays */
- decoder->ae = malloc(sizeof(unsigned int) * n_states);
+ decoder->ae = malloc(sizeof(unsigned int) * n_states);
decoder->ae_next = malloc(sizeof(unsigned int) * n_states);
decoder->state_history = malloc(sizeof(uint8_t) * n_states * (len + decoder->code->K - 1));
@@ -287,7 +281,7 @@ osmo_conv_decode_reset(struct osmo_conv_decoder *decoder, int start_state)
memset(decoder->ae, 0x00, sizeof(unsigned int) * decoder->n_states);
} else {
/* Fixed start state */
- for (i=0; i<decoder->n_states; i++) {
+ for (i = 0; i < decoder->n_states; i++) {
decoder->ae[i] = (i == start_state) ? 0 : MAX_AE;
}
}
@@ -304,12 +298,12 @@ osmo_conv_decode_rewind(struct osmo_conv_decoder *decoder)
decoder->p_idx = 0;
/* Initial error normalize (remove constant) */
- for (i=0; i<decoder->n_states; i++) {
+ for (i = 0; i < decoder->n_states; i++) {
if (decoder->ae[i] < min_ae)
min_ae = decoder->ae[i];
}
- for (i=0; i<decoder->n_states; i++)
+ for (i = 0; i < decoder->n_states; i++)
decoder->ae[i] -= min_ae;
}
@@ -325,7 +319,7 @@ osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder)
int
osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
- const sbit_t *input, int n)
+ const sbit_t *input, int n)
{
const struct osmo_conv_code *code = decoder->code;
@@ -342,27 +336,25 @@ osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
/* Prepare */
n_states = decoder->n_states;
- ae = decoder->ae;
+ ae = decoder->ae;
ae_next = decoder->ae_next;
state_history = &decoder->state_history[n_states * decoder->o_idx];
- in_sym = alloca(sizeof(sbit_t) * code->N);
+ in_sym = alloca(sizeof(sbit_t) * code->N);
i_idx = 0;
p_idx = decoder->p_idx;
/* Scan the treillis */
- for (i=0; i<n; i++)
- {
+ for (i = 0; i < n; i++) {
/* Reset next accumulated error */
- for (s=0; s<n_states; s++) {
+ for (s = 0; s < n_states; s++)
ae_next[s] = MAX_AE;
- }
/* Get input */
if (code->puncture) {
/* Hard way ... */
- for (j=0; j<code->N; j++) {
+ for (j = 0; j < code->N; j++) {
int idx = ((decoder->o_idx + i) * code->N) + j;
if (idx == code->puncture[p_idx]) {
in_sym[j] = 0; /* Undefined */
@@ -379,23 +371,21 @@ osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
}
/* Scan all state */
- for (s=0; s<n_states; s++)
- {
+ for (s = 0; s < n_states; s++) {
/* Scan possible input bits */
- for (b=0; b<2; b++)
- {
+ for (b = 0; b < 2; b++) {
int nae, ov, e;
uint8_t m;
/* Next output and state */
- uint8_t out = code->next_output[s][b];
+ uint8_t out = code->next_output[s][b];
uint8_t state = code->next_state[s][b];
/* New error for this path */
nae = ae[s]; /* start from last error */
m = 1 << (code->N - 1); /* mask for 'out' bit selection */
- for (j=0; j<code->N; j++) {
+ for (j = 0; j < code->N; j++) {
int is = (int)in_sym[j];
if (is) {
ov = (out & m) ? -127 : 127; /* sbit_t value for it */
@@ -425,8 +415,7 @@ osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
}
int
-osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
- const sbit_t *input)
+osmo_conv_decode_flush(struct osmo_conv_decoder *decoder, const sbit_t *input)
{
const struct osmo_conv_code *code = decoder->code;
@@ -443,27 +432,25 @@ osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
/* Prepare */
n_states = decoder->n_states;
- ae = decoder->ae;
+ ae = decoder->ae;
ae_next = decoder->ae_next;
state_history = &decoder->state_history[n_states * decoder->o_idx];
- in_sym = alloca(sizeof(sbit_t) * code->N);
+ in_sym = alloca(sizeof(sbit_t) * code->N);
i_idx = 0;
p_idx = decoder->p_idx;
/* Scan the treillis */
- for (i=0; i<code->K-1; i++)
- {
+ for (i = 0; i < code->K - 1; i++) {
/* Reset next accumulated error */
- for (s=0; s<n_states; s++) {
+ for (s = 0; s < n_states; s++)
ae_next[s] = MAX_AE;
- }
/* Get input */
if (code->puncture) {
/* Hard way ... */
- for (j=0; j<code->N; j++) {
+ for (j = 0; j < code->N; j++) {
int idx = ((decoder->o_idx + i) * code->N) + j;
if (idx == code->puncture[p_idx]) {
in_sym[j] = 0; /* Undefined */
@@ -480,8 +467,7 @@ osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
}
/* Scan all state */
- for (s=0; s<n_states; s++)
- {
+ for (s = 0; s < n_states; s++) {
int nae, ov, e;
uint8_t m;
@@ -490,10 +476,10 @@ osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
uint8_t state;
if (code->next_term_output) {
- out = code->next_term_output[s];
+ out = code->next_term_output[s];
state = code->next_term_state[s];
} else {
- out = code->next_output[s][0];
+ out = code->next_output[s][0];
state = code->next_state[s][0];
}
@@ -501,7 +487,7 @@ osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
nae = ae[s]; /* start from last error */
m = 1 << (code->N - 1); /* mask for 'out' bit selection */
- for (j=0; j<code->N; j++) {
+ for (j = 0; j < code->N; j++) {
int is = (int)in_sym[j];
if (is) {
ov = (out & m) ? -127 : 127; /* sbit_t value for it */
@@ -530,38 +516,86 @@ osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
}
int
-osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
- ubit_t *output, int has_flush, int end_state)
+osmo_conv_decode_get_best_end_state(struct osmo_conv_decoder *decoder)
{
const struct osmo_conv_code *code = decoder->code;
- int min_ae;
- uint8_t min_state, cur_state;
- int i, s, n;
+ int min_ae, min_state;
+ int s;
- uint8_t *sh_ptr;
+ /* If flushed, we _know_ the end state */
+ if (code->term == CONV_TERM_FLUSH)
+ return 0;
- /* End state ? */
- if (end_state < 0) {
- /* Find state with least error */
- min_ae = MAX_AE;
- min_state = 0xff;
+ /* Search init */
+ min_state = -1;
+ min_ae = MAX_AE;
- for (s=0; s<decoder->n_states; s++)
- {
+ /* If tail biting, we search for the minimum path metric that
+ * gives a circular traceback (i.e. start_state == end_state */
+ if (code->term == CONV_TERM_TAIL_BITING) {
+ int t, n, i;
+ uint8_t *sh_ptr;
+
+ for (s = 0; s < decoder->n_states; s++) {
+ /* Check if that state traces back to itself */
+ n = decoder->o_idx;
+ sh_ptr = &decoder->state_history[decoder->n_states * (n-1)];
+ t = s;
+
+ for (i = n - 1; i >= 0; i--) {
+ t = sh_ptr[t];
+ sh_ptr -= decoder->n_states;
+ }
+
+ if (s != t)
+ continue;
+
+ /* If it does, consider it */
if (decoder->ae[s] < min_ae) {
min_ae = decoder->ae[s];
min_state = s;
}
}
- if (min_state == 0xff)
- return -1;
- } else {
- min_state = (uint8_t) end_state;
- min_ae = decoder->ae[end_state];
+ if (min_ae < MAX_AE)
+ return min_state;
+ }
+
+ /* Finally, just the lowest path metric */
+ for (s = 0; s < decoder->n_states; s++) {
+ /* Is it smaller ? */
+ if (decoder->ae[s] < min_ae) {
+ min_ae = decoder->ae[s];
+ min_state = s;
+ }
}
+ return min_state;
+}
+
+int
+osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
+ ubit_t *output, int has_flush, int end_state)
+{
+ const struct osmo_conv_code *code = decoder->code;
+
+ int min_ae;
+ uint8_t min_state, cur_state;
+ int i, n;
+
+ uint8_t *sh_ptr;
+
+ /* End state ? */
+ if (end_state < 0)
+ end_state = osmo_conv_decode_get_best_end_state(decoder);
+
+ if (end_state < 0)
+ return -1;
+
+ min_state = (uint8_t) end_state;
+ min_ae = decoder->ae[end_state];
+
/* Traceback */
cur_state = min_state;
@@ -571,16 +605,15 @@ osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
/* No output for the K-1 termination input bits */
if (has_flush) {
- for (i=0; i<code->K-1; i++) {
+ for (i = 0; i < code->K - 1; i++) {
cur_state = sh_ptr[cur_state];
sh_ptr -= decoder->n_states;
}
n -= code->K - 1;
}
- /* Generate output backward */
- for (i=n-1; i>=0; i--)
- {
+ /* Generate output backward */
+ for (i = n - 1; i >= 0; i--) {
min_state = cur_state;
cur_state = sh_ptr[cur_state];
@@ -602,12 +635,12 @@ osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
*
* This is an all-in-one function, taking care of
* \ref osmo_conv_decode_init, \ref osmo_conv_decode_scan,
- * \ref osmo_conv_decode_flush, \ref osmo_conv_decode_get_output and
- * \ref osmo_conv_decode_deinit.
+ * \ref osmo_conv_decode_flush, \ref osmo_conv_decode_get_best_end_state,
+ * \ref osmo_conv_decode_get_output and \ref osmo_conv_decode_deinit.
*/
int
osmo_conv_decode(const struct osmo_conv_code *code,
- const sbit_t *input, ubit_t *output)
+ const sbit_t *input, ubit_t *output)
{
struct osmo_conv_decoder decoder;
int rv, l;
@@ -630,7 +663,7 @@ osmo_conv_decode(const struct osmo_conv_code *code,
rv = osmo_conv_decode_get_output(&decoder, output,
code->term == CONV_TERM_FLUSH, /* has_flush */
- code->term == CONV_TERM_FLUSH ? 0 : -1 /* end_state */
+ -1 /* end_state */
);
osmo_conv_decode_deinit(&decoder);
diff --git a/src/conv_acc.c b/src/core/conv_acc.c
index 0f6f7ca2..4bd3b076 100644
--- a/src/conv_acc.c
+++ b/src/core/conv_acc.c
@@ -16,10 +16,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdlib.h>
@@ -487,10 +483,27 @@ static void _traceback_rec(struct vdecoder *dec,
*/
static int traceback(struct vdecoder *dec, uint8_t *out, int term, int len)
{
- int i, sum, max = -1;
- unsigned path, state = 0;
+ int i, j, sum, max = -1;
+ unsigned path, state = 0, state_scan;
+
+ if (term == CONV_TERM_TAIL_BITING) {
+ for (i = 0; i < dec->trellis.num_states; i++) {
+ state_scan = i;
+ for (j = len - 1; j >= 0; j--) {
+ path = dec->paths[j][state_scan] + 1;
+ state_scan = vstate_lshift(state_scan, dec->k, path);
+ }
+ if (state_scan != i)
+ continue;
+ sum = dec->trellis.sums[i];
+ if (sum > max) {
+ max = sum;
+ state = i;
+ }
+ }
+ }
- if (term != CONV_TERM_FLUSH) {
+ if ((max < 0) && (term != CONV_TERM_FLUSH)) {
for (i = 0; i < dec->trellis.num_states; i++) {
sum = dec->trellis.sums[i];
if (sum > max) {
diff --git a/src/conv_acc_generic.c b/src/core/conv_acc_generic.c
index 28876738..2257e6a9 100644
--- a/src/conv_acc_generic.c
+++ b/src/core/conv_acc_generic.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdlib.h>
diff --git a/src/conv_acc_neon.c b/src/core/conv_acc_neon.c
index 72449468..fb180e3d 100644
--- a/src/conv_acc_neon.c
+++ b/src/core/conv_acc_neon.c
@@ -18,10 +18,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdlib.h>
diff --git a/src/conv_acc_neon_impl.h b/src/core/conv_acc_neon_impl.h
index 4471127e..8a78c75b 100644
--- a/src/conv_acc_neon_impl.h
+++ b/src/core/conv_acc_neon_impl.h
@@ -18,10 +18,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Some distributions (notably Alpine Linux) for some strange reason
diff --git a/src/conv_acc_sse.c b/src/core/conv_acc_sse.c
index 63d8722a..513ab052 100644
--- a/src/conv_acc_sse.c
+++ b/src/core/conv_acc_sse.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
diff --git a/src/conv_acc_sse_avx.c b/src/core/conv_acc_sse_avx.c
index 5ac3c163..82b4fa62 100644
--- a/src/conv_acc_sse_avx.c
+++ b/src/core/conv_acc_sse_avx.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
diff --git a/src/conv_acc_sse_impl.h b/src/core/conv_acc_sse_impl.h
index 9ebbfe9c..807dbe5e 100644
--- a/src/conv_acc_sse_impl.h
+++ b/src/core/conv_acc_sse_impl.h
@@ -18,10 +18,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Some distributions (notably Alpine Linux) for some strange reason
diff --git a/src/counter.c b/src/core/counter.c
index 0fa31661..dace15f3 100644
--- a/src/counter.c
+++ b/src/core/counter.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <string.h>
@@ -79,7 +75,7 @@ int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *),
/*! Counts the registered counter
* \returns amount of counters */
-int osmo_counters_count()
+int osmo_counters_count(void)
{
return llist_count(&counters);
}
diff --git a/src/crc16.c b/src/core/crc16.c
index 29dace2e..29dace2e 100644
--- a/src/crc16.c
+++ b/src/core/crc16.c
diff --git a/src/crcXXgen.c.tpl b/src/core/crcXXgen.c.tpl
index 74e6d521..154291cc 100644
--- a/src/crcXXgen.c.tpl
+++ b/src/core/crcXXgen.c.tpl
@@ -16,10 +16,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*! \addtogroup crc
diff --git a/src/exec.c b/src/core/exec.c
index 2a03ba80..2e33788e 100644
--- a/src/exec.c
+++ b/src/core/exec.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include "config.h"
@@ -78,7 +74,7 @@ static bool str_in_list(const char **list, const char *key)
*
* Constraints: Keys up to a maximum length of 255 characters are supported.
*
- * \oaram[out] out caller-allocated array of pointers for the generated output
+ * \param[out] out caller-allocated array of pointers for the generated output
* \param[in] out_len size of out (number of pointers)
* \param[in] in input environment (NULL-terminated list of pointers like **environ)
* \param[in] whitelist whitelist of permitted keys in environment (like **environ)
@@ -135,7 +131,7 @@ int osmo_environment_filter(char **out, size_t out_len, char **in, const char **
* Constraints: If the same key exists in 'out' and 'in', duplicate keys are
* generated. It is a simple append, without any duplicate checks.
*
- * \oaram[out] out caller-allocated array of pointers for the generated output
+ * \param[out] out caller-allocated array of pointers for the generated output
* \param[in] out_len size of out (number of pointers)
* \param[in] in input environment (NULL-terminated list of pointers like **environ)
* \returns number of entries filled in 'out'; negative on error */
@@ -217,10 +213,18 @@ int osmo_system_nowait2(const char *command, const char **env_whitelist, char **
int rc;
if (user) {
+ if (getpw_buflen == -1) /* Value was indeterminate */
+ getpw_buflen = 16384; /* Should be more than enough */
char buf[getpw_buflen];
- getpwnam_r(user, &_pw, buf, sizeof(buf), &pw);
- if (!pw)
+ rc = getpwnam_r(user, &_pw, buf, sizeof(buf), &pw);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "getpwnam_r(\"%s\") failed: %s\n", user, strerror(-rc));
+ return rc;
+ }
+ if (!pw) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "getpwnam_r(\"%s\"): user not found!\n", user);
return -EINVAL;
+ }
}
rc = fork();
@@ -268,6 +272,9 @@ int osmo_system_nowait2(const char *command, const char **env_whitelist, char **
return -EIO;
} else {
/* we are in the parent */
+ if (rc == -1)
+ LOGP(DLGLOBAL, LOGL_ERROR, "fork() error executing command '%s': %s\n",
+ command, strerror(errno));
return rc;
}
}
diff --git a/src/fsm.c b/src/core/fsm.c
index f1dbb412..9333cac5 100644
--- a/src/fsm.c
+++ b/src/core/fsm.c
@@ -14,11 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
*/
#include <errno.h>
@@ -450,8 +445,8 @@ struct osmo_fsm_inst *osmo_fsm_inst_alloc(struct osmo_fsm *fsm, void *ctx, void
osmo_timer_setup(&fi->timer, fsm_tmr_cb, fi);
if (osmo_fsm_inst_update_id(fi, id) < 0) {
- fsm_free_or_steal(fi);
- return NULL;
+ fsm_free_or_steal(fi);
+ return NULL;
}
INIT_LLIST_HEAD(&fi->proc.children);
@@ -581,7 +576,7 @@ void osmo_fsm_inst_free(struct osmo_fsm_inst *fi)
* \param[in] event Event integer value
* \returns string rendering of the event
*/
-const char *osmo_fsm_event_name(struct osmo_fsm *fsm, uint32_t event)
+const char *osmo_fsm_event_name(const struct osmo_fsm *fsm, uint32_t event)
{
static __thread char buf[32];
if (!fsm->event_names) {
@@ -595,7 +590,7 @@ const char *osmo_fsm_event_name(struct osmo_fsm *fsm, uint32_t event)
* \param[in] fi FSM instance
* \returns string rendering of the FSM identity
*/
-const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi)
+const char *osmo_fsm_inst_name(const struct osmo_fsm_inst *fi)
{
if (!fi)
return "NULL";
@@ -611,7 +606,7 @@ const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi)
* \param[in] state FSM state number
* \returns string rendering of the FSM state
*/
-const char *osmo_fsm_state_name(struct osmo_fsm *fsm, uint32_t state)
+const char *osmo_fsm_state_name(const struct osmo_fsm *fsm, uint32_t state)
{
static __thread char buf[32];
if (state >= fsm->num_states) {
@@ -690,8 +685,11 @@ static int state_chg(struct osmo_fsm_inst *fi, uint32_t new_state,
if (!keep_timer
|| (keep_timer && !osmo_timer_pending(&fi->timer))) {
fi->T = T;
- if (timeout_ms)
- osmo_timer_schedule(&fi->timer, timeout_ms / 1000, timeout_ms % 1000);
+ if (timeout_ms) {
+ osmo_timer_schedule(&fi->timer,
+ /* seconds */ (timeout_ms / 1000),
+ /* microseconds */ (timeout_ms % 1000) * 1000);
+ }
}
/* Call 'onenter' last, user might terminate FSM from there */
diff --git a/src/gsmtap_util.c b/src/core/gsmtap_util.c
index 336e1d03..b64c7b05 100644
--- a/src/gsmtap_util.c
+++ b/src/core/gsmtap_util.c
@@ -17,13 +17,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
-#include "../config.h"
+#include "config.h"
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/logging.h>
@@ -33,6 +29,7 @@
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/byteswap.h>
+#include <osmocom/core/utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/rsl.h>
@@ -50,6 +47,43 @@
*
* \file gsmtap_util.c */
+/*! one gsmtap instance
+ * Until gsmtap_inst_fd() is removed from the API at some point in the future, we have to keep the first member as
+ * 'int' and the second as 'struct osmo_wqueue' (this effectively makes sure that the struct member wq.bfd.fd maintains
+ * the same memory offset from the start of the struct) to ensure that inlined static 'instances' of gsmtap_inst_fd() in
+ * old binaries keep working the way they used to even with gsmtap_inst objects obtained from newer versions of libosmocore */
+struct gsmtap_inst {
+ int osmo_io_mode; /*!< Indicates whether or not to use Osmo IO mode for message output (thus enabling use of tx queues).
+ * This field member may not be changed or moved (backwards compatibility) */
+ struct osmo_wqueue wq; /*!< the wait queue. This field member may not be changed or moved (backwards compatibility) */
+
+ struct osmo_io_fd *out; /*!< Used when osmo_io_mode is nonzero */
+ int sink_fd;
+};
+
+struct _gsmtap_inst_legacy {
+ int ofd_wq_mode;
+ struct osmo_wqueue wq;
+ struct osmo_fd sink_ofd;
+};
+osmo_static_assert(offsetof(struct gsmtap_inst, wq) == offsetof(struct _gsmtap_inst_legacy, wq),
+ gsmtap_inst_new_wq_offset_equals_legacy_wq_offset);
+
+/*! Deprecated, use gsmtap_inst_fd2() instead
+ * \param[in] gti GSMTAP instance
+ * \returns file descriptor of GSMTAP instance */
+int gsmtap_inst_fd(struct gsmtap_inst *gti)
+{
+ return gsmtap_inst_fd2(gti);
+}
+
+/*! obtain the file descriptor associated with a gsmtap instance
+ * \param[in] gti GSMTAP instance
+ * \returns file descriptor of GSMTAP instance */
+int gsmtap_inst_fd2(const struct gsmtap_inst *gti)
+{
+ return gti->wq.bfd.fd;
+}
/*! convert RSL channel number to GSMTAP channel type
* \param[in] rsl_chantype RSL channel type
@@ -63,12 +97,14 @@ uint8_t chantype_rsl2gsmtap2(uint8_t rsl_chantype, uint8_t link_id, bool user_pl
switch (rsl_chantype) {
case RSL_CHAN_Bm_ACCHs:
+ case RSL_CHAN_OSMO_VAMOS_Bm_ACCHs:
if (user_plane)
ret = GSMTAP_CHANNEL_VOICE_F;
else
ret = GSMTAP_CHANNEL_FACCH_F;
break;
case RSL_CHAN_Lm_ACCHs:
+ case RSL_CHAN_OSMO_VAMOS_Lm_ACCHs:
if (user_plane)
ret = GSMTAP_CHANNEL_VOICE_H;
else
@@ -233,13 +269,14 @@ struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type,
#include <sys/socket.h>
#include <netinet/in.h>
+#include <osmocom/core/osmo_io.h>
-/*! Create a new (sending) GSMTAP source socket
+/*! Create a new (sending) GSMTAP source socket
* \param[in] host host name or IP address in string format
* \param[in] port UDP port number in host byte order
* \return file descriptor of the new socket
*
- * Opens a GSMTAP source (sending) socket, conncet it to host/port and
+ * Opens a GSMTAP source (sending) socket, connect it to host/port and
* return resulting fd. If \a host is NULL, the destination address
* will be localhost. If \a port is 0, the default \ref
* GSMTAP_UDP_PORT will be used.
@@ -255,6 +292,30 @@ int gsmtap_source_init_fd(const char *host, uint16_t port)
OSMO_SOCK_F_CONNECT);
}
+/*! Create a new (sending) GSMTAP source socket
+ * \param[in] local_host local host name or IP address in string format
+ * \param[in] local_port local UDP port number in host byte order
+ * \param[in] rem_host remote host name or IP address in string format
+ * \param[in] rem_port remote UDP port number in host byte order
+ * \return file descriptor of the new socket
+ *
+ * Opens a GSMTAP source (sending) socket, connect it to remote host/port,
+ * bind to local host/port and return resulting fd.
+ * If \a local_host is NULL, the default address is used.
+ * If \a local_port is 0, than random unused port will be selected by OS.
+ * If \a rem_host is NULL, the destination address will be localhost.
+ * If \a rem_port is 0, the default \ref GSMTAP_UDP_PORT will be used.
+ */
+int gsmtap_source_init_fd2(const char *local_host, uint16_t local_port, const char *rem_host, uint16_t rem_port)
+{
+ if (!local_host)
+ return gsmtap_source_init_fd(rem_host, rem_port);
+
+ return osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, local_host, local_port,
+ rem_host ? rem_host : "localhost", rem_port ? rem_port : GSMTAP_UDP_PORT,
+ OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
+}
+
/*! Add a local sink to an existing GSMTAP source and return fd
* \param[in] gsmtap_fd file descriptor of the gsmtap socket
* \returns file descriptor of locally bound receive socket
@@ -302,13 +363,13 @@ int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg)
if (!gti)
return -ENODEV;
- if (gti->ofd_wq_mode)
- return osmo_wqueue_enqueue(&gti->wq, msg);
+ if (gti->osmo_io_mode)
+ return osmo_iofd_write_msgb(gti->out, msg);
else {
/* try immediate send and return error if any */
int rc;
- rc = write(gsmtap_inst_fd(gti), msg->data, msg->len);
+ rc = write(gsmtap_inst_fd2(gti), msg->data, msg->len);
if (rc < 0) {
return rc;
} else if (rc >= msg->len) {
@@ -321,6 +382,20 @@ int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg)
}
}
+/*! Send a \ref msgb through a GSMTAP source; free the message even if tx queue full.
+ * \param[in] gti GSMTAP instance
+ * \param[in] msg message buffer; always freed, caller must not reference it later.
+ * \return 0 in case of success; negative in case of error
+ */
+int gsmtap_sendmsg_free(struct gsmtap_inst *gti, struct msgb *msg)
+{
+ int rc;
+ rc = gsmtap_sendmsg(gti, msg);
+ if (rc < 0)
+ msgb_free(msg);
+ return rc;
+}
+
/*! send an arbitrary type through GSMTAP.
* See \ref gsmtap_makemsg_ex for arguments
*/
@@ -358,40 +433,6 @@ int gsmtap_send(struct gsmtap_inst *gti, uint16_t arfcn, uint8_t ts,
signal_dbm, snr, data, len);
}
-/* Callback from select layer if we can write to the socket */
-static int gsmtap_wq_w_cb(struct osmo_fd *ofd, struct msgb *msg)
-{
- int rc;
-
- rc = write(ofd->fd, msg->data, msg->len);
- if (rc < 0) {
- return rc;
- }
- if (rc != msg->len) {
- return -EIO;
- }
-
- return 0;
-}
-
-/* Callback from select layer if we can read from the sink socket */
-static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags)
-{
- int rc;
- uint8_t buf[4096];
-
- if (!(flags & OSMO_FD_READ))
- return 0;
-
- rc = read(fd->fd, buf, sizeof(buf));
- if (rc < 0) {
- return rc;
- }
- /* simply discard any data arriving on the socket */
-
- return 0;
-}
-
/*! Add a local sink to an existing GSMTAP source and return fd
* \param[in] gti existing GSMTAP source
* \returns file descriptor of locally bound receive socket
@@ -409,30 +450,63 @@ static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags)
*/
int gsmtap_source_add_sink(struct gsmtap_inst *gti)
{
- int fd, rc;
+ return gti->sink_fd = gsmtap_source_add_sink_fd(gsmtap_inst_fd2(gti));
+}
- fd = gsmtap_source_add_sink_fd(gsmtap_inst_fd(gti));
- if (fd < 0)
- return fd;
+/* Registered in Osmo IO as a no-op to set the write callback. */
+static void gsmtap_ops_noop_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
+{
+}
- if (gti->ofd_wq_mode) {
- struct osmo_fd *sink_ofd;
+static struct osmo_io_ops gsmtap_ops = { .write_cb = gsmtap_ops_noop_cb };
- sink_ofd = &gti->sink_ofd;
- sink_ofd->fd = fd;
- sink_ofd->when = OSMO_FD_READ;
- sink_ofd->cb = gsmtap_sink_fd_cb;
+/*! Open GSMTAP source socket, connect and register osmo_fd
+ * \param[in] local_host IP address in string format
+ * \param[in] local_port UDP port number in host byte order
+ * \param[in] rem_host host name or IP address in string format
+ * \param[in] rem_port UDP port number in host byte order
+ * \param[in] ofd_wq_mode Register \ref osmo_wqueue (1) or not (0)
+ * \return callee-allocated \ref gsmtap_inst
+ *
+ * Open GSMTAP source (sending) socket, connect it to remote host/port,
+ * bind it local host/port,
+ * allocate 'struct gsmtap_inst' and optionally osmo_fd/osmo_wqueue
+ * registration.
+ */
+struct gsmtap_inst *gsmtap_source_init2(const char *local_host, uint16_t local_port,
+ const char *rem_host, uint16_t rem_port, int ofd_wq_mode)
+{
+ struct gsmtap_inst *gti;
+ int fd;
- rc = osmo_fd_register(sink_ofd);
- if (rc < 0) {
- close(fd);
- return rc;
- }
+ fd = gsmtap_source_init_fd2(local_host, local_port, rem_host, rem_port);
+ if (fd < 0)
+ return NULL;
+
+ gti = talloc_zero(NULL, struct gsmtap_inst);
+ gti->osmo_io_mode = ofd_wq_mode;
+ /* Still using the wq member for its 'fd' field only, since we are keeping it for now, anyways */
+ gti->wq.bfd.fd = fd;
+ gti->sink_fd = -1;
+
+ if (ofd_wq_mode) {
+ gti->out = osmo_iofd_setup(gti, gti->wq.bfd.fd, "gsmtap_inst.io_fd", OSMO_IO_FD_MODE_READ_WRITE, &gsmtap_ops, NULL);
+ if (gti->out == NULL)
+ goto err_cleanup;
+ if (osmo_iofd_register(gti->out, gti->wq.bfd.fd) < 0)
+ goto err_cleanup;
+
+ /* osmo write queue previously used was set up with value of 64 */
+ osmo_iofd_set_txqueue_max_length(gti->out, 64);
}
- return fd;
-}
+ return gti;
+err_cleanup:
+ talloc_free(gti);
+ close(fd);
+ return NULL;
+}
/*! Open GSMTAP source socket, connect and register osmo_fd
* \param[in] host host name or IP address in string format
@@ -447,31 +521,25 @@ int gsmtap_source_add_sink(struct gsmtap_inst *gti)
struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port,
int ofd_wq_mode)
{
- struct gsmtap_inst *gti;
- int fd, rc;
-
- fd = gsmtap_source_init_fd(host, port);
- if (fd < 0)
- return NULL;
+ return gsmtap_source_init2(NULL, 0, host, port, ofd_wq_mode);
+}
- gti = talloc_zero(NULL, struct gsmtap_inst);
- gti->ofd_wq_mode = ofd_wq_mode;
- gti->wq.bfd.fd = fd;
- gti->sink_ofd.fd = -1;
+void gsmtap_source_free(struct gsmtap_inst *gti)
+{
+ if (!gti)
+ return;
- if (ofd_wq_mode) {
- osmo_wqueue_init(&gti->wq, 64);
- gti->wq.write_cb = &gsmtap_wq_w_cb;
+ if (gti->osmo_io_mode) {
+ osmo_iofd_free(gti->out);
- rc = osmo_fd_register(&gti->wq.bfd);
- if (rc < 0) {
- talloc_free(gti);
- close(fd);
- return NULL;
+ if (gti->sink_fd != -1) {
+ close(gti->sink_fd);
+ gti->sink_fd = -1;
}
+
}
- return gti;
+ talloc_free(gti);
}
#endif /* HAVE_SYS_SOCKET_H */
@@ -512,6 +580,8 @@ const struct value_string gsmtap_type_names[] = {
{ GSMTAP_TYPE_TETRA_I1, "TETRA V+D" },
{ GSMTAP_TYPE_TETRA_I1_BURST, "TETRA bursts" },
{ GSMTAP_TYPE_WMX_BURST, "WiMAX burst" },
+ { GSMTAP_TYPE_GB_LLC, "GPRS Gb LLC" },
+ { GSMTAP_TYPE_GB_SNDCP, "GPRS Gb SNDCP" },
{ GSMTAP_TYPE_GMR1_UM, "GMR-1 air interfeace (MES-MS<->GTS)"},
{ GSMTAP_TYPE_UMTS_RLC_MAC, "UMTS RLC/MAC" },
{ GSMTAP_TYPE_UMTS_RRC, "UMTS RRC" },
@@ -520,6 +590,9 @@ const struct value_string gsmtap_type_names[] = {
{ GSMTAP_TYPE_LTE_MAC_FRAMED, "LTE MAC with context hdr" },
{ GSMTAP_TYPE_OSMOCORE_LOG, "libosmocore logging" },
{ GSMTAP_TYPE_QC_DIAG, "Qualcomm DIAG" },
+ { GSMTAP_TYPE_LTE_NAS, "LTE Non-Access Stratum" },
+ { GSMTAP_TYPE_E1T1, "E1/T1 lines" },
+ { GSMTAP_TYPE_GSM_RLP, "GSM Radio Link Protocol" },
{ 0, NULL }
};
diff --git a/src/isdnhdlc.c b/src/core/isdnhdlc.c
index 58b4a661..4ced5afd 100644
--- a/src/isdnhdlc.c
+++ b/src/core/isdnhdlc.c
@@ -18,10 +18,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <string.h>
@@ -101,13 +97,12 @@ check_frame(struct osmo_isdnhdlc_vars *hdlc)
When a new flag is found, the complete frame has been received
and the CRC is checked.
If a valid frame is found, the function returns the frame length
- excluding the CRC with the bit HDLC_END_OF_FRAME set.
+ excluding the CRC.
If the beginning of a valid frame is found, the function returns
the length.
If a framing error is found (too many 1s and not a flag) the function
- returns the length with the bit OSMO_HDLC_FRAMING_ERROR set.
- If a CRC error is found the function returns the length with the
- bit OSMO_HDLC_CRC_ERROR set.
+ returns -OSMO_HDLC_FRAMING_ERROR.
+ If a CRC error is found the function returns -OSMO_HDLC_CRC_ERROR.
If the frame length exceeds the destination buffer size, the function
returns the length with the bit OSMO_HDLC_LENGTH_ERROR set.
diff --git a/src/it_q.c b/src/core/it_q.c
index 1bb0e155..810dc903 100644
--- a/src/it_q.c
+++ b/src/core/it_q.c
@@ -14,11 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
*/
/*! \addtogroup it_q
@@ -37,7 +32,7 @@
* and call a queue-specific callback function.
*/
-#include "../config.h"
+#include "config.h"
#ifdef HAVE_SYS_EVENTFD_H
@@ -250,7 +245,7 @@ int _osmo_it_q_enqueue(struct osmo_it_q *queue, struct llist_head *item)
/*! Thread-safe de-queue from an inter-thread message queue.
* \param[in] queue Inter-thread queue from which to dequeue
- * \returns dequeued message buffer; NULL if none available
+ * \returns llist_head of dequeued message; NULL if none available
*/
struct llist_head *_osmo_it_q_dequeue(struct osmo_it_q *queue)
{
@@ -259,12 +254,9 @@ struct llist_head *_osmo_it_q_dequeue(struct osmo_it_q *queue)
pthread_mutex_lock(&queue->mutex);
- if (llist_empty(&queue->list))
- l = NULL;
- l = queue->list.next;
- OSMO_ASSERT(l);
- llist_del(l);
- queue->current_length--;
+ l = item_dequeue(&queue->list);
+ if (l != NULL)
+ queue->current_length--;
pthread_mutex_unlock(&queue->mutex);
diff --git a/src/core/libosmocore.map b/src/core/libosmocore.map
new file mode 100644
index 00000000..c5ab6e37
--- /dev/null
+++ b/src/core/libosmocore.map
@@ -0,0 +1,631 @@
+LIBOSMOCORE_1.0 {
+global:
+
+assert_loginfo;
+bit_value_to_char;
+bitvec_add_array;
+bitvec_alloc;
+bitvec_fill;
+bitvec_find_bit_pos;
+bitvec_free;
+bitvec_get_bit_high;
+bitvec_get_bit_pos;
+bitvec_get_bit_pos_high;
+bitvec_get_bytes;
+bitvec_get_int16_msb;
+bitvec_get_nth_set_bit;
+bitvec_get_uint;
+bitvec_pack;
+bitvec_read_field;
+bitvec_rl;
+bitvec_rl_curbit;
+bitvec_set_bit;
+bitvec_set_bit_pos;
+bitvec_set_bits;
+bitvec_set_bytes;
+bitvec_set_u64;
+bitvec_set_uint;
+bitvec_shiftl;
+bitvec_spare_padding;
+bitvec_to_string_r;
+bitvec_unhex;
+bitvec_unpack;
+bitvec_write_field;
+bitvec_zero;
+chantype_gsmtap2rsl;
+chantype_rsl2gsmtap;
+chantype_rsl2gsmtap2;
+get_string_value;
+get_value_string;
+get_value_string_or_null;
+gsmtap_gsm_channel_names;
+gsmtap_inst_fd;
+gsmtap_inst_fd2;
+gsmtap_makemsg;
+gsmtap_makemsg_ex;
+gsmtap_send;
+gsmtap_send_ex;
+gsmtap_sendmsg;
+gsmtap_sendmsg_free;
+gsmtap_source_add_sink;
+gsmtap_source_add_sink_fd;
+gsmtap_source_free;
+gsmtap_source_init;
+gsmtap_source_init2;
+gsmtap_source_init_fd;
+gsmtap_source_init_fd2;
+gsmtap_type_names;
+log_add_target;
+log_category_name;
+log_check_level;
+log_cache_enable;
+log_cache_update;
+log_del_target;
+log_enable_multithread;
+log_fini;
+log_init;
+log_level_str;
+loglevel_strs;
+logp;
+logp2;
+log_parse_category;
+log_parse_category_mask;
+log_parse_level;
+logp_stub;
+log_reset_context;
+log_set_all_filter;
+log_set_category_filter;
+log_set_context;
+log_set_log_level;
+log_set_print_category;
+log_set_print_category_hex;
+log_set_print_extended_timestamp;
+log_set_print_filename;
+log_set_print_filename2;
+log_set_print_filename_pos;
+log_set_print_level;
+log_set_print_tid;
+log_set_print_timestamp;
+log_set_use_color;
+log_target_create;
+log_target_create_file;
+log_target_create_file_stream;
+log_target_create_gsmtap;
+log_target_create_rb;
+log_target_create_stderr;
+log_target_create_syslog;
+log_target_create_systemd;
+log_target_destroy;
+log_target_file_reopen;
+log_target_file_switch_to_stream;
+log_target_file_switch_to_wqueue;
+log_target_find;
+log_target_rb_avail_size;
+log_target_rb_get;
+log_target_rb_used_size;
+log_target_systemd_set_raw;
+log_targets_reopen;
+log_tgt_mutex_lock_impl;
+log_tgt_mutex_unlock_impl;
+msgb_alloc;
+msgb_alloc_c;
+msgb_copy;
+msgb_copy_c;
+msgb_copy_resize;
+msgb_copy_resize_c;
+msgb_data;
+msgb_dequeue;
+msgb_enqueue;
+_msgb_eq;
+msgb_free;
+msgb_hexdump;
+msgb_hexdump_buf;
+msgb_hexdump_c;
+msgb_length;
+msgb_printf;
+msgb_reset;
+msgb_resize_area;
+msgb_set_talloc_ctx;
+msgb_talloc_ctx_init;
+osmo_base64_decode;
+osmo_base64_encode;
+osmo_bcd2char;
+osmo_bcd2str;
+osmo_bit_reversal;
+osmo_char2bcd;
+osmo_clock_gettime;
+osmo_clock_override_add;
+osmo_clock_override_enable;
+osmo_clock_override_gettimespec;
+osmo_close_all_fds_above;
+osmo_config_list_parse;
+osmo_constant_time_cmp;
+osmo_conv_decode;
+osmo_conv_decode_acc;
+osmo_conv_decode_deinit;
+osmo_conv_decode_flush;
+osmo_conv_decode_get_best_end_state;
+osmo_conv_decode_get_output;
+osmo_conv_decode_init;
+osmo_conv_decode_reset;
+osmo_conv_decode_rewind;
+osmo_conv_decode_scan;
+osmo_conv_encode;
+osmo_conv_encode_flush;
+osmo_conv_encode_init;
+osmo_conv_encode_load_state;
+osmo_conv_encode_raw;
+osmo_conv_get_input_length;
+osmo_conv_get_output_length;
+osmo_counter_alloc;
+osmo_counter_difference;
+osmo_counter_free;
+osmo_counter_get_by_name;
+osmo_counters_count;
+osmo_counters_for_each;
+osmo_crc16;
+osmo_crc16_ccitt;
+osmo_crc16_ccitt_table;
+osmo_crc16_table;
+osmo_crc16gen_check_bits;
+osmo_crc16gen_compute_bits;
+osmo_crc16gen_set_bits;
+osmo_crc32gen_check_bits;
+osmo_crc32gen_compute_bits;
+osmo_crc32gen_set_bits;
+osmo_crc64gen_check_bits;
+osmo_crc64gen_compute_bits;
+osmo_crc64gen_set_bits;
+osmo_crc8gen_check_bits;
+osmo_crc8gen_compute_bits;
+osmo_crc8gen_set_bits;
+osmo_ctx;
+osmo_ctx_init;
+osmo_daemonize;
+osmo_decode_big_endian;
+osmo_encode_big_endian;
+osmo_environment_append;
+osmo_environment_filter;
+osmo_environment_whitelist;
+osmo_escape_cstr_buf;
+osmo_escape_cstr_c;
+osmo_escape_str;
+osmo_escape_str_buf;
+osmo_escape_str_buf2;
+osmo_escape_str_buf3;
+osmo_escape_str_c;
+osmo_event_for_prim;
+osmo_fd_close;
+osmo_fd_disp_fds;
+osmo_fd_fill_fds;
+osmo_fd_get_by_fd;
+osmo_fd_is_registered;
+osmo_fd_register;
+osmo_fd_setup;
+osmo_fd_unregister;
+osmo_fd_update_when;
+osmo_float_str_to_int;
+osmo_fsm_event_name;
+osmo_fsm_find_by_name;
+osmo_fsm_inst_alloc;
+osmo_fsm_inst_alloc_child;
+_osmo_fsm_inst_broadcast_children;
+osmo_fsm_inst_change_parent;
+_osmo_fsm_inst_dispatch;
+osmo_fsm_inst_find_by_id;
+osmo_fsm_inst_find_by_name;
+osmo_fsm_inst_free;
+osmo_fsm_inst_name;
+_osmo_fsm_inst_state_chg;
+_osmo_fsm_inst_state_chg_keep_or_start_timer;
+_osmo_fsm_inst_state_chg_keep_or_start_timer_ms;
+_osmo_fsm_inst_state_chg_keep_timer;
+_osmo_fsm_inst_state_chg_ms;
+_osmo_fsm_inst_term;
+_osmo_fsm_inst_term_children;
+osmo_fsm_inst_unlink_parent;
+osmo_fsm_inst_update_id;
+osmo_fsm_inst_update_id_f;
+osmo_fsm_inst_update_id_f_sanitize;
+osmo_fsm_log_addr;
+osmo_fsm_log_timeouts;
+osmo_fsm_register;
+osmo_fsm_set_dealloc_ctx;
+osmo_fsm_state_name;
+osmo_fsm_term_cause_names;
+osmo_fsm_term_safely;
+osmo_fsm_unregister;
+osmo_generate_backtrace;
+osmo_get_macaddr;
+osmo_gettid;
+osmo_gettimeofday;
+osmo_gettimeofday_override;
+osmo_gettimeofday_override_add;
+osmo_gettimeofday_override_time;
+osmo_g_fsms;
+osmo_hexdump;
+osmo_hexdump_buf;
+osmo_hexdump_c;
+osmo_hexdump_nospc;
+osmo_hexdump_nospc_c;
+osmo_hexparse;
+osmo_identifier_sanitize_buf;
+osmo_identifier_valid;
+osmo_init_ignore_signals;
+osmo_init_logging;
+osmo_init_logging2;
+osmo_int_to_float_str_buf;
+osmo_int_to_float_str_c;
+osmo_io_backend_names;
+osmo_iofd_close;
+osmo_iofd_free;
+osmo_iofd_get_data;
+osmo_iofd_get_ioops;
+osmo_iofd_get_fd;
+osmo_iofd_get_name;
+osmo_iofd_set_name;
+osmo_iofd_get_priv_nr;
+osmo_iofd_init;
+osmo_iofd_mode_names;
+osmo_iofd_ops;
+osmo_iofd_register;
+osmo_iofd_sendto_msgb;
+osmo_iofd_sctp_send_msgb;
+osmo_iofd_sendmsg_msgb;
+osmo_iofd_set_alloc_info;
+osmo_iofd_set_cmsg_size;
+osmo_iofd_set_data;
+osmo_iofd_set_ioops;
+osmo_iofd_set_priv_nr;
+osmo_iofd_set_txqueue_max_length;
+osmo_iofd_setup;
+osmo_iofd_txqueue_clear;
+osmo_iofd_txqueue_len;
+osmo_iofd_unregister;
+osmo_iofd_uring_init;
+osmo_iofd_notify_connected;
+osmo_iofd_write_msgb;
+osmo_ip_str_type;
+osmo_isdnhdlc_decode;
+osmo_isdnhdlc_encode;
+osmo_isdnhdlc_out_init;
+osmo_isdnhdlc_rcv_init;
+osmo_is_hexstr;
+osmo_isqrt32;
+osmo_it_q_alloc;
+osmo_it_q_by_name;
+_osmo_it_q_dequeue;
+osmo_it_q_destroy;
+_osmo_it_q_enqueue;
+osmo_it_q_flush;
+osmo_log_backtrace;
+osmo_log_info;
+osmo_log_target_list;
+osmo_luhn;
+osmo_macaddr_parse;
+osmo_mnl_destroy;
+osmo_mnl_init;
+osmo_multiaddr_ip_and_port_snprintf;
+osmo_netdev_add_addr;
+osmo_netdev_add_route;
+osmo_netdev_alloc;
+osmo_netdev_free;
+osmo_netdev_get_dev_name;
+osmo_netdev_get_ifindex;
+osmo_netdev_get_name;
+osmo_netdev_get_netns_name;
+osmo_netdev_get_priv_data;
+osmo_netdev_ifupdown;
+osmo_netdev_is_registered;
+osmo_netdev_register;
+osmo_netdev_set_dev_name_chg_cb;
+osmo_netdev_set_ifindex;
+osmo_netdev_set_ifupdown_ind_cb;
+osmo_netdev_set_mtu_chg_cb;
+osmo_netdev_set_netns_name;
+osmo_netdev_set_priv_data;
+osmo_netdev_unregister;
+osmo_netns_open_fd;
+osmo_netns_switch_enter;
+osmo_netns_switch_exit;
+osmo_nibble_shift_left_unal;
+osmo_nibble_shift_right;
+osmo_panic;
+osmo_pbit2ubit;
+osmo_pbit2ubit_ext;
+osmo_plugin_load_all;
+osmo_prbs11;
+osmo_prbs15;
+osmo_prbs7;
+osmo_prbs9;
+osmo_prbs_get_ubit;
+osmo_prbs_get_ubits;
+osmo_prbs_state_init;
+osmo_prim_op_names;
+osmo_print_n;
+osmo_quote_cstr_buf;
+osmo_quote_cstr_c;
+osmo_quote_str;
+osmo_quote_str_buf;
+osmo_quote_str_buf2;
+osmo_quote_str_buf3;
+osmo_quote_str_c;
+osmo_revbytebits_32;
+osmo_revbytebits_8;
+osmo_revbytebits_buf;
+osmo_sbit2ubit;
+osmo_select_init;
+osmo_select_main;
+osmo_select_main_ctx;
+osmo_select_shutdown_done;
+osmo_select_shutdown_request;
+osmo_select_shutdown_requested;
+osmo_separated_identifiers_valid;
+osmo_sercomm_change_speed;
+osmo_sercomm_drv_pull;
+osmo_sercomm_drv_rx_char;
+osmo_sercomm_init;
+osmo_sercomm_initialized;
+osmo_sercomm_register_rx_cb;
+osmo_sercomm_sendmsg;
+osmo_sercomm_tx_queue_depth;
+osmo_serial_clear_custom_baudrate;
+osmo_serial_init;
+osmo_serial_set_baudrate;
+osmo_serial_set_custom_baudrate;
+osmo_serial_speed_t;
+osmo_set_panic_handler;
+osmo_signal_dispatch;
+osmo_signalfd_setup;
+osmo_signal_register_handler;
+osmo_signal_talloc_ctx_init;
+osmo_signal_unregister_handler;
+osmo_sockaddr_cmp;
+osmo_sockaddr_from_octets;
+osmo_sockaddr_from_str_and_uint;
+osmo_sockaddr_in_to_str_and_uint;
+osmo_sockaddr_is_any;
+osmo_sockaddr_is_local;
+osmo_sockaddr_local_ip;
+osmo_sockaddr_netmask_to_prefixlen;
+osmo_sockaddr_ntop;
+osmo_sockaddr_port;
+osmo_sockaddr_set_port;
+osmo_sockaddr_str_cmp;
+osmo_sockaddr_str_from_32;
+osmo_sockaddr_str_from_32h;
+osmo_sockaddr_str_from_32n;
+osmo_sockaddr_str_from_in6_addr;
+osmo_sockaddr_str_from_in_addr;
+osmo_sockaddr_str_from_sockaddr;
+osmo_sockaddr_str_from_sockaddr_in;
+osmo_sockaddr_str_from_sockaddr_in6;
+osmo_sockaddr_str_from_osa;
+osmo_sockaddr_str_from_str;
+osmo_sockaddr_str_from_str2;
+osmo_sockaddr_str_is_nonzero;
+osmo_sockaddr_str_is_set;
+osmo_sockaddr_str_to_32;
+osmo_sockaddr_str_to_32h;
+osmo_sockaddr_str_to_32n;
+osmo_sockaddr_str_to_in6_addr;
+osmo_sockaddr_str_to_in_addr;
+osmo_sockaddr_str_to_sockaddr;
+osmo_sockaddr_str_to_sockaddr_in;
+osmo_sockaddr_str_to_sockaddr_in6;
+osmo_sockaddr_str_to_osa;
+osmo_sockaddr_to_octets;
+osmo_sockaddr_to_str;
+osmo_sockaddr_to_str_and_uint;
+osmo_sockaddr_to_str_buf;
+osmo_sockaddr_to_str_buf2;
+osmo_sockaddr_to_str_c;
+osmo_sock_get_ip_and_port;
+osmo_sock_get_local_ip;
+osmo_sock_get_local_ip_port;
+osmo_sock_get_name;
+osmo_sock_get_name2;
+osmo_sock_get_name2_c;
+osmo_sock_get_name_buf;
+osmo_sock_get_remote_ip;
+osmo_sock_get_remote_ip_port;
+osmo_sock_init;
+osmo_sock_init2;
+osmo_sock_init2_multiaddr;
+osmo_sock_init2_multiaddr2;
+osmo_sock_init2_ofd;
+osmo_sock_init_ofd;
+osmo_sock_init_osa;
+osmo_sock_init_osa_ofd;
+osmo_sock_init_sa;
+osmo_sock_local_ip;
+osmo_sock_mcast_all_set;
+osmo_sock_mcast_iface_set;
+osmo_sock_mcast_loop_set;
+osmo_sock_mcast_subscribe;
+osmo_sock_mcast_ttl_set;
+osmo_sock_multiaddr_add_local_addr;
+osmo_sock_multiaddr_del_local_addr;
+osmo_sock_multiaddr_get_ip_and_port;
+osmo_sock_multiaddr_get_name_buf;
+osmo_sock_sctp_get_peer_addr_info;
+osmo_sock_set_dscp;
+osmo_sock_set_priority;
+osmo_sock_unix_init;
+osmo_sock_unix_init_ofd;
+osmo_soft_uart_default_cfg;
+osmo_soft_uart_alloc;
+osmo_soft_uart_free;
+osmo_soft_uart_configure;
+osmo_soft_uart_get_name;
+osmo_soft_uart_set_name;
+osmo_soft_uart_set_rx;
+osmo_soft_uart_set_tx;
+osmo_soft_uart_rx_ubits;
+osmo_soft_uart_tx_ubits;
+osmo_soft_uart_get_status;
+osmo_soft_uart_set_status;
+osmo_soft_uart_set_status_line;
+osmo_soft_uart_flush_rx;
+osmo_stat_item_dec;
+osmo_stat_item_flush;
+osmo_stat_item_for_each_group;
+osmo_stat_item_for_each_item;
+osmo_stat_item_get_by_name;
+osmo_stat_item_get_desc;
+osmo_stat_item_get_group_by_name_idx;
+osmo_stat_item_get_group_by_name_idxname;
+osmo_stat_item_get_last;
+osmo_stat_item_group_alloc;
+osmo_stat_item_group_free;
+osmo_stat_item_group_get_item;
+osmo_stat_item_group_reset;
+osmo_stat_item_group_set_name;
+osmo_stat_item_inc;
+osmo_stat_item_init;
+osmo_stat_item_reset;
+osmo_stat_item_set;
+osmo_stats_config;
+osmo_stats_init;
+osmo_stats_report;
+osmo_stats_reporter_alloc;
+osmo_stats_reporter_create_log;
+osmo_stats_reporter_create_statsd;
+osmo_stats_reporter_disable;
+osmo_stats_reporter_enable;
+osmo_stats_reporter_find;
+osmo_stats_reporter_free;
+osmo_stats_reporter_list;
+osmo_stats_reporter_send;
+osmo_stats_reporter_send_buffer;
+osmo_stats_reporter_set_flush_period;
+osmo_stats_reporter_set_local_addr;
+osmo_stats_reporter_set_max_class;
+osmo_stats_reporter_set_mtu;
+osmo_stats_reporter_set_name_prefix;
+osmo_stats_reporter_set_remote_addr;
+osmo_stats_reporter_set_remote_port;
+osmo_stats_reporter_udp_close;
+osmo_stats_reporter_udp_open;
+osmo_stats_set_interval;
+osmo_stats_tcp_osmo_fd_register;
+osmo_stats_tcp_osmo_fd_unregister;
+osmo_stats_tcp_set_interval;
+osmo_stderr_target;
+osmo_str2bcd;
+osmo_str2lower;
+osmo_str2upper;
+osmo_strlcpy;
+osmo_strnchr;
+osmo_strrb_add;
+osmo_strrb_create;
+osmo_strrb_elements;
+osmo_strrb_get_nth;
+_osmo_strrb_is_bufindex_valid;
+osmo_strrb_is_empty;
+osmo_strbuf_drop_tail;
+osmo_strbuf_added_tail;
+osmo_str_startswith;
+osmo_str_to_int;
+osmo_str_to_int64;
+osmo_str_tolower;
+osmo_str_tolower_buf;
+osmo_str_tolower_c;
+osmo_str_toupper;
+osmo_str_toupper_buf;
+osmo_str_toupper_c;
+osmo_system_nowait;
+osmo_system_nowait2;
+osmo_t4_encode;
+osmo_talloc_replace_string_fmt;
+osmo_tcp_stats_config;
+_osmo_tdef_fsm_inst_state_chg;
+osmo_tdef_get;
+osmo_tdef_get_entry;
+osmo_tdef_get_state_timeout;
+osmo_tdef_range_str_buf;
+osmo_tdef_set;
+osmo_tdefs_reset;
+osmo_tdef_unit_names;
+osmo_tdef_val_in_range;
+osmo_time_cc_cleanup;
+osmo_time_cc_init;
+osmo_time_cc_set_flag;
+osmo_timer_add;
+osmo_timer_del;
+osmo_timerfd_disable;
+osmo_timerfd_schedule;
+osmo_timerfd_setup;
+osmo_timer_pending;
+osmo_timer_remaining;
+osmo_timers_check;
+osmo_timer_schedule;
+osmo_timer_setup;
+osmo_timers_nearest;
+osmo_timers_nearest_ms;
+osmo_timers_prepare;
+osmo_timers_update;
+osmo_tundev_alloc;
+osmo_tundev_close;
+osmo_tundev_free;
+osmo_tundev_get_dev_name;
+osmo_tundev_get_name;
+osmo_tundev_get_netdev;
+osmo_tundev_get_netns_name;
+osmo_tundev_get_priv_data;
+osmo_tundev_is_open;
+osmo_tundev_open;
+osmo_tundev_send;
+osmo_tundev_set_data_ind_cb;
+osmo_tundev_set_dev_name;
+osmo_tundev_set_netns_name;
+osmo_tundev_set_priv_data;
+osmo_ubit2pbit;
+osmo_ubit2pbit_ext;
+osmo_ubit2sbit;
+osmo_ubit_dump;
+osmo_ubit_dump_buf;
+osmo_use_count_by;
+osmo_use_count_find;
+osmo_use_count_free;
+_osmo_use_count_get_put;
+osmo_use_count_make_static_entries;
+osmo_use_count_name_buf;
+osmo_use_count_to_str_buf;
+osmo_use_count_to_str_c;
+osmo_use_count_total;
+osmo_vlogp;
+osmo_wqueue_bfd_cb;
+osmo_wqueue_clear;
+osmo_wqueue_enqueue;
+osmo_wqueue_enqueue_quiet;
+osmo_wqueue_set_maxlen;
+osmo_wqueue_init;
+rate_ctr_add;
+rate_ctr_difference;
+rate_ctr_for_each_counter;
+rate_ctr_for_each_group;
+rate_ctr_get_by_name;
+rate_ctr_get_group_by_name_idx;
+rate_ctr_group_alloc;
+rate_ctr_group_free;
+rate_ctr_group_get_ctr;
+rate_ctr_group_reset;
+rate_ctr_group_set_name;
+rate_ctr_init;
+rate_ctr_reset;
+rb_erase;
+rb_first;
+rb_insert_color;
+rb_last;
+rb_next;
+rb_prev;
+rb_replace_node;
+sercomm_drv_lock;
+sercomm_drv_unlock;
+tall_ctr_ctx; /* deprecated */
+tall_log_ctx;
+tall_msgb_ctx; /* deprecated */
+
+local: *;
+};
diff --git a/src/logging.c b/src/core/logging.c
index 4517afcd..e1721241 100644
--- a/src/logging.c
+++ b/src/core/logging.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup logging
@@ -29,12 +25,13 @@
*
* \file logging.c */
-#include "../config.h"
+#include "config.h"
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <unistd.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
@@ -57,6 +54,9 @@
#include <time.h>
#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
@@ -65,12 +65,18 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/thread.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/gsmtap_util.h>
#include <osmocom/vty/logging.h> /* for LOGGING_STR. */
/* maximum length of the log string of a single log event (typically line) */
#define MAX_LOG_SIZE 4096
+/* maximum number of log statements we queue in file/stderr target write queue */
+#define LOG_WQUEUE_LEN 156
+
osmo_static_assert(_LOG_CTX_COUNT <= ARRAY_SIZE(((struct log_context*)NULL)->ctx),
enum_logging_ctx_items_fit_in_struct_log_context);
osmo_static_assert(_LOG_FLT_COUNT <= ARRAY_SIZE(((struct log_target*)NULL)->filter_data),
@@ -87,6 +93,80 @@ LLIST_HEAD(osmo_log_target_list);
static __thread long int logging_tid;
#if (!EMBEDDED)
+/*! One global copy that contains the union of log levels for all targets
+* for all categories, used for quick lock free checks of log targets. */
+static volatile uint8_t *log_level_lookup_cache;
+
+/*! Updates cache for all targets for all categies, caller must hold osmo_log_tgt_mutex. */
+static void log_cache_update_all(void)
+{
+ struct log_target *tgt;
+ uint8_t tmp_en[osmo_log_info->num_cat];
+ uint8_t tmp_level[osmo_log_info->num_cat];
+
+ if (!log_level_lookup_cache)
+ return;
+
+ memset(tmp_en, 0, osmo_log_info->num_cat);
+ memset(tmp_level, UINT8_MAX, osmo_log_info->num_cat);
+
+ /* values can also decrease.. */
+ llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
+ for (int i = 0; i < osmo_log_info->num_cat; i++) {
+ struct log_category *cat = &tgt->categories[i];
+ tmp_en[i] = OSMO_MAX(tmp_en[i], cat->enabled);
+ tmp_level[i] = OSMO_MIN(tmp_level[i], cat->loglevel);
+ tmp_level[i] = tgt->loglevel ? OSMO_MIN(tmp_level[i], tgt->loglevel) : tmp_level[i];
+ }
+ }
+
+ for (int i = 0; i < osmo_log_info->num_cat; i++)
+ log_level_lookup_cache[i] = tmp_en[i] ? tmp_level[i] : UINT8_MAX;
+}
+
+/*! Updates single cache entry, caller must hold osmo_log_tgt_mutex.
+ *
+ * \param[in] mapped_subsys plain category index (after mapping)
+ * \param[in] enabled log category enabled?
+ * \param[in] level log level
+ */
+void log_cache_update(int mapped_subsys, uint8_t enabled, uint8_t level)
+{
+ struct log_target *tgt;
+ struct log_category tmp = { UINT8_MAX, 0 };
+
+ if (!log_level_lookup_cache)
+ return;
+
+ /* values can also decrease.. */
+ llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
+ struct log_category *cat = &tgt->categories[mapped_subsys];
+ tmp.enabled = OSMO_MAX(tmp.enabled, cat->enabled);
+ tmp.loglevel = OSMO_MIN(tmp.loglevel, cat->loglevel);
+ tmp.loglevel = tgt->loglevel ? OSMO_MIN(tmp.loglevel, tgt->loglevel) : tmp.loglevel;
+ }
+ tmp.enabled = OSMO_MAX(tmp.enabled, enabled);
+ tmp.loglevel = OSMO_MIN(tmp.loglevel, level);
+
+ log_level_lookup_cache[mapped_subsys] = tmp.enabled ? tmp.loglevel : UINT8_MAX;
+}
+
+/*! Queries log level cache.
+ *
+ * \param[in] mapped_subsys plain category index (after mapping)
+ * \param[in] level log level
+ * \returns true if logging should happen for at least one log target
+*/
+static bool log_cache_check(int mapped_subsys, int level)
+{
+ if (!log_level_lookup_cache) {
+ /* log-cache is not enabled, so we simply behave like we did before the cache */
+ return true;
+ }
+
+ return (level < log_level_lookup_cache[mapped_subsys]) ? false : true;
+}
+
/*! This mutex must be held while using osmo_log_target_list or any of its
log_targets in a multithread program. Prevents race conditions between threads
like producing unordered timestamps or VTY deleting a target while another
@@ -159,7 +239,7 @@ static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
.description = "LAPD in libosmogsm",
.loglevel = LOGL_NOTICE,
.enabled = 1,
- .color = "\033[38;5;21m",
+ .color = "\033[38;5;12m",
},
[INT2IDX(DLINP)] = {
.name = "DLINP",
@@ -289,6 +369,30 @@ static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
.enabled = 1, .loglevel = LOGL_NOTICE,
.color = "\033[38;5;63m",
},
+ [INT2IDX(DLIUUP)] = {
+ .name = "DLIUUP",
+ .description = "Iu UP layer",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;65m",
+ },
+ [INT2IDX(DLPFCP)] = {
+ .name = "DLPFCP",
+ .description = "libosmo-pfcp Packet Forwarding Control Protocol",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;51m",
+ },
+ [INT2IDX(DLCSN1)] = {
+ .name = "DLCSN1",
+ .description = "libosmo-csn1 Concrete Syntax Notation 1 codec",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;11m",
+ },
+ [INT2IDX(DLIO)] = {
+ .name = "DLIO",
+ .description = "libosmocore IO Subsystem",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;67m",
+ },
};
void assert_loginfo(const char *src)
@@ -396,6 +500,10 @@ void log_parse_category_mask(struct log_target* target, const char *_mask)
}
} while ((category_token = strtok(NULL, ":")));
+#if !defined(EMBEDDED)
+ log_cache_update_all();
+#endif
+
free(mask);
}
@@ -440,23 +548,34 @@ static const char *const_basename(const char *path)
return bn + 1;
}
-static void _output(struct log_target *target, unsigned int subsys,
- unsigned int level, const char *file, int line, int cont,
- const char *format, va_list ap)
+/*! main output formatting function for log lines.
+ * \param[out] buf caller-allocated output buffer for the generated string
+ * \param[in] buf_len number of bytes available in buf
+ * \param[in] target log target for which the string is to be formatted
+ * \param[in] subsys Log sub-system number
+ * \param[in] level Log level
+ * \param[in] file name of source code file generating the log
+ * \param[in] line line source code line number within 'file' generating the log
+ * \param[in] cont is this a continuation (true) or not (false)
+ * \param[in] format format string
+ * \param[in] ap variable argument list for format
+ * \returns number of bytes written to out */
+static int _output_buf(char *buf, int buf_len, struct log_target *target, unsigned int subsys,
+ unsigned int level, const char *file, int line, int cont,
+ const char *format, va_list ap)
{
- char buf[MAX_LOG_SIZE];
- int ret, len = 0, offset = 0, rem = sizeof(buf);
+ int ret;
const char *c_subsys = NULL;
+ struct osmo_strbuf sb = { .buf = buf, .pos = buf, .len = buf_len };
+
+ /* safety net in case of encountering errors and returning nothing */
+ buf[0] = '\0';
/* are we using color */
if (target->use_color) {
c_subsys = color(subsys);
- if (c_subsys) {
- ret = snprintf(buf + offset, rem, "%s", c_subsys);
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
- }
+ if (c_subsys)
+ OSMO_STRBUF_PRINTF(sb, "%s", c_subsys);
}
if (!cont) {
if (target->print_ext_timestamp) {
@@ -465,13 +584,10 @@ static void _output(struct log_target *target, unsigned int subsys,
struct timeval tv;
osmo_gettimeofday(&tv, NULL);
localtime_r(&tv.tv_sec, &tm);
- ret = snprintf(buf + offset, rem, "%04d%02d%02d%02d%02d%02d%03d ",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec,
- (int)(tv.tv_usec / 1000));
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ OSMO_STRBUF_PRINTF(sb, "%04d%02d%02d%02d%02d%02d%03d ",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ (int)(tv.tv_usec / 1000));
#endif
} else if (target->print_timestamp) {
time_t tm;
@@ -479,108 +595,95 @@ static void _output(struct log_target *target, unsigned int subsys,
goto err;
/* Get human-readable representation of time.
man ctime: we need at least 26 bytes in buf */
- if (rem < 26 || !ctime_r(&tm, buf + offset))
+ if (OSMO_STRBUF_REMAIN(sb) < 26 || !ctime_r(&tm, sb.pos))
goto err;
- ret = strlen(buf + offset);
+ ret = strnlen(sb.pos, 26);
if (ret <= 0)
goto err;
+ OSMO_STRBUF_ADDED_TAIL(sb, ret);
/* Get rid of useless final '\n' added by ctime_r. We want a space instead. */
- buf[offset + ret - 1] = ' ';
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ OSMO_STRBUF_DROP_TAIL(sb, 1);
+ OSMO_STRBUF_PRINTF(sb, " ");
}
if (target->print_tid) {
if (logging_tid == 0)
logging_tid = (long int)osmo_gettid();
- ret = snprintf(buf + offset, rem, "%ld ", logging_tid);
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
- }
- if (target->print_category) {
- ret = snprintf(buf + offset, rem, "%s%s%s%s ",
- target->use_color ? level_color(level) : "",
- log_category_name(subsys),
- target->use_color ? OSMO_LOGCOLOR_END : "",
- c_subsys ? c_subsys : "");
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
- }
- if (target->print_level) {
- ret = snprintf(buf + offset, rem, "%s%s%s%s ",
- target->use_color ? level_color(level) : "",
- log_level_str(level),
- target->use_color ? OSMO_LOGCOLOR_END : "",
- c_subsys ? c_subsys : "");
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
- }
- if (target->print_category_hex) {
- ret = snprintf(buf + offset, rem, "<%4.4x> ", subsys);
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ OSMO_STRBUF_PRINTF(sb, "%ld ", logging_tid);
}
+ if (target->print_category)
+ OSMO_STRBUF_PRINTF(sb, "%s%s%s%s ",
+ target->use_color ? level_color(level) : "",
+ log_category_name(subsys),
+ target->use_color ? OSMO_LOGCOLOR_END : "",
+ c_subsys ? c_subsys : "");
+ if (target->print_level)
+ OSMO_STRBUF_PRINTF(sb, "%s%s%s%s ",
+ target->use_color ? level_color(level) : "",
+ log_level_str(level),
+ target->use_color ? OSMO_LOGCOLOR_END : "",
+ c_subsys ? c_subsys : "");
+ if (target->print_category_hex)
+ OSMO_STRBUF_PRINTF(sb, "<%4.4x> ", subsys);
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);
+ OSMO_STRBUF_PRINTF(sb, "%s:%d ", file, line);
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);
+ OSMO_STRBUF_PRINTF(sb, "%s:%d ", const_basename(file), line);
break;
}
}
}
- ret = vsnprintf(buf + offset, rem, format, ap);
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ OSMO_STRBUF_APPEND(sb, vsnprintf, format, ap);
/* 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') {
+ && sb.pos > sb.buf && *(sb.pos - 1) == '\n') {
switch (target->print_filename2) {
case LOG_FILENAME_NONE:
break;
case LOG_FILENAME_PATH:
- offset --;
- ret = snprintf(buf + offset, rem, " (%s:%d)\n", file, line);
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ OSMO_STRBUF_DROP_TAIL(sb, 1);
+ OSMO_STRBUF_PRINTF(sb, " (%s:%d)\n", file, line);
break;
case LOG_FILENAME_BASENAME:
- 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);
+ OSMO_STRBUF_DROP_TAIL(sb, 1);
+ OSMO_STRBUF_PRINTF(sb, " (%s:%d)\n", const_basename(file), line);
break;
}
}
if (target->use_color && c_subsys) {
- ret = snprintf(buf + offset, rem, OSMO_LOGCOLOR_END);
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ /* Ensure the last color escape is sent before the newline
+ * (to not clobber journald, which works on single-lines only) */
+ if (sb.pos > sb.buf && *(sb.pos - 1) == '\n') {
+ OSMO_STRBUF_DROP_TAIL(sb, 1);
+ OSMO_STRBUF_PRINTF(sb, OSMO_LOGCOLOR_END "\n");
+ } else {
+ OSMO_STRBUF_PRINTF(sb, OSMO_LOGCOLOR_END);
+ }
}
err:
- buf[sizeof(buf)-1] = '\0';
- target->output(target, level, buf);
+ return OSMO_STRBUF_CHAR_COUNT(sb);
+}
+
+/* Format the log line for given target; use a stack buffer and call target->output */
+static void _output(struct log_target *target, unsigned int subsys,
+ unsigned int level, const char *file, int line, int cont,
+ const char *format, va_list ap)
+{
+ char buf[MAX_LOG_SIZE];
+ int rc;
+
+ rc = _output_buf(buf, sizeof(buf), target, subsys, level, file, line, cont, format, ap);
+ if (rc > 0)
+ target->output(target, level, buf);
}
/* Catch internal logging category indexes as well as out-of-bounds indexes.
@@ -655,6 +758,11 @@ void osmo_vlogp(int subsys, int level, const char *file, int line,
subsys = map_subsys(subsys);
+#if !defined(EMBEDDED)
+ if (!log_cache_check(subsys, level))
+ return;
+#endif
+
log_tgt_mutex_lock();
llist_for_each_entry(tar, &osmo_log_target_list, entry) {
@@ -711,12 +819,27 @@ void logp2(int subsys, unsigned int level, const char *file, int line, int cont,
TRACE(LIBOSMOCORE_LOG_DONE());
}
+/* This logging function is used as a fallback when the logging framework is
+ * not is not properly initialized. */
+void logp_stub(const char *file, int line, int cont, const char *format, ...)
+{
+ va_list ap;
+ if (!cont)
+ fprintf(stderr, "%s:%d ", file, line);
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+}
+
/*! Register a new log target with the logging core
* \param[in] target Log target to be registered
*/
void log_add_target(struct log_target *target)
{
llist_add_tail(&target->entry, &osmo_log_target_list);
+#if (!EMBEDDED)
+ log_cache_update_all();
+#endif
}
/*! Unregister a log target from the logging core
@@ -725,6 +848,9 @@ void log_add_target(struct log_target *target)
void log_del_target(struct log_target *target)
{
llist_del(&target->entry);
+#if (!EMBEDDED)
+ log_cache_update_all();
+#endif
}
/*! Reset (clear) the logging context */
@@ -885,6 +1011,9 @@ void log_set_print_level(struct log_target *target, int print_level)
void log_set_log_level(struct log_target *target, int log_level)
{
target->loglevel = log_level;
+#if !defined(EMBEDDED)
+ log_cache_update_all();
+#endif
}
/*! Set a category filter on a given log target
@@ -901,15 +1030,73 @@ void log_set_category_filter(struct log_target *target, int category,
category = map_subsys(category);
target->categories[category].enabled = !!enable;
target->categories[category].loglevel = level;
+
+#if !defined(EMBEDDED)
+ log_cache_update(category, !!enable, level);
+#endif
}
#if (!EMBEDDED)
-static void _file_output(struct log_target *target, unsigned int level,
+/* write-queue tells us we should write another msgb (log line) to the output fd */
+static int _file_wq_write_cb(struct osmo_fd *ofd, struct msgb *msg)
+{
+ int rc;
+
+ rc = write(ofd->fd, msgb_data(msg), msgb_length(msg));
+ if (rc < 0)
+ return rc;
+ if (rc != msgb_length(msg)) {
+ /* pull the number of bytes we have already written */
+ msgb_pull(msg, rc);
+ /* ask write_queue to re-insert the msgb at the head of the queue */
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+/* output via buffered, blocking stdio streams */
+static void _file_output_stream(struct log_target *target, unsigned int level,
const char *log)
{
- fprintf(target->tgt_file.out, "%s", log);
+ OSMO_ASSERT(target->tgt_file.out);
+ fputs(log, target->tgt_file.out);
fflush(target->tgt_file.out);
}
+
+/* output via non-blocking write_queue, doing internal buffering */
+static void _file_raw_output(struct log_target *target, int subsys, unsigned int level, const char *file,
+ int line, int cont, const char *format, va_list ap)
+{
+ struct msgb *msg;
+ int rc;
+
+ OSMO_ASSERT(target->tgt_file.wqueue);
+ msg = msgb_alloc_c(target->tgt_file.wqueue, MAX_LOG_SIZE, "log_file_msg");
+ if (!msg)
+ return;
+
+ /* we simply enqueue the log message to a write queue here, to avoid any blocking
+ * writes on the output file. The write queue will tell us once the file is writable
+ * and call _file_wq_write_cb() */
+ rc = _output_buf((char *)msgb_data(msg), msgb_tailroom(msg), target, subsys, level, file, line, cont, format, ap);
+ msgb_put(msg, rc);
+
+ /* attempt a synchronous, non-blocking write, if the write queue is empty */
+ if (target->tgt_file.wqueue->current_length == 0) {
+ rc = _file_wq_write_cb(&target->tgt_file.wqueue->bfd, msg);
+ if (rc == 0) {
+ /* the write was complete, we can exit early */
+ msgb_free(msg);
+ return;
+ }
+ }
+ /* if we reach here, either we already had elements in the write_queue, or the synchronous write
+ * failed: enqueue the message to the write_queue (backlog) */
+ if (osmo_wqueue_enqueue_quiet(target->tgt_file.wqueue, msg) < 0) {
+ msgb_free(msg);
+ /* TODO: increment some counter so we can see that messages were dropped */
+ }
+}
#endif
/*! Create a new log target skeleton
@@ -955,6 +1142,15 @@ struct log_target *log_target_create(void)
/* global log level */
target->loglevel = 0;
+
+#if !defined(EMBEDDED)
+ /* update cache */
+ for (i = 0; i < osmo_log_info->num_cat; i++) {
+ const struct log_info_cat *c = &osmo_log_info->cat[i];
+ log_cache_update(i, c->enabled, c->loglevel);
+ }
+#endif
+
return target;
}
@@ -972,7 +1168,7 @@ struct log_target *log_target_create_stderr(void)
target->type = LOG_TGT_TYPE_STDERR;
target->tgt_file.out = stderr;
- target->output = _file_output;
+ target->output = _file_output_stream;
return target;
#else
return NULL;
@@ -980,11 +1176,11 @@ struct log_target *log_target_create_stderr(void)
}
#if (!EMBEDDED)
-/*! Create a new file-based log target
+/*! Create a new file-based log target using buffered, blocking stream output
* \param[in] fname File name of the new log file
* \returns Log target in case of success, NULL otherwise
*/
-struct log_target *log_target_create_file(const char *fname)
+struct log_target *log_target_create_file_stream(const char *fname)
{
struct log_target *target;
@@ -998,9 +1194,165 @@ struct log_target *log_target_create_file(const char *fname)
log_target_destroy(target);
return NULL;
}
+ target->output = _file_output_stream;
+ target->tgt_file.fname = talloc_strdup(target, fname);
+
+ return target;
+}
+
+/*! switch from non-blocking/write-queue to blocking + buffered stream output
+ * \param[in] target log target which we should switch
+ * \return 0 on success; 1 if already switched before; negative on error
+ * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock.
+ */
+int log_target_file_switch_to_stream(struct log_target *target)
+{
+ struct osmo_wqueue *wq;
+
+ if (!target)
+ return -ENODEV;
+
+ if (target->tgt_file.out) {
+ /* target has already been switched over */
+ return 1;
+ }
+
+ wq = target->tgt_file.wqueue;
+ OSMO_ASSERT(wq);
+
+ /* re-open output as stream */
+ if (target->type == LOG_TGT_TYPE_STDERR)
+ target->tgt_file.out = stderr;
+ else
+ target->tgt_file.out = fopen(target->tgt_file.fname, "a");
+ if (!target->tgt_file.out) {
+ return -EIO;
+ }
+
+ /* synchronously write anything left in the queue */
+ while (!llist_empty(&wq->msg_queue)) {
+ struct msgb *msg = msgb_dequeue(&wq->msg_queue);
+ fwrite(msgb_data(msg), msgb_length(msg), 1, target->tgt_file.out);
+ msgb_free(msg);
+ }
+
+ /* now that everything succeeded, we can finally close the old output fd */
+ if (target->type == LOG_TGT_TYPE_FILE) {
+ osmo_fd_unregister(&wq->bfd);
+ close(wq->bfd.fd);
+ wq->bfd.fd = -1;
+ }
+
+ /* release the queue itself */
+ talloc_free(wq);
+ target->tgt_file.wqueue = NULL;
+ target->output = _file_output_stream;
+ target->raw_output = NULL;
+
+ return 0;
+}
+
+/*! switch from blocking + buffered file output to non-blocking write-queue based output.
+ * \param[in] target log target which we should switch
+ * \return 0 on success; 1 if already switched before; negative on error
+ * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock.
+ */
+int log_target_file_switch_to_wqueue(struct log_target *target)
+{
+ struct osmo_wqueue *wq;
+ int rc;
+
+ if (!target)
+ return -ENODEV;
+
+ if (!target->tgt_file.out) {
+ /* target has already been switched over */
+ return 1;
+ }
+
+ /* we create a ~640kB sized talloc pool within the write-queue to ensure individual
+ * log lines (stored as msgbs) will not put result in malloc() calls, and also to
+ * reduce the OOM probability within logging, as the pool is already allocated */
+ wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN,
+ LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE));
+ if (!wq)
+ return -ENOMEM;
+ osmo_wqueue_init(wq, LOG_WQUEUE_LEN);
+
+ fflush(target->tgt_file.out);
+ if (target->type == LOG_TGT_TYPE_FILE) {
+ rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660);
+ if (rc < 0) {
+ talloc_free(wq);
+ return -errno;
+ }
+ } else {
+ rc = STDERR_FILENO;
+ }
+ wq->bfd.fd = rc;
+ wq->bfd.when = OSMO_FD_WRITE;
+ wq->write_cb = _file_wq_write_cb;
+
+ rc = osmo_fd_register(&wq->bfd);
+ if (rc < 0) {
+ talloc_free(wq);
+ return -EIO;
+ }
+ target->tgt_file.wqueue = wq;
+ target->raw_output = _file_raw_output;
+ target->output = NULL;
+
+ /* now that everything succeeded, we can finally close the old output stream */
+ if (target->type == LOG_TGT_TYPE_FILE)
+ fclose(target->tgt_file.out);
+ target->tgt_file.out = NULL;
+
+ return 0;
+}
+
+/*! Create a new file-based log target using non-blocking write_queue
+ * \param[in] fname File name of the new log file
+ * \returns Log target in case of success, NULL otherwise
+ */
+struct log_target *log_target_create_file(const char *fname)
+{
+ struct log_target *target;
+ struct osmo_wqueue *wq;
+ int rc;
+
+ target = log_target_create();
+ if (!target)
+ return NULL;
+
+ target->type = LOG_TGT_TYPE_FILE;
+ /* we create a ~640kB sized talloc pool within the write-queue to ensure individual
+ * log lines (stored as msgbs) will not put result in malloc() calls, and also to
+ * reduce the OOM probability within logging, as the pool is already allocated */
+ wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN,
+ LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE));
+ if (!wq) {
+ log_target_destroy(target);
+ return NULL;
+ }
+ osmo_wqueue_init(wq, LOG_WQUEUE_LEN);
+ wq->bfd.fd = open(fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660);
+ if (wq->bfd.fd < 0) {
+ talloc_free(wq);
+ log_target_destroy(target);
+ return NULL;
+ }
+ wq->bfd.when = OSMO_FD_WRITE;
+ wq->write_cb = _file_wq_write_cb;
- target->output = _file_output;
+ rc = osmo_fd_register(&wq->bfd);
+ if (rc < 0) {
+ talloc_free(wq);
+ log_target_destroy(target);
+ return NULL;
+ }
+ target->tgt_file.wqueue = wq;
+ target->raw_output = _file_raw_output;
target->tgt_file.fname = talloc_strdup(target, fname);
return target;
@@ -1013,7 +1365,7 @@ struct log_target *log_target_create_file(const char *fname)
* \returns Log target (if found), NULL otherwise
* Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock.
*/
-struct log_target *log_target_find(int type, const char *fname)
+struct log_target *log_target_find(enum log_target_type type, const char *fname)
{
struct log_target *tgt;
@@ -1040,17 +1392,36 @@ struct log_target *log_target_find(int type, const char *fname)
* \param[in] target log target to unregister, close and delete */
void log_target_destroy(struct log_target *target)
{
-
/* just in case, to make sure we don't have any references */
log_del_target(target);
#if (!EMBEDDED)
+ struct osmo_wqueue *wq;
switch (target->type) {
case LOG_TGT_TYPE_FILE:
- if (target->tgt_file.out == NULL)
- break;
- fclose(target->tgt_file.out);
- target->tgt_file.out = NULL;
+ case LOG_TGT_TYPE_STDERR:
+ if (target->tgt_file.out) {
+ if (target->type == LOG_TGT_TYPE_FILE)
+ fclose(target->tgt_file.out);
+ target->tgt_file.out = NULL;
+ }
+ wq = target->tgt_file.wqueue;
+ if (wq) {
+ if (wq->bfd.fd >= 0) {
+ osmo_fd_unregister(&wq->bfd);
+ if (target->type == LOG_TGT_TYPE_FILE)
+ close(wq->bfd.fd);
+ wq->bfd.fd = -1;
+ }
+ osmo_wqueue_clear(wq);
+ talloc_free(wq);
+ target->tgt_file.wqueue = NULL;
+ }
+ talloc_free((void *)target->tgt_file.fname);
+ target->tgt_file.fname = NULL;
+ break;
+ case LOG_TGT_TYPE_GSMTAP:
+ gsmtap_source_free(target->tgt_gsmtap.gsmtap_inst);
break;
#ifdef HAVE_SYSLOG_H
case LOG_TGT_TYPE_SYSLOG:
@@ -1071,13 +1442,33 @@ void log_target_destroy(struct log_target *target)
* \returns 0 in case of success; negative otherwise */
int log_target_file_reopen(struct log_target *target)
{
- fclose(target->tgt_file.out);
+ struct osmo_wqueue *wq;
+ int rc;
- target->tgt_file.out = fopen(target->tgt_file.fname, "a");
- if (!target->tgt_file.out)
- return -errno;
+ OSMO_ASSERT(target->type == LOG_TGT_TYPE_FILE || target->type == LOG_TGT_TYPE_STDERR);
+ OSMO_ASSERT(target->tgt_file.out || target->tgt_file.wqueue);
- /* we assume target->output already to be set */
+ if (target->tgt_file.out) {
+ fclose(target->tgt_file.out);
+ target->tgt_file.out = fopen(target->tgt_file.fname, "a");
+ if (!target->tgt_file.out)
+ return -errno;
+ } else {
+ wq = target->tgt_file.wqueue;
+ if (wq->bfd.fd >= 0) {
+ osmo_fd_unregister(&wq->bfd);
+ close(wq->bfd.fd);
+ wq->bfd.fd = -1;
+ }
+
+ rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660);
+ if (rc < 0)
+ return -errno;
+ wq->bfd.fd = rc;
+ rc = osmo_fd_register(&wq->bfd);
+ if (rc < 0)
+ return rc;
+ }
return 0;
}
@@ -1107,6 +1498,31 @@ int log_targets_reopen(void)
return rc;
}
+/*! Enable the log level lookup cache to bypass string formatting and other code for log statements which are
+ * not actually enabled/needed by any existing log target.
+ * \retruns 0 in case of success, negative -errno in case of error. */
+int log_cache_enable(void)
+{
+#if !defined(EMBEDDED)
+ if (log_level_lookup_cache)
+ return -EEXIST;
+
+ log_level_lookup_cache = talloc_zero_array(osmo_log_info, uint8_t, osmo_log_info->num_cat);
+ if (!log_level_lookup_cache)
+ return -ENOMEM;
+
+ /* copy everything for level lookup cache */
+ log_tgt_mutex_lock();
+ log_cache_update_all();
+ log_tgt_mutex_unlock();
+
+ return 0;
+#else
+ return -ENOTSUP;
+#endif
+}
+
+
/*! Initialize the Osmocom logging core
* \param[in] inf Information regarding logging categories, could be NULL
* \param[in] ctx talloc context for logging allocations
@@ -1200,6 +1616,11 @@ int log_check_level(int subsys, unsigned int level)
subsys = map_subsys(subsys);
+#if !defined(EMBEDDED)
+ if (!log_cache_check(subsys, level))
+ return 0;
+#endif
+
/* TODO: The following could/should be cached (update on config) */
log_tgt_mutex_lock();
diff --git a/src/logging_gsmtap.c b/src/core/logging_gsmtap.c
index e190f88f..7775c27d 100644
--- a/src/logging_gsmtap.c
+++ b/src/core/logging_gsmtap.c
@@ -21,17 +21,13 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup logging
* @{
* \file logging_gsmtap.c */
-#include "../config.h"
+#include "config.h"
#include <stdarg.h>
#include <stdlib.h>
@@ -69,6 +65,7 @@ static void _gsmtap_raw_output(struct log_target *target, int subsys,
struct timeval tv;
int rc;
const char *file_basename;
+ unsigned int _level;
/* get timestamp ASAP */
osmo_gettimeofday(&tv, NULL);
@@ -118,7 +115,11 @@ static void _gsmtap_raw_output(struct log_target *target, int subsys,
}
msgb_put(msg, rc);
+ /* Ensure that any error occurring when sending the log message doesn't cause infinite recursion */
+ _level = target->loglevel;
+ target->loglevel = UINT8_MAX;
rc = gsmtap_sendmsg(target->tgt_gsmtap.gsmtap_inst, msg);
+ target->loglevel = _level;
if (rc)
msgb_free(msg);
}
diff --git a/src/logging_syslog.c b/src/core/logging_syslog.c
index f980689d..1153bdf4 100644
--- a/src/logging_syslog.c
+++ b/src/core/logging_syslog.c
@@ -16,17 +16,13 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup logging
* @{
* \file logging_syslog.c */
-#include "../config.h"
+#include "config.h"
#ifdef HAVE_SYSLOG_H
diff --git a/src/logging_systemd.c b/src/core/logging_systemd.c
index 7c966863..2e86feb6 100644
--- a/src/logging_systemd.c
+++ b/src/core/logging_systemd.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup logging
diff --git a/src/loggingrb.c b/src/core/loggingrb.c
index 4a80cc8a..2bf7b665 100644
--- a/src/loggingrb.c
+++ b/src/core/loggingrb.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup loggingrb
diff --git a/src/macaddr.c b/src/core/macaddr.c
index 56fdf86e..3b231fb8 100644
--- a/src/macaddr.c
+++ b/src/core/macaddr.c
@@ -18,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup utils
diff --git a/src/mnl.c b/src/core/mnl.c
index a7d3511e..d148e1b3 100644
--- a/src/mnl.c
+++ b/src/core/mnl.c
@@ -8,7 +8,7 @@
/*
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
- * All Rights Reserverd.
+ * All Rights Reserved.
*
* SPDX-License-Identifier: GPL-2.0+
*
@@ -21,11 +21,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
*/
#include <osmocom/core/select.h>
@@ -59,7 +54,7 @@ static int osmo_mnl_fd_cb(struct osmo_fd *ofd, unsigned int what)
}
/*! create an osmocom-wrapped limnl netlink socket.
- * \parma[in] ctx talloc context from which to allocate
+ * \param[in] ctx talloc context from which to allocate
* \param[in] bus netlink socket bus ID (see NETLINK_* constants)
* \param[in] groups groups of messages to bind/subscribe to
* \param[in] mnl_cb callback function called for each incoming message
diff --git a/src/msgb.c b/src/core/msgb.c
index fbb157ae..713510c6 100644
--- a/src/msgb.c
+++ b/src/core/msgb.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup msgb
@@ -318,24 +314,29 @@ void *msgb_talloc_ctx_init(void *root_ctx, unsigned int pool_size)
return tall_msgb_ctx;
}
-/*! Copy an msgb.
+/*! Copy an msgb with memory reallocation.
*
- * This function allocates a new msgb, copies the data buffer of msg,
- * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part
- * is not copied.
+ * This function allocates a new msgb with new_len size, copies the data buffer of msg,
+ * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part is not copied.
+ * \param[in] ctx talloc context on which allocation happens
* \param[in] msg The old msgb object
- * \param[in] name Human-readable name to be associated with msgb
+ * \param[in] new_len The length of new msgb object
+ * \param[in] name Human-readable name to be associated with new msgb
*/
-struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *name)
+struct msgb *msgb_copy_resize_c(const void *ctx, const struct msgb *msg, uint16_t new_len, const char *name)
{
struct msgb *new_msg;
- new_msg = msgb_alloc_c(ctx, msg->data_len, name);
- if (!new_msg)
+ if (new_len < msgb_length(msg)) {
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "Data from old msgb (%u bytes) won't fit into new msgb (%u bytes) after reallocation\n",
+ msgb_length(msg), new_len);
return NULL;
+ }
- /* copy data */
- memcpy(new_msg->_data, msg->_data, new_msg->data_len);
+ new_msg = msgb_alloc_c(ctx, new_len, name);
+ if (!new_msg)
+ return NULL;
/* copy header */
new_msg->len = msg->len;
@@ -343,6 +344,9 @@ struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *na
new_msg->head += msg->head - msg->_data;
new_msg->tail += msg->tail - msg->_data;
+ /* copy data */
+ memcpy(new_msg->data, msg->data, msgb_length(msg));
+
if (msg->l1h)
new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data);
if (msg->l2h)
@@ -355,6 +359,32 @@ struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *na
return new_msg;
}
+/*! Copy an msgb with memory reallocation.
+ *
+ * This function allocates a new msgb with new_len size, copies the data buffer of msg,
+ * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part is not copied.
+ * \param[in] msg The old msgb object
+ * \param[in] name Human-readable name to be associated with new msgb
+ */
+struct msgb *msgb_copy_resize(const struct msgb *msg, uint16_t new_len, const char *name)
+{
+ return msgb_copy_resize_c(tall_msgb_ctx, msg, new_len, name);
+}
+
+/*! Copy an msgb.
+ *
+ * This function allocates a new msgb, copies the data buffer of msg,
+ * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part
+ * is not copied.
+ * \param[in] ctx talloc context on which allocation happens
+ * \param[in] msg The old msgb object
+ * \param[in] name Human-readable name to be associated with msgb
+ */
+struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *name)
+{
+ return msgb_copy_resize_c(ctx, msg, msg->data_len, name);
+}
+
/*! Copy an msgb.
*
* This function allocates a new msgb, copies the data buffer of msg,
@@ -430,11 +460,11 @@ int msgb_resize_area(struct msgb *msg, uint8_t *area,
*/
char *msgb_hexdump_buf(char *buf, size_t buf_len, const struct msgb *msg)
{
- int buf_offs = 0;
+ unsigned int buf_offs = 0;
int nchars;
const unsigned char *start = msg->data;
const unsigned char *lxhs[4];
- int i;
+ unsigned int i;
lxhs[0] = msg->l1h;
lxhs[1] = msg->l2h;
diff --git a/src/msgfile.c b/src/core/msgfile.c
index 1f11aa60..abb4e7cf 100644
--- a/src/msgfile.c
+++ b/src/core/msgfile.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#define _WITH_GETLINE
diff --git a/src/core/netdev.c b/src/core/netdev.c
new file mode 100644
index 00000000..318134af
--- /dev/null
+++ b/src/core/netdev.c
@@ -0,0 +1,962 @@
+
+/* network device (interface) functions.
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "config.h"
+
+/*! \addtogroup netdev
+ * @{
+ * network device (interface) convenience functions
+ *
+ * \file netdev.c
+ *
+ * Example lifecycle use of the API:
+ *
+ * struct osmo_sockaddr_str osa_str = {};
+ * struct osmo_sockaddr osa = {};
+ *
+ * // Allocate object:
+ * struct osmo_netdev *netdev = osmo_netdev_alloc(parent_talloc_ctx, name);
+ * OSMO_ASSERT(netdev);
+ *
+ * // Configure object (before registration):
+ * rc = osmo_netdev_set_netns_name(netdev, "some_netns_name_or_null");
+ * rc = osmo_netdev_set_ifindex(netdev, if_nametoindex("eth0"));
+ *
+ * // Register object:
+ * rc = osmo_netdev_register(netdev);
+ * // The network interface is now being monitored and the network interface
+ * // can be operated (see below)
+ *
+ * // Add a local IPv4 address:
+ * rc = osmo_sockaddr_str_from_str2(&osa_str, "192.168.200.1");
+ * rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas);
+ * rc = osmo_netdev_add_addr(netdev, &osa, 24);
+ *
+ * // Bring network interface up:
+ * rc = osmo_netdev_ifupdown(netdev, true);
+ *
+ * // Add default route (0.0.0.0/0):
+ * rc = osmo_sockaddr_str_from_str2(&osa_str, "0.0.0.0");
+ * rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas);
+ * rc = osmo_netdev_add_route(netdev, &osa, 0, NULL);
+ *
+ * // Unregister (can be freed directly too):
+ * rc = osmo_netdev_unregister(netdev);
+ * // Free the object:
+ * osmo_netdev_free(netdev);
+ */
+
+#if (!EMBEDDED)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <net/route.h>
+
+#if defined(__linux__)
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+#else
+#error "Unknown platform!"
+#endif
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/netns.h>
+#include <osmocom/core/netdev.h>
+
+#if ENABLE_LIBMNL
+#include <osmocom/core/mnl.h>
+#endif
+
+#define IFINDEX_UNUSED 0
+
+#define LOGNETDEV(netdev, lvl, fmt, args ...) \
+ LOGP(DLGLOBAL, lvl, "NETDEV(%s,if=%s/%u,ns=%s): " fmt, \
+ (netdev)->name, osmo_netdev_get_dev_name(netdev) ? : "", \
+ (netdev)->ifindex, (netdev)->netns_name ? : "", ## args)
+
+static struct llist_head g_netdev_netns_ctx_list = LLIST_HEAD_INIT(g_netdev_netns_ctx_list);
+static struct llist_head g_netdev_list = LLIST_HEAD_INIT(g_netdev_list);
+
+/* One per netns, shared by all osmo_netdev in a given netns: */
+struct netdev_netns_ctx {
+ struct llist_head entry; /* entry in g_netdev_netns_ctx_list */
+ unsigned int refcount; /* Number of osmo_netdev currently registered on this netns */
+ const char *netns_name; /* default netns has empty string "" (never NULL!) */
+ int netns_fd; /* FD to the netns with name "netns_name" above */
+#if ENABLE_LIBMNL
+ struct osmo_mnl *omnl;
+#endif
+};
+
+static struct netdev_netns_ctx *netdev_netns_ctx_alloc(void *ctx, const char *netns_name)
+{
+ struct netdev_netns_ctx *netns_ctx;
+ OSMO_ASSERT(netns_name);
+
+ netns_ctx = talloc_zero(ctx, struct netdev_netns_ctx);
+ if (!netns_ctx)
+ return NULL;
+
+ netns_ctx->netns_name = talloc_strdup(netns_ctx, netns_name);
+ netns_ctx->netns_fd = -1;
+
+ llist_add_tail(&netns_ctx->entry, &g_netdev_netns_ctx_list);
+ return netns_ctx;
+
+}
+
+static void netdev_netns_ctx_free(struct netdev_netns_ctx *netns_ctx)
+{
+ if (!netns_ctx)
+ return;
+
+ llist_del(&netns_ctx->entry);
+
+#if ENABLE_LIBMNL
+ if (netns_ctx->omnl) {
+ osmo_mnl_destroy(netns_ctx->omnl);
+ netns_ctx->omnl = NULL;
+ }
+#endif
+
+ if (netns_ctx->netns_fd != -1) {
+ close(netns_ctx->netns_fd);
+ netns_ctx->netns_fd = -1;
+ }
+ talloc_free(netns_ctx);
+}
+
+#if ENABLE_LIBMNL
+static int netdev_netns_ctx_mnl_cb(const struct nlmsghdr *nlh, void *data);
+#endif
+
+static int netdev_netns_ctx_init(struct netdev_netns_ctx *netns_ctx)
+{
+ struct osmo_netns_switch_state switch_state;
+ int rc;
+
+ if (netns_ctx->netns_name[0] != '\0') {
+ LOGP(DLGLOBAL, LOGL_INFO, "Prepare netns: Switch to netns '%s'\n", netns_ctx->netns_name);
+ netns_ctx->netns_fd = osmo_netns_open_fd(netns_ctx->netns_name);
+ if (netns_ctx->netns_fd < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Prepare netns: Cannot switch to netns '%s': %s (%d)\n",
+ netns_ctx->netns_name, strerror(errno), errno);
+ return netns_ctx->netns_fd;
+ }
+
+ /* temporarily switch to specified namespace to create netlink socket */
+ rc = osmo_netns_switch_enter(netns_ctx->netns_fd, &switch_state);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Prepare netns: Cannot switch to netns '%s': %s (%d)\n",
+ netns_ctx->netns_name, strerror(errno), errno);
+ /* netns_ctx->netns_fd will be freed by future call to netdev_netns_ctx_free() */
+ return rc;
+ }
+ }
+
+#if ENABLE_LIBMNL
+ netns_ctx->omnl = osmo_mnl_init(NULL, NETLINK_ROUTE, RTMGRP_LINK, netdev_netns_ctx_mnl_cb, netns_ctx);
+ rc = (netns_ctx->omnl ? 0 : -EFAULT);
+#else
+ rc = 0;
+#endif
+
+ /* switch back to default namespace */
+ if (netns_ctx->netns_name[0] != '\0') {
+ int rc2 = osmo_netns_switch_exit(&switch_state);
+ if (rc2 < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Prepare netns: Cannot switch back from netns '%s': %s\n",
+ netns_ctx->netns_name, strerror(errno));
+ return rc2;
+ }
+ LOGP(DLGLOBAL, LOGL_INFO, "Prepare netns: Back from netns '%s'\n",
+ netns_ctx->netns_name);
+ }
+ return rc;
+}
+
+static struct netdev_netns_ctx *netdev_netns_ctx_find_by_netns_name(const char *netns_name)
+{
+ struct netdev_netns_ctx *netns_ctx;
+
+ llist_for_each_entry(netns_ctx, &g_netdev_netns_ctx_list, entry) {
+ if (strcmp(netns_ctx->netns_name, netns_name))
+ continue;
+ return netns_ctx;
+ }
+
+ return NULL;
+}
+
+static struct netdev_netns_ctx *netdev_netns_ctx_get(const char *netns_name)
+{
+ struct netdev_netns_ctx *netns_ctx;
+ int rc;
+
+ OSMO_ASSERT(netns_name);
+ netns_ctx = netdev_netns_ctx_find_by_netns_name(netns_name);
+ if (!netns_ctx) {
+ netns_ctx = netdev_netns_ctx_alloc(NULL, netns_name);
+ if (!netns_ctx)
+ return NULL;
+ rc = netdev_netns_ctx_init(netns_ctx);
+ if (rc < 0) {
+ netdev_netns_ctx_free(netns_ctx);
+ return NULL;
+ }
+ }
+ netns_ctx->refcount++;
+ return netns_ctx;
+}
+
+static void netdev_netns_ctx_put(struct netdev_netns_ctx *netns_ctx)
+{
+ OSMO_ASSERT(netns_ctx);
+ netns_ctx->refcount--;
+
+ if (netns_ctx->refcount == 0)
+ netdev_netns_ctx_free(netns_ctx);
+}
+
+struct osmo_netdev {
+ /* entry in g_netdev_list */
+ struct llist_head entry;
+
+ /* Pointer to struct shared (refcounted) by all osmo_netdev in the same netns: */
+ struct netdev_netns_ctx *netns_ctx;
+
+ /* Name used to identify the osmo_netdev */
+ char *name;
+
+ /* ifindex of the network interface (address space is per netns) */
+ unsigned int ifindex;
+
+ /* Network interface name. Can change over lifetime of the interface. */
+ char *dev_name;
+
+ /* netns name where the netdev interface is created (NULL = default netns) */
+ char *netns_name;
+
+ /* API user private data */
+ void *priv_data;
+
+ /* Whether the netdev is in operation (managing the netdev interface) */
+ bool registered;
+
+ /* Called by netdev each time a new up/down state change is detected. Can be NULL. */
+ osmo_netdev_ifupdown_ind_cb_t ifupdown_ind_cb;
+
+ /* Called by netdev each time the registered network interface is renamed by the system. Can be NULL. */
+ osmo_netdev_dev_name_chg_cb_t dev_name_chg_cb;
+
+ /* Called by netdev each time the configured MTU changes in registered network interface. Can be NULL. */
+ osmo_netdev_mtu_chg_cb_t mtu_chg_cb;
+
+ /* Whether the netdev interface is UP */
+ bool if_up;
+ /* Whether we know the interface updown state (aka if if_up holds information)*/
+ bool if_up_known;
+
+ /* The netdev interface MTU size */
+ uint32_t if_mtu;
+ /* Whether we know the interface MTU size (aka if if_mtu holds information)*/
+ bool if_mtu_known;
+};
+
+#define NETDEV_NETNS_ENTER(netdev, switch_state, str_prefix) \
+ do { \
+ if ((netdev)->netns_name) { \
+ LOGNETDEV(netdev, LOGL_DEBUG, str_prefix ": Switch to netns '%s'\n", \
+ (netdev)->netns_name); \
+ int rc2 = osmo_netns_switch_enter((netdev)->netns_ctx->netns_fd, switch_state); \
+ if (rc2 < 0) { \
+ LOGNETDEV(netdev, LOGL_ERROR, str_prefix ": Cannot switch to netns '%s': %s (%d)\n", \
+ (netdev)->netns_name, strerror(errno), errno); \
+ return -EACCES; \
+ } \
+ } \
+ } while (0)
+
+#define NETDEV_NETNS_EXIT(netdev, switch_state, str_prefix) \
+ do { \
+ if ((netdev)->netns_name) { \
+ int rc2 = osmo_netns_switch_exit(switch_state); \
+ if (rc2 < 0) { \
+ LOGNETDEV(netdev, LOGL_ERROR, str_prefix ": Cannot switch back from netns '%s': %s\n", \
+ (netdev)->netns_name, strerror(errno)); \
+ return rc2; \
+ } \
+ LOGNETDEV(netdev, LOGL_DEBUG, str_prefix ": Back from netns '%s'\n", \
+ (netdev)->netns_name); \
+ } \
+ } while (0)
+
+#if ENABLE_LIBMNL
+/* validate the netlink attributes */
+static int netdev_mnl_data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case IFLA_ADDRESS:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ return MNL_CB_ERROR;
+ break;
+ case IFLA_MTU:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ return MNL_CB_ERROR;
+ break;
+ case IFLA_IFNAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ return MNL_CB_ERROR;
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void netdev_mnl_check_mtu_change(struct osmo_netdev *netdev, uint32_t mtu)
+{
+ if (netdev->if_mtu_known && netdev->if_mtu == mtu)
+ return;
+
+ LOGNETDEV(netdev, LOGL_NOTICE, "MTU changed: %u\n", mtu);
+ if (netdev->mtu_chg_cb)
+ netdev->mtu_chg_cb(netdev, mtu);
+
+ netdev->if_mtu_known = true;
+ netdev->if_mtu = mtu;
+}
+
+static void netdev_mnl_check_link_state_change(struct osmo_netdev *netdev, bool if_up)
+{
+ if (netdev->if_up_known && netdev->if_up == if_up)
+ return;
+
+ LOGNETDEV(netdev, LOGL_NOTICE, "Physical link state changed: %s\n",
+ if_up ? "UP" : "DOWN");
+ if (netdev->ifupdown_ind_cb)
+ netdev->ifupdown_ind_cb(netdev, if_up);
+
+ netdev->if_up_known = true;
+ netdev->if_up = if_up;
+}
+
+static int netdev_mnl_cb(struct osmo_netdev *netdev, struct ifinfomsg *ifm, struct nlattr **tb)
+{
+ char ifnamebuf[IF_NAMESIZE];
+ const char *ifname = NULL;
+ bool if_running;
+
+ if (tb[IFLA_IFNAME]) {
+ ifname = mnl_attr_get_str(tb[IFLA_IFNAME]);
+ LOGNETDEV(netdev, LOGL_DEBUG, "%s(): ifname=%s\n", __func__, ifname);
+ } else {
+ /* Try harder to obtain the ifname. This code path should in
+ * general not be triggered since usually IFLA_IFNAME is there */
+ struct osmo_netns_switch_state switch_state;
+ NETDEV_NETNS_ENTER(netdev, &switch_state, "if_indextoname");
+ ifname = if_indextoname(ifm->ifi_index, ifnamebuf);
+ NETDEV_NETNS_EXIT(netdev, &switch_state, "if_indextoname");
+ }
+ if (ifname) {
+ /* Update dev_name if it changed: */
+ if (strcmp(netdev->dev_name, ifname) != 0) {
+ if (netdev->dev_name_chg_cb)
+ netdev->dev_name_chg_cb(netdev, ifname);
+ osmo_talloc_replace_string(netdev, &netdev->dev_name, ifname);
+ }
+ }
+
+ if (tb[IFLA_MTU]) {
+ uint32_t mtu = mnl_attr_get_u32(tb[IFLA_MTU]);
+ LOGNETDEV(netdev, LOGL_DEBUG, "%s(): mtu=%u\n", __func__, mtu);
+ netdev_mnl_check_mtu_change(netdev, mtu);
+ }
+ if (tb[IFLA_ADDRESS]) {
+ uint8_t *hwaddr = mnl_attr_get_payload(tb[IFLA_ADDRESS]);
+ uint16_t hwaddr_len = mnl_attr_get_payload_len(tb[IFLA_ADDRESS]);
+ LOGNETDEV(netdev, LOGL_DEBUG, "%s(): hwaddress=%s\n",
+ __func__, osmo_hexdump(hwaddr, hwaddr_len));
+ }
+
+ if_running = !!(ifm->ifi_flags & IFF_RUNNING);
+ LOGNETDEV(netdev, LOGL_DEBUG, "%s(): up=%u running=%u\n",
+ __func__, !!(ifm->ifi_flags & IFF_UP), if_running);
+ netdev_mnl_check_link_state_change(netdev, if_running);
+
+ return MNL_CB_OK;
+}
+
+static int netdev_netns_ctx_mnl_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct osmo_mnl *omnl = data;
+ struct netdev_netns_ctx *netns_ctx = (struct netdev_netns_ctx *)omnl->priv;
+ struct nlattr *tb[IFLA_MAX+1] = {};
+ struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
+ struct osmo_netdev *netdev;
+
+ OSMO_ASSERT(omnl);
+ OSMO_ASSERT(ifm);
+
+ mnl_attr_parse(nlh, sizeof(*ifm), netdev_mnl_data_attr_cb, tb);
+
+ LOGP(DLGLOBAL, LOGL_DEBUG,
+ "%s(): index=%d type=%d flags=0x%x family=%d\n", __func__,
+ ifm->ifi_index, ifm->ifi_type, ifm->ifi_flags, ifm->ifi_family);
+
+ if (ifm->ifi_index == IFINDEX_UNUSED)
+ return MNL_CB_ERROR;
+
+ /* Find the netdev (if any) using key <netns,ifindex>.
+ * Different users of the API may have its own osmo_netdev object
+ * tracking potentially same netif, hence we need to iterate the whole list
+ * and dispatch to all matches:
+ */
+ bool found_any = false;
+ llist_for_each_entry(netdev, &g_netdev_list, entry) {
+ if (!netdev->registered)
+ continue;
+ if (netdev->ifindex != ifm->ifi_index)
+ continue;
+ if (strcmp(netdev->netns_ctx->netns_name, netns_ctx->netns_name))
+ continue;
+ found_any = true;
+ netdev_mnl_cb(netdev, ifm, &tb[0]);
+ }
+
+ if (!found_any) {
+ LOGP(DLGLOBAL, LOGL_DEBUG, "%s(): device with ifindex %u on netns %s not registered\n", __func__,
+ ifm->ifi_index, netns_ctx->netns_name);
+ }
+ return MNL_CB_OK;
+}
+
+/* Trigger an initial dump of the iface to get link information */
+static int netdev_mnl_request_initial_dump(struct osmo_mnl *omnl, unsigned int if_index)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ struct ifinfomsg *ifm;
+
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_seq = time(NULL);
+
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifi_family = AF_UNSPEC;
+ ifm->ifi_index = if_index;
+
+ if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "mnl_socket_sendto\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int netdev_mnl_set_ifupdown(struct osmo_mnl *omnl, unsigned int if_index,
+ const char *dev_name, bool up)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ struct ifinfomsg *ifm;
+ unsigned int change = 0;
+ unsigned int flags = 0;
+
+ if (up) {
+ change |= IFF_UP;
+ flags |= IFF_UP;
+ } else {
+ change |= IFF_UP;
+ flags &= ~IFF_UP;
+ }
+
+ nlh->nlmsg_type = RTM_NEWLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_seq = time(NULL);
+
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifi_family = AF_UNSPEC;
+ ifm->ifi_change = change;
+ ifm->ifi_flags = flags;
+ ifm->ifi_index = if_index;
+
+ if (dev_name)
+ mnl_attr_put_str(nlh, IFLA_IFNAME, dev_name);
+
+ if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "mnl_socket_sendto\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int netdev_mnl_add_addr(struct osmo_mnl *omnl, unsigned int if_index, const struct osmo_sockaddr *osa, uint8_t prefix)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ struct ifaddrmsg *ifm;
+
+ nlh->nlmsg_type = RTM_NEWADDR;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK;
+ nlh->nlmsg_seq = time(NULL);
+
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifa_family = osa->u.sa.sa_family;
+ ifm->ifa_prefixlen = prefix;
+ ifm->ifa_flags = IFA_F_PERMANENT;
+ ifm->ifa_scope = RT_SCOPE_UNIVERSE;
+ ifm->ifa_index = if_index;
+
+ /*
+ * The exact meaning of IFA_LOCAL and IFA_ADDRESS depend
+ * on the address family being used and the device type.
+ * For broadcast devices (like the interfaces we use),
+ * for IPv4 we specify both and they are used interchangeably.
+ * For IPv6, only IFA_ADDRESS needs to be set.
+ */
+ switch (osa->u.sa.sa_family) {
+ case AF_INET:
+ mnl_attr_put_u32(nlh, IFA_LOCAL, osa->u.sin.sin_addr.s_addr);
+ mnl_attr_put_u32(nlh, IFA_ADDRESS, osa->u.sin.sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ mnl_attr_put(nlh, IFA_ADDRESS, sizeof(struct in6_addr), &osa->u.sin6.sin6_addr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "mnl_socket_sendto\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int netdev_mnl_add_route(struct osmo_mnl *omnl,
+ unsigned int if_index,
+ const struct osmo_sockaddr *dst_osa,
+ uint8_t dst_prefix,
+ const struct osmo_sockaddr *gw_osa)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ struct rtmsg *rtm;
+
+ nlh->nlmsg_type = RTM_NEWROUTE;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
+ nlh->nlmsg_seq = time(NULL);
+
+ rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(*rtm));
+ rtm->rtm_family = dst_osa->u.sa.sa_family;
+ rtm->rtm_dst_len = dst_prefix;
+ rtm->rtm_src_len = 0;
+ rtm->rtm_tos = 0;
+ rtm->rtm_protocol = RTPROT_STATIC;
+ rtm->rtm_table = RT_TABLE_MAIN;
+ rtm->rtm_type = RTN_UNICAST;
+ rtm->rtm_scope = gw_osa ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK;
+ rtm->rtm_flags = 0;
+
+ switch (dst_osa->u.sa.sa_family) {
+ case AF_INET:
+ mnl_attr_put_u32(nlh, RTA_DST, dst_osa->u.sin.sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ mnl_attr_put(nlh, RTA_DST, sizeof(struct in6_addr), &dst_osa->u.sin6.sin6_addr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mnl_attr_put_u32(nlh, RTA_OIF, if_index);
+
+ if (gw_osa) {
+ switch (gw_osa->u.sa.sa_family) {
+ case AF_INET:
+ mnl_attr_put_u32(nlh, RTA_GATEWAY, gw_osa->u.sin.sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ mnl_attr_put(nlh, RTA_GATEWAY, sizeof(struct in6_addr), &gw_osa->u.sin6.sin6_addr);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "mnl_socket_sendto\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+#endif /* if ENABLE_LIBMNL */
+
+/*! Allocate a new netdev object.
+ * \param[in] ctx talloc context to use as a parent when allocating the netdev object
+ * \param[in] name A name providen to identify the netdev object
+ * \returns newly allocated netdev object on success; NULL on error
+ */
+struct osmo_netdev *osmo_netdev_alloc(void *ctx, const char *name)
+{
+ struct osmo_netdev *netdev;
+
+ netdev = talloc_zero(ctx, struct osmo_netdev);
+ if (!netdev)
+ return NULL;
+
+ netdev->name = talloc_strdup(netdev, name);
+
+ llist_add_tail(&netdev->entry, &g_netdev_list);
+ return netdev;
+}
+
+/*! Free an allocated netdev object.
+ * \param[in] netdev The netdev object to free
+ */
+void osmo_netdev_free(struct osmo_netdev *netdev)
+{
+ if (!netdev)
+ return;
+ if (osmo_netdev_is_registered(netdev))
+ osmo_netdev_unregister(netdev);
+ llist_del(&netdev->entry);
+ talloc_free(netdev);
+}
+
+/*! Start managing the network device referenced by the netdev object.
+ * \param[in] netdev The netdev object to open
+ * \returns 0 on success; negative on error
+ */
+int osmo_netdev_register(struct osmo_netdev *netdev)
+{
+ char ifnamebuf[IF_NAMESIZE];
+ struct osmo_netns_switch_state switch_state;
+ int rc = 0;
+
+ if (netdev->registered)
+ return -EALREADY;
+
+ netdev->netns_ctx = netdev_netns_ctx_get(netdev->netns_name ? : "");
+ if (!netdev->netns_ctx)
+ return -EFAULT;
+
+ NETDEV_NETNS_ENTER(netdev, &switch_state, "register");
+
+ if (!if_indextoname(netdev->ifindex, ifnamebuf)) {
+ rc = -ENODEV;
+ goto err_put_exit;
+ }
+ osmo_talloc_replace_string(netdev, &netdev->dev_name, ifnamebuf);
+
+#if ENABLE_LIBMNL
+ rc = netdev_mnl_request_initial_dump(netdev->netns_ctx->omnl, netdev->ifindex);
+#endif
+
+ NETDEV_NETNS_EXIT(netdev, &switch_state, "register");
+
+ netdev->registered = true;
+ return rc;
+
+err_put_exit:
+ NETDEV_NETNS_EXIT(netdev, &switch_state, "register");
+ netdev_netns_ctx_put(netdev->netns_ctx);
+ return rc;
+}
+
+/*! Unregister the netdev object (stop managing /moniutoring the interface)
+ * \param[in] netdev The netdev object to close
+ * \returns 0 on success; negative on error
+ */
+int osmo_netdev_unregister(struct osmo_netdev *netdev)
+{
+ if (!netdev->registered)
+ return -EALREADY;
+
+ netdev->if_up_known = false;
+ netdev->if_mtu_known = false;
+
+ netdev_netns_ctx_put(netdev->netns_ctx);
+ netdev->registered = false;
+ return 0;
+}
+
+/*! Retrieve whether the netdev object is in "registered" state.
+ * \param[in] netdev The netdev object to check
+ * \returns true if in state "registered"; false otherwise
+ */
+bool osmo_netdev_is_registered(struct osmo_netdev *netdev)
+{
+ return netdev->registered;
+}
+
+/*! Set private user data pointer on the netdev object.
+ * \param[in] netdev The netdev object where the field is set
+ */
+void osmo_netdev_set_priv_data(struct osmo_netdev *netdev, void *priv_data)
+{
+ netdev->priv_data = priv_data;
+}
+
+/*! Get private user data pointer from the netdev object.
+ * \param[in] netdev The netdev object from where to retrieve the field
+ * \returns The current value of the priv_data field.
+ */
+void *osmo_netdev_get_priv_data(struct osmo_netdev *netdev)
+{
+ return netdev->priv_data;
+}
+
+/*! Set data_ind_cb callback, called when a new packet is received on the network interface.
+ * \param[in] netdev The netdev object where the field is set
+ * \param[in] data_ind_cb the user provided function to be called when the link status (UP/DOWN) changes
+ */
+void osmo_netdev_set_ifupdown_ind_cb(struct osmo_netdev *netdev, osmo_netdev_ifupdown_ind_cb_t ifupdown_ind_cb)
+{
+ netdev->ifupdown_ind_cb = ifupdown_ind_cb;
+}
+
+/*! Set dev_name_chg_cb callback, called when a change in the network name is detected
+ * \param[in] netdev The netdev object where the field is set
+ * \param[in] dev_name_chg_cb the user provided function to be called when a the interface is renamed
+ */
+void osmo_netdev_set_dev_name_chg_cb(struct osmo_netdev *netdev, osmo_netdev_dev_name_chg_cb_t dev_name_chg_cb)
+{
+ netdev->dev_name_chg_cb = dev_name_chg_cb;
+}
+
+/*! Set mtu_chg_cb callback, called when a change in the network name is detected
+ * \param[in] netdev The netdev object where the field is set
+ * \param[in] mtu_chg_cb the user provided function to be called when the configured MTU at the interface changes
+ */
+void osmo_netdev_set_mtu_chg_cb(struct osmo_netdev *netdev, osmo_netdev_mtu_chg_cb_t mtu_chg_cb)
+{
+ netdev->mtu_chg_cb = mtu_chg_cb;
+}
+
+/*! Get name used to identify the netdev object.
+ * \param[in] netdev The netdev object from where to retrieve the field
+ * \returns The current value of the name used to identify the netdev object
+ */
+const char *osmo_netdev_get_name(const struct osmo_netdev *netdev)
+{
+ return netdev->name;
+}
+
+/*! Set (specify) interface index identifying the network interface to manage
+ * \param[in] netdev The netdev object where the field is set
+ * \param[in] ifindex The interface index identifying the interface
+ * \returns 0 on success; negative on error
+ *
+ * The ifindex, together with the netns_name (see
+ * osmo_netdev_netns_name_set()), form together the key identifiers of a
+ * network interface to manage.
+ * This field is used during osmo_netdev_register() time, and hence must be set
+ * before calling that API, and cannot be changed when the netdev object is in
+ * "registered" state.
+ */
+int osmo_netdev_set_ifindex(struct osmo_netdev *netdev, unsigned int ifindex)
+{
+ if (netdev->registered)
+ return -EALREADY;
+ netdev->ifindex = ifindex;
+ return 0;
+}
+
+/*! Get interface index identifying the interface managed by netdev
+ * \param[in] netdev The netdev object from where to retrieve the field
+ * \returns The current value of the configured netdev interface ifindex to use (0 = unset)
+ */
+unsigned int osmo_netdev_get_ifindex(const struct osmo_netdev *netdev)
+{
+ return netdev->ifindex;
+}
+
+/*! Set (specify) name of the network namespace where the network interface to manage is located
+ * \param[in] netdev The netdev object where the field is set
+ * \param[in] netns_name The network namespace where the network interface is located
+ * \returns 0 on success; negative on error
+ *
+ * The netns_name, together with the ifindex (see
+ * osmo_netdev_ifindex_set()), form together the key identifiers of a
+ * network interface to manage.
+ * This field is used during osmo_netdev_register() time, and hence must be set
+ * before calling that API, and cannot be changed when the netdev object is in
+ * "registered" state.
+ * If left as NULL (default), the management will be done in the current network namespace.
+ */
+int osmo_netdev_set_netns_name(struct osmo_netdev *netdev, const char *netns_name)
+{
+ if (netdev->registered)
+ return -EALREADY;
+ osmo_talloc_replace_string(netdev, &netdev->netns_name, netns_name);
+ return 0;
+}
+
+/*! Get name of network namespace used when opening the netdev interface
+ * \param[in] netdev The netdev object from where to retrieve the field
+ * \returns The current value of the configured network namespace
+ */
+const char *osmo_netdev_get_netns_name(const struct osmo_netdev *netdev)
+{
+ return netdev->netns_name;
+}
+
+/*! Get name used to name the network interface created by the netdev object
+ * \param[in] netdev The netdev object from where to retrieve the field
+ * \returns The interface name (or NULL if unknown)
+ *
+ * This information is retrieved internally once the netdev object enters the
+ * "registered" state. Hence, when not registered NULL can be returned.
+ */
+const char *osmo_netdev_get_dev_name(const struct osmo_netdev *netdev)
+{
+ return netdev->dev_name;
+}
+
+/*! Bring netdev interface UP or DOWN.
+ * \param[in] netdev The netdev object managing the netdev interface
+ * \param[in] ifupdown true to set the interface UP, false to set it DOWN
+ * \returns 0 on succes; negative on error.
+ */
+int osmo_netdev_ifupdown(struct osmo_netdev *netdev, bool ifupdown)
+{
+ struct osmo_netns_switch_state switch_state;
+ int rc;
+
+ if (!netdev->registered)
+ return -ENODEV;
+
+ LOGNETDEV(netdev, LOGL_NOTICE, "Bringing dev %s %s\n",
+ netdev->dev_name, ifupdown ? "UP" : "DOWN");
+
+ NETDEV_NETNS_ENTER(netdev, &switch_state, "ifupdown");
+
+#if ENABLE_LIBMNL
+ rc = netdev_mnl_set_ifupdown(netdev->netns_ctx->omnl, netdev->ifindex,
+ netdev->dev_name, ifupdown);
+#else
+ LOGNETDEV(netdev, LOGL_ERROR, "%s: NOT SUPPORTED. Build libosmocore with --enable-libmnl.\n", __func__);
+ rc = -ENOTSUP;
+#endif
+
+ NETDEV_NETNS_EXIT(netdev, &switch_state, "ifupdown");
+
+ return rc;
+}
+
+/*! Add IP address to netdev interface
+ * \param[in] netdev The netdev object managing the netdev interface
+ * \param[in] addr The local address to set on the interface
+ * \param[in] prefixlen The network prefix of addr
+ * \returns 0 on succes; negative on error.
+ */
+int osmo_netdev_add_addr(struct osmo_netdev *netdev, const struct osmo_sockaddr *addr, uint8_t prefixlen)
+{
+ struct osmo_netns_switch_state switch_state;
+ char buf[INET6_ADDRSTRLEN];
+ int rc;
+
+ if (!netdev->registered)
+ return -ENODEV;
+
+ LOGNETDEV(netdev, LOGL_NOTICE, "Adding address %s/%u to dev %s\n",
+ osmo_sockaddr_ntop(&addr->u.sa, buf), prefixlen, netdev->dev_name);
+
+ NETDEV_NETNS_ENTER(netdev, &switch_state, "Add address");
+
+#if ENABLE_LIBMNL
+ rc = netdev_mnl_add_addr(netdev->netns_ctx->omnl, netdev->ifindex, addr, prefixlen);
+#else
+ LOGNETDEV(netdev, LOGL_ERROR, "%s: NOT SUPPORTED. Build libosmocore with --enable-libmnl.\n", __func__);
+ rc = -ENOTSUP;
+#endif
+
+ NETDEV_NETNS_EXIT(netdev, &switch_state, "Add address");
+
+ return rc;
+}
+
+/*! Add IP route to netdev interface
+ * \param[in] netdev The netdev object managing the netdev interface
+ * \param[in] dst_addr The destination address of the route
+ * \param[in] dst_prefixlen The network prefix of dst_addr
+ * \param[in] gw_addr The gateway address. Optional, can be NULL.
+ * \returns 0 on succes; negative on error.
+ */
+int osmo_netdev_add_route(struct osmo_netdev *netdev, const struct osmo_sockaddr *dst_addr, uint8_t dst_prefixlen, const struct osmo_sockaddr *gw_addr)
+{
+ struct osmo_netns_switch_state switch_state;
+ char buf_dst[INET6_ADDRSTRLEN];
+ char buf_gw[INET6_ADDRSTRLEN];
+ int rc;
+
+ if (!netdev->registered)
+ return -ENODEV;
+
+ LOGNETDEV(netdev, LOGL_NOTICE, "Adding route %s/%u%s%s dev %s\n",
+ osmo_sockaddr_ntop(&dst_addr->u.sa, buf_dst), dst_prefixlen,
+ gw_addr ? " via " : "",
+ gw_addr ? osmo_sockaddr_ntop(&gw_addr->u.sa, buf_gw) : "",
+ netdev->dev_name);
+
+ NETDEV_NETNS_ENTER(netdev, &switch_state, "Add route");
+
+#if ENABLE_LIBMNL
+ rc = netdev_mnl_add_route(netdev->netns_ctx->omnl, netdev->ifindex, dst_addr, dst_prefixlen, gw_addr);
+#else
+ LOGNETDEV(netdev, LOGL_ERROR, "%s: NOT SUPPORTED. Build libosmocore with --enable-libmnl.\n", __func__);
+ rc = -ENOTSUP;
+#endif
+
+ NETDEV_NETNS_EXIT(netdev, &switch_state, "Add route");
+
+ return rc;
+}
+
+#endif /* (!EMBEDDED) */
+
+/*! @} */
diff --git a/src/core/netns.c b/src/core/netns.c
new file mode 100644
index 00000000..c1d75b1e
--- /dev/null
+++ b/src/core/netns.c
@@ -0,0 +1,208 @@
+
+/* Network namespace convenience functions
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "config.h"
+
+/*! \addtogroup netns
+ * @{
+ * Network namespace convenience functions
+ *
+ * \file netns.c */
+
+#if defined(__linux__)
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sched.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/netns.h>
+
+#define NETNS_PREFIX_PATH "/var/run/netns"
+#define NETNS_CURRENT_PATH "/proc/self/ns/net"
+
+/*! Open a file descriptor for the current network namespace.
+ * \returns fd of the current network namespace on success; negative in case of error
+ */
+static int netns_open_current_fd(void)
+{
+ int fd;
+ /* store the default namespace for later reference */
+ if ((fd = open(NETNS_CURRENT_PATH, O_RDONLY)) < 0)
+ return -errno;
+ return fd;
+}
+
+/*! switch to a (non-default) namespace, store existing signal mask in oldmask.
+ * \param[in] nsfd file descriptor representing the namespace to which we shall switch
+ * \param[out] state caller-provided memory location to which state of previous netns is stored
+ * \returns 0 on success; negative on error */
+int osmo_netns_switch_enter(int nsfd, struct osmo_netns_switch_state *state)
+{
+ sigset_t intmask;
+ int rc;
+
+ state->prev_nsfd = -1;
+
+ if (sigfillset(&intmask) < 0)
+ return -errno;
+ if ((rc = sigprocmask(SIG_BLOCK, &intmask, &state->prev_sigmask)) != 0)
+ return -rc;
+ state->prev_nsfd = netns_open_current_fd();
+
+ if (setns(nsfd, CLONE_NEWNET) < 0) {
+ /* restore old mask if we couldn't switch the netns */
+ sigprocmask(SIG_SETMASK, &state->prev_sigmask, NULL);
+ close(state->prev_nsfd);
+ state->prev_nsfd = -1;
+ return -errno;
+ }
+ return 0;
+}
+
+/*! switch back to the previous namespace, restoring signal mask.
+ * \param[in] state information about previous netns, filled by osmo_netns_switch_enter()
+ * \returns 0 on successs; negative on error */
+int osmo_netns_switch_exit(struct osmo_netns_switch_state *state)
+{
+ if (state->prev_nsfd < 0)
+ return -EINVAL;
+
+ int rc;
+ if (setns(state->prev_nsfd, CLONE_NEWNET) < 0)
+ return -errno;
+
+ close(state->prev_nsfd);
+ state->prev_nsfd = -1;
+
+ if ((rc = sigprocmask(SIG_SETMASK, &state->prev_sigmask, NULL)) != 0)
+ return -rc;
+ return 0;
+}
+
+static int create_netns(const char *name)
+{
+ char path[MAXPATHLEN];
+ sigset_t intmask, oldmask;
+ int fd, prev_nsfd;
+ int rc, rc2;
+
+ /* create /var/run/netns, if it doesn't exist already */
+ rc = mkdir(NETNS_PREFIX_PATH, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
+ if (rc < 0 && errno != EEXIST)
+ return rc;
+
+ /* create /var/run/netns/[name], if it doesn't exist already */
+ rc = snprintf(path, sizeof(path), "%s/%s", NETNS_PREFIX_PATH, name);
+ if (rc >= sizeof(path))
+ return -ENAMETOOLONG;
+ fd = open(path, O_RDONLY|O_CREAT|O_EXCL, 0);
+ if (fd < 0)
+ return -errno;
+ if (close(fd) < 0)
+ return -errno;
+
+ /* mask off all signals, store old signal mask */
+ if (sigfillset(&intmask) < 0)
+ return -errno;
+ if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
+ return -rc;
+
+ prev_nsfd = netns_open_current_fd();
+ if (prev_nsfd < 0)
+ return prev_nsfd;
+
+ /* create a new network namespace */
+ if (unshare(CLONE_NEWNET) < 0) {
+ rc = -errno;
+ goto restore_sigmask;
+ }
+ if (mount(NETNS_CURRENT_PATH, path, "none", MS_BIND, NULL) < 0) {
+ rc = -errno;
+ goto restore_sigmask;
+ }
+
+ /* switch back to previous namespace */
+ if (setns(prev_nsfd, CLONE_NEWNET) < 0) {
+ rc = -errno;
+ goto restore_sigmask;
+ }
+
+restore_sigmask:
+ close(prev_nsfd);
+
+ /* restore process mask */
+ if ((rc2 = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0)
+ return -rc2;
+
+ /* might have been set above in case mount fails */
+ if (rc < 0)
+ return rc;
+
+ /* finally, open the created namespace file descriptor from previous ns */
+ if ((fd = open(path, O_RDONLY)) < 0)
+ return -errno;
+
+ return fd;
+}
+
+/*! Open a file descriptor for the network namespace with provided name.
+ * Creates /var/run/netns/ directory if it doesn't exist already.
+ * \param[in] name Name of the network namespace (in /var/run/netns/)
+ * \returns File descriptor of network namespace; negative in case of error
+ */
+int osmo_netns_open_fd(const char *name)
+{
+ int rc;
+ int fd;
+ char path[MAXPATHLEN];
+
+ /* path = /var/run/netns/[name] */
+ rc = snprintf(path, sizeof(path), "%s/%s", NETNS_PREFIX_PATH, name);
+ if (rc >= sizeof(path))
+ return -ENAMETOOLONG;
+
+ /* If netns already exists, simply open it: */
+ fd = open(path, O_RDONLY);
+ if (fd >= 0)
+ return fd;
+
+ /* The netns doesn't exist yet, let's create it: */
+ fd = create_netns(name);
+ return fd;
+}
+
+#endif /* defined(__linux__) */
+
+/*! @} */
diff --git a/src/core/osmo_io.c b/src/core/osmo_io.c
new file mode 100644
index 00000000..b589cb71
--- /dev/null
+++ b/src/core/osmo_io.c
@@ -0,0 +1,1007 @@
+/*
+ * New osmocom async I/O API.
+ *
+ * (C) 2022-2024 by Harald Welte <laforge@osmocom.org>
+ * (C) 2022-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Daniel Willmann <dwillmann@sysmocom.de>
+ *
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "../config.h"
+#ifndef EMBEDDED
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <talloc.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <osmocom/core/osmo_io.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+
+#include "osmo_io_internal.h"
+
+/*! \addtogroup osmo_io
+ * @{
+ *
+ * \file osmo_io.c */
+
+/*! This environment variable can be set to manually set the backend used in osmo_io */
+#define OSMO_IO_BACKEND_ENV "LIBOSMO_IO_BACKEND"
+
+const struct value_string osmo_io_backend_names[] = {
+ { OSMO_IO_BACKEND_POLL, "poll" },
+ { OSMO_IO_BACKEND_IO_URING, "io_uring" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_iofd_mode_names[] = {
+ { OSMO_IO_FD_MODE_READ_WRITE, "read/write" },
+ { OSMO_IO_FD_MODE_RECVFROM_SENDTO, "recvfrom/sendto" },
+ { OSMO_IO_FD_MODE_RECVMSG_SENDMSG, "recvmsg/sendmsg" },
+ { 0, NULL }
+};
+
+static enum osmo_io_backend g_io_backend;
+
+/* Used by some tests, can't be static */
+struct iofd_backend_ops osmo_iofd_ops;
+
+#if defined(HAVE_URING)
+void osmo_iofd_uring_init(void);
+#endif
+
+/*! initialize osmo_io for the current thread */
+void osmo_iofd_init(void)
+{
+ switch (g_io_backend) {
+ case OSMO_IO_BACKEND_POLL:
+ break;
+#if defined(HAVE_URING)
+ case OSMO_IO_BACKEND_IO_URING:
+ osmo_iofd_uring_init();
+ break;
+#endif
+ default:
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+/* ensure main thread always has pre-initialized osmo_io
+ * priority 103: run after on_dso_load_select */
+static __attribute__((constructor(103))) void on_dso_load_osmo_io(void)
+{
+ char *backend = getenv(OSMO_IO_BACKEND_ENV);
+ if (backend == NULL)
+ backend = OSMO_IO_BACKEND_DEFAULT;
+
+ if (!strcmp("POLL", backend)) {
+ g_io_backend = OSMO_IO_BACKEND_POLL;
+ osmo_iofd_ops = iofd_poll_ops;
+#if defined(HAVE_URING)
+ } else if (!strcmp("IO_URING", backend)) {
+ g_io_backend = OSMO_IO_BACKEND_IO_URING;
+ osmo_iofd_ops = iofd_uring_ops;
+#endif
+ } else {
+ fprintf(stderr, "Invalid osmo_io backend requested: \"%s\"\nCheck the environment variable %s\n", backend, OSMO_IO_BACKEND_ENV);
+ exit(1);
+ }
+
+ OSMO_ASSERT(osmo_iofd_ops.close);
+ OSMO_ASSERT(osmo_iofd_ops.register_fd);
+ OSMO_ASSERT(osmo_iofd_ops.unregister_fd);
+ OSMO_ASSERT(osmo_iofd_ops.write_enable);
+ OSMO_ASSERT(osmo_iofd_ops.write_disable);
+ OSMO_ASSERT(osmo_iofd_ops.read_enable);
+ OSMO_ASSERT(osmo_iofd_ops.read_disable);
+ OSMO_ASSERT(osmo_iofd_ops.notify_connected);
+
+ osmo_iofd_init();
+}
+
+/*! Allocate the msghdr.
+ * \param[in] iofd the osmo_io file structure
+ * \param[in] action the action this msg(hdr) is for (read, write, ..)
+ * \param[in] msg the msg buffer to use. Will allocate a new one if NULL
+ * \param[in] cmsg_size size (in bytes) of iofd_msghdr.cmsg buffer. Can be 0 if cmsg is not used.
+ * \returns the newly allocated msghdr or NULL in case of error */
+struct iofd_msghdr *iofd_msghdr_alloc(struct osmo_io_fd *iofd, enum iofd_msg_action action, struct msgb *msg,
+ size_t cmsg_size)
+{
+ bool free_msg = false;
+ struct iofd_msghdr *hdr;
+
+ if (!msg) {
+ msg = iofd_msgb_alloc(iofd);
+ if (!msg)
+ return NULL;
+ free_msg = true;
+ } else {
+ talloc_steal(iofd, msg);
+ }
+
+ hdr = talloc_zero_size(iofd, sizeof(struct iofd_msghdr) + cmsg_size);
+ if (!hdr) {
+ if (free_msg)
+ talloc_free(msg);
+ return NULL;
+ }
+
+ hdr->action = action;
+ hdr->iofd = iofd;
+ hdr->msg = msg;
+
+ return hdr;
+}
+
+/*! Free the msghdr.
+ * \param[in] msghdr the msghdr to free
+ */
+void iofd_msghdr_free(struct iofd_msghdr *msghdr)
+{
+ /* msghdr->msg is never owned by msghdr, it will either be freed in the send path or
+ * or passed on to the read callback which takes ownership. */
+ talloc_free(msghdr);
+}
+
+/*! convenience wrapper to call msgb_alloc with parameters from osmo_io_fd */
+struct msgb *iofd_msgb_alloc(struct osmo_io_fd *iofd)
+{
+ uint16_t headroom = iofd->msgb_alloc.headroom;
+
+ OSMO_ASSERT(iofd->msgb_alloc.size < 0xffff - headroom);
+ return msgb_alloc_headroom_c(iofd, iofd->msgb_alloc.size + headroom, headroom, "osmo_io_msgb");
+}
+
+/*! return the pending msgb in iofd or NULL if there is none*/
+struct msgb *iofd_msgb_pending(struct osmo_io_fd *iofd)
+{
+ struct msgb *msg = NULL;
+
+ msg = iofd->pending;
+ iofd->pending = NULL;
+
+ return msg;
+}
+
+/*! Return the pending msgb or allocate and return a new one */
+struct msgb *iofd_msgb_pending_or_alloc(struct osmo_io_fd *iofd)
+{
+ struct msgb *msg = NULL;
+
+ msg = iofd_msgb_pending(iofd);
+ if (!msg)
+ msg = iofd_msgb_alloc(iofd);
+
+ return msg;
+}
+
+/*! Enqueue a message to be sent.
+ *
+ * Enqueues the message at the back of the queue provided there is enough space.
+ * \param[in] iofd the file descriptor
+ * \param[in] msghdr the message to enqueue
+ * \returns 0 if the message was enqueued succcessfully,
+ * -ENOSPC if the queue already contains the maximum number of messages
+ */
+int iofd_txqueue_enqueue(struct osmo_io_fd *iofd, struct iofd_msghdr *msghdr)
+{
+ if (iofd->tx_queue.current_length >= iofd->tx_queue.max_length)
+ return -ENOSPC;
+
+ llist_add_tail(&msghdr->list, &iofd->tx_queue.msg_queue);
+ iofd->tx_queue.current_length++;
+
+ if (iofd->tx_queue.current_length == 1 && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ osmo_iofd_ops.write_enable(iofd);
+
+ return 0;
+}
+
+/*! Enqueue a message at the front.
+ *
+ * Used to enqueue a msgb from a partial send again. This function will always
+ * enqueue the message, even if the maximum number of messages is reached.
+ * \param[in] iofd the file descriptor
+ * \param[in] msghdr the message to enqueue
+ */
+void iofd_txqueue_enqueue_front(struct osmo_io_fd *iofd, struct iofd_msghdr *msghdr)
+{
+ llist_add(&msghdr->list, &iofd->tx_queue.msg_queue);
+ iofd->tx_queue.current_length++;
+
+ if (iofd->tx_queue.current_length == 1 && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ osmo_iofd_ops.write_enable(iofd);
+}
+
+/*! Dequeue a message from the front.
+ *
+ * \param[in] iofd the file descriptor
+ * \returns the msghdr from the front of the queue or NULL if the queue is empty
+ */
+struct iofd_msghdr *iofd_txqueue_dequeue(struct osmo_io_fd *iofd)
+{
+ struct llist_head *lh;
+
+ if (iofd->tx_queue.current_length == 0)
+ return NULL;
+
+ lh = iofd->tx_queue.msg_queue.next;
+
+ OSMO_ASSERT(lh);
+ iofd->tx_queue.current_length--;
+ llist_del(lh);
+
+ if (iofd->tx_queue.current_length == 0)
+ osmo_iofd_ops.write_disable(iofd);
+
+ return llist_entry(lh, struct iofd_msghdr, list);
+}
+
+/*! Handle segmentation of the msg. If this function returns *_HANDLE_ONE or MORE then the data in msg will contain
+ * one complete message.
+ * If there are bytes left over, *pending_out will point to a msgb with the remaining data.
+*/
+static enum iofd_seg_act iofd_handle_segmentation(struct osmo_io_fd *iofd, struct msgb *msg, struct msgb **pending_out)
+{
+ int extra_len, received_len;
+ struct msgb *msg_pending;
+
+ /* Save the start of message before segmentation_cb (which could change it) */
+ uint8_t *data = msg->data;
+
+ received_len = msgb_length(msg);
+
+ if (!iofd->io_ops.segmentation_cb) {
+ *pending_out = NULL;
+ return IOFD_SEG_ACT_HANDLE_ONE;
+ }
+
+ int expected_len = iofd->io_ops.segmentation_cb(msg);
+ if (expected_len == -EAGAIN) {
+ goto defer;
+ } else if (expected_len < 0) {
+ /* Something is wrong, skip this msgb */
+ LOGPIO(iofd, LOGL_ERROR, "segmentation_cb returned error (%d), skipping msg of size %d\n",
+ expected_len, received_len);
+ *pending_out = NULL;
+ msgb_free(msg);
+ return IOFD_SEG_ACT_DEFER;
+ }
+
+ extra_len = received_len - expected_len;
+ /* No segmentation needed, return the whole msgb */
+ if (extra_len == 0) {
+ *pending_out = NULL;
+ return IOFD_SEG_ACT_HANDLE_ONE;
+ /* segment is incomplete */
+ } else if (extra_len < 0) {
+ goto defer;
+ }
+
+ /* msgb contains more than one segment */
+ /* Copy the trailing data over */
+ msg_pending = iofd_msgb_alloc(iofd);
+ memcpy(msgb_data(msg_pending), data + expected_len, extra_len);
+ msgb_put(msg_pending, extra_len);
+ *pending_out = msg_pending;
+
+ /* Trim the original msgb to size. Don't use msgb_trim because we need to reference
+ * msg->data from before it might have been modified by the segmentation_cb(). */
+ msg->tail = data + expected_len;
+ msg->len = msg->tail - msg->data;
+ return IOFD_SEG_ACT_HANDLE_MORE;
+
+defer:
+ *pending_out = msg;
+ return IOFD_SEG_ACT_DEFER;
+}
+
+/*! Restore message boundaries on read() and pass individual messages to the read callback
+ */
+void iofd_handle_segmented_read(struct osmo_io_fd *iofd, struct msgb *msg, int rc)
+{
+ int res;
+ struct msgb *pending = NULL;
+
+ OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_READ_WRITE);
+
+ if (rc <= 0) {
+ iofd->io_ops.read_cb(iofd, rc, msg);
+ return;
+ }
+
+ do {
+ res = iofd_handle_segmentation(iofd, msg, &pending);
+ if (res != IOFD_SEG_ACT_DEFER || rc < 0)
+ iofd->io_ops.read_cb(iofd, rc, msg);
+ if (res == IOFD_SEG_ACT_HANDLE_MORE)
+ msg = pending;
+ } while (res == IOFD_SEG_ACT_HANDLE_MORE);
+
+ OSMO_ASSERT(iofd->pending == NULL);
+ iofd->pending = pending;
+}
+
+/*! completion handler: Internal function called by osmo_io_backend after a given I/O operation has completed
+ * \param[in] iofd I/O file-descriptor on which I/O has completed
+ * \param[in] msg message buffer containing data related to completed I/O
+ * \param[in] rc result code with read size or error (-errno)
+ * \param[in] hdr serialized msghdr containing state of completed I/O */
+void iofd_handle_recv(struct osmo_io_fd *iofd, struct msgb *msg, int rc, struct iofd_msghdr *hdr)
+{
+ talloc_steal(iofd->msgb_alloc.ctx, msg);
+ switch (iofd->mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ iofd_handle_segmented_read(iofd, msg, rc);
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ iofd->io_ops.recvfrom_cb(iofd, rc, msg, &hdr->osa);
+ break;
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ iofd->io_ops.recvmsg_cb(iofd, rc, msg, &hdr->hdr);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+/*! completion handler: Internal function called by osmo_io_backend after a given I/O operation has completed
+ * \param[in] iofd I/O file-descriptor on which I/O has completed
+ * \param[in] rc return value of the I/O operation
+ * \param[in] msghdr serialized msghdr containing state of completed I/O
+ */
+void iofd_handle_send_completion(struct osmo_io_fd *iofd, int rc, struct iofd_msghdr *msghdr)
+{
+ struct msgb *msg = msghdr->msg;
+
+ /* Incomplete write */
+ if (rc > 0 && rc < msgb_length(msg)) {
+ /* Re-enqueue remaining data */
+ msgb_pull(msg, rc);
+ msghdr->iov[0].iov_len = msgb_length(msg);
+ iofd_txqueue_enqueue_front(iofd, msghdr);
+ return;
+ }
+
+ /* Reenqueue the complete msgb */
+ if (rc == -EAGAIN) {
+ iofd_txqueue_enqueue_front(iofd, msghdr);
+ return;
+ }
+
+ /* All other failure and success cases are handled here */
+ switch (msghdr->action) {
+ case IOFD_ACT_WRITE:
+ if (iofd->io_ops.write_cb)
+ iofd->io_ops.write_cb(iofd, rc, msg);
+ break;
+ case IOFD_ACT_SENDTO:
+ if (iofd->io_ops.sendto_cb)
+ iofd->io_ops.sendto_cb(iofd, rc, msg, &msghdr->osa);
+ break;
+ case IOFD_ACT_SENDMSG:
+ if (iofd->io_ops.sendmsg_cb)
+ iofd->io_ops.sendmsg_cb(iofd, rc, msg);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ msgb_free(msghdr->msg);
+ iofd_msghdr_free(msghdr);
+}
+
+/* Public functions */
+
+/*! Write a message to a file descriptor / connected socket.
+ * The osmo_io_fd must be using OSMO_IO_FD_MODE_READ_WRITE.
+ *
+ * Appends the message to the internal transmit queue for eventual non-blocking
+ * write to the underlying socket/file descriptor.
+ *
+ * If the function returns success (0) it will take ownership of the msgb and
+ * internally call msgb_free() after the write request completes.
+ * In case of an error, the msgb needs to be freed by the caller.
+ *
+ * \param[in] iofd osmo_io_fd file descriptor to write data to
+ * \param[in] msg message buffer containing the data to write
+ * \returns 0 in case of success; a negative value in case of error
+ */
+int osmo_iofd_write_msgb(struct osmo_io_fd *iofd, struct msgb *msg)
+{
+ int rc;
+
+ if (OSMO_UNLIKELY(msgb_length(msg) == 0)) {
+ LOGPIO(iofd, LOGL_ERROR, "Length is 0, rejecting msgb.\n");
+ return -EINVAL;
+ }
+
+ OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_READ_WRITE);
+
+ struct iofd_msghdr *msghdr = iofd_msghdr_alloc(iofd, IOFD_ACT_WRITE, msg, 0);
+ if (!msghdr)
+ return -ENOMEM;
+
+ msghdr->flags = MSG_NOSIGNAL;
+ msghdr->iov[0].iov_base = msgb_data(msghdr->msg);
+ msghdr->iov[0].iov_len = msgb_length(msghdr->msg);
+ msghdr->hdr.msg_iov = &msghdr->iov[0];
+ msghdr->hdr.msg_iovlen = 1;
+
+ rc = iofd_txqueue_enqueue(iofd, msghdr);
+ if (rc < 0) {
+ iofd_msghdr_free(msghdr);
+ LOGPIO(iofd, LOGL_ERROR, "enqueueing message failed (%d). Rejecting msgb\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/*! Send a message through an unconnected socket.
+ * The osmo_io_fd must be using OSMO_IO_FD_MODE_RECVFROM_SENDTO.
+ *
+ * Appends the message to the internal transmit queue for eventual non-blocking
+ * sendto on the underlying socket/file descriptor.
+ *
+ * If the function returns success (0), it will take ownership of the msgb and
+ * internally call msgb_free() after the sendto request completes.
+ * In case of an error the msgb needs to be freed by the caller.
+ *
+ * \param[in] iofd file descriptor to write to
+ * \param[in] msg message buffer to send
+ * \param[in] sendto_flags Flags to pass to the send call
+ * \param[in] dest destination address to send the message to
+ * \returns 0 in case of success; a negative value in case of error
+ */
+int osmo_iofd_sendto_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendto_flags, const struct osmo_sockaddr *dest)
+{
+ int rc;
+
+ if (OSMO_UNLIKELY(msgb_length(msg) == 0)) {
+ LOGPIO(iofd, LOGL_ERROR, "Length is 0, rejecting msgb.\n");
+ return -EINVAL;
+ }
+
+ OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_RECVFROM_SENDTO);
+
+ struct iofd_msghdr *msghdr = iofd_msghdr_alloc(iofd, IOFD_ACT_SENDTO, msg, 0);
+ if (!msghdr)
+ return -ENOMEM;
+
+ if (dest) {
+ msghdr->osa = *dest;
+ msghdr->hdr.msg_name = &msghdr->osa.u.sa;
+ msghdr->hdr.msg_namelen = osmo_sockaddr_size(&msghdr->osa);
+ }
+ msghdr->flags = sendto_flags;
+ msghdr->iov[0].iov_base = msgb_data(msghdr->msg);
+ msghdr->iov[0].iov_len = msgb_length(msghdr->msg);
+ msghdr->hdr.msg_iov = &msghdr->iov[0];
+ msghdr->hdr.msg_iovlen = 1;
+
+ rc = iofd_txqueue_enqueue(iofd, msghdr);
+ if (rc < 0) {
+ iofd_msghdr_free(msghdr);
+ LOGPIO(iofd, LOGL_ERROR, "enqueueing message failed (%d). Rejecting msgb\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/*! osmo_io equivalent of the sendmsg(2) socket API call.
+ * The osmo_io_fd must be using OSMO_IO_FD_MODE_RECVMSG_SENDMSG.
+ *
+ * Appends the message to the internal transmit queue for eventual non-blocking
+ * sendmsg on the underlying socket/file descriptor.
+ *
+ * If the function returns success (0), it will take ownership of the msgb and
+ * internally call msgb_free() after the sendmsg request completes.
+ * In case of an error the msgb needs to be freed by the caller.
+ *
+ * \param[in] iofd file descriptor to write to
+ * \param[in] msg message buffer to send; is used to fill msgh->iov[]
+ * \param[in] sendmsg_flags Flags to pass to the send call
+ * \param[in] msgh 'struct msghdr' for name/control/flags. iov must be empty!
+ * \returns 0 in case of success; a negative value in case of error
+ */
+int osmo_iofd_sendmsg_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendmsg_flags, const struct msghdr *msgh)
+{
+ int rc;
+ struct iofd_msghdr *msghdr;
+
+ if (OSMO_UNLIKELY(msgb_length(msg) == 0)) {
+ LOGPIO(iofd, LOGL_ERROR, "Length is 0, rejecting msgb.\n");
+ return -EINVAL;
+ }
+
+ OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_RECVMSG_SENDMSG);
+
+ if (OSMO_UNLIKELY(msgh->msg_namelen > sizeof(msghdr->osa))) {
+ LOGPIO(iofd, LOGL_ERROR, "osmo_iofd_sendmsg msg_namelen (%u) > supported %zu bytes\n",
+ msgh->msg_namelen, sizeof(msghdr->osa));
+ return -EINVAL;
+ }
+
+ if (OSMO_UNLIKELY(msgh->msg_iovlen)) {
+ LOGPIO(iofd, LOGL_ERROR, "osmo_iofd_sendmsg must have all in 'struct msgb', not in 'msg_iov'\n");
+ return -EINVAL;
+ }
+
+ msghdr = iofd_msghdr_alloc(iofd, IOFD_ACT_SENDMSG, msg, msgh->msg_controllen);
+ if (!msghdr)
+ return -ENOMEM;
+
+ /* copy over optional address */
+ if (msgh->msg_name) {
+ memcpy(&msghdr->osa, msgh->msg_name, msgh->msg_namelen);
+ msghdr->hdr.msg_name = &msghdr->osa.u.sa;
+ msghdr->hdr.msg_namelen = msgh->msg_namelen;
+ }
+
+ /* build iov from msgb */
+ msghdr->iov[0].iov_base = msgb_data(msghdr->msg);
+ msghdr->iov[0].iov_len = msgb_length(msghdr->msg);
+ msghdr->hdr.msg_iov = &msghdr->iov[0];
+ msghdr->hdr.msg_iovlen = 1;
+
+ /* copy over the cmsg from the msghdr */
+ if (msgh->msg_control && msgh->msg_controllen) {
+ msghdr->hdr.msg_control = msghdr->cmsg;
+ msghdr->hdr.msg_controllen = msgh->msg_controllen;
+ memcpy(msghdr->cmsg, msgh->msg_control, msgh->msg_controllen);
+ }
+
+ /* copy over msg_flags */
+ msghdr->hdr.msg_flags = sendmsg_flags;
+
+ rc = iofd_txqueue_enqueue(iofd, msghdr);
+ if (rc < 0) {
+ iofd_msghdr_free(msghdr);
+ LOGPIO(iofd, LOGL_ERROR, "enqueueing message failed (%d). Rejecting msgb\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int check_mode_callback_compat(enum osmo_io_fd_mode mode, const struct osmo_io_ops *ops)
+{
+ switch (mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ if (ops->recvfrom_cb || ops->sendto_cb)
+ return false;
+ if (ops->recvmsg_cb || ops->sendmsg_cb)
+ return false;
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ if (ops->read_cb || ops->write_cb)
+ return false;
+ if (ops->recvmsg_cb || ops->sendmsg_cb)
+ return false;
+ break;
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ if (ops->recvfrom_cb || ops->sendto_cb)
+ return false;
+ if (ops->read_cb || ops->write_cb)
+ return false;
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+/*! Allocate and setup a new iofd.
+ *
+ * Use this to create a new osmo_io_fd, specifying the osmo_io_fd_mode and osmo_io_ops, as well as optionally
+ * the file-descriptor number and a human-readable name. This is the first function you call for any
+ * osmo_io_fd.
+ *
+ * The created osmo_io_fd is not yet registered, and hence can not be used for any I/O until a subsequent
+ * call to osmo_iofd_register().
+ *
+ * The created osmo_io_fd is initialized with some default settings:
+ * * msgb allocations size: OSMO_IO_DEFAULT_MSGB_SIZE (1024)
+ * * msgb headroom: OSMO_IO_DEFAULT_MSGB_HEADROOM (128)
+ * * tx_queue depth: 32
+ *
+ * Those values may be adjusted from their defaults by using osmo_iofd_set_alloc_info() and
+ * osmo_iofd_set_txqueue_max_length() on the osmo_io_fd.
+ *
+ * \param[in] ctx the parent context from which to allocate
+ * \param[in] fd the underlying system file descriptor. May be -1 if not known yet; must then be specified
+ * at subsequent osmo_iofd_register() time.
+ * \param[in] name the optional human-readable name of the iofd; may be NULL
+ * \param[in] mode the osmo_io_fd_mode of the iofd, whether it should use read()/write(), sendto()/recvfrom()
+ * semantics.
+ * \param[in] ioops structure specifying the read/write/send/recv callbacks. Will be copied to the iofd, so
+ * the caller does not have to keep it around after issuing the osmo_iofd_setup call.
+ * \param[in] data opaque user data pointer accessible by the ioops callbacks
+ * \returns The newly allocated osmo_io_fd struct or NULL on failure
+ */
+struct osmo_io_fd *osmo_iofd_setup(const void *ctx, int fd, const char *name, enum osmo_io_fd_mode mode,
+ const struct osmo_io_ops *ioops, void *data)
+{
+ struct osmo_io_fd *iofd;
+
+ /* reject unsupported/unknown modes */
+ switch (mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ break;
+ default:
+ return NULL;
+ }
+
+ if (ioops && !check_mode_callback_compat(mode, ioops)) {
+ LOGP(DLIO, LOGL_ERROR, "iofd(%s): rejecting call-backs incompatible with mode %s\n",
+ name ? name : "unknown", osmo_iofd_mode_name(mode));
+ return NULL;
+ }
+
+ iofd = talloc_zero(ctx, struct osmo_io_fd);
+ if (!iofd)
+ return NULL;
+
+ iofd->fd = fd;
+ iofd->mode = mode;
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_CLOSED);
+
+ if (name)
+ iofd->name = talloc_strdup(iofd, name);
+
+ if (ioops)
+ iofd->io_ops = *ioops;
+
+ iofd->pending = NULL;
+
+ iofd->data = data;
+
+ iofd->msgb_alloc.ctx = ctx;
+ iofd->msgb_alloc.size = OSMO_IO_DEFAULT_MSGB_SIZE;
+ iofd->msgb_alloc.headroom = OSMO_IO_DEFAULT_MSGB_HEADROOM;
+
+ iofd->tx_queue.max_length = 32;
+ INIT_LLIST_HEAD(&iofd->tx_queue.msg_queue);
+
+ return iofd;
+}
+
+/*! Set the size of the control message buffer allocated when submitting recvmsg.
+ *
+ * If your osmo_io_fd is in OSMO_IO_FD_MODE_RECVMSG_SENDMSG mode, this API function can be used to tell the
+ * osmo_io code how much memory should be allocated for the cmsg (control message) buffer when performing
+ * recvmsg(). */
+int osmo_iofd_set_cmsg_size(struct osmo_io_fd *iofd, size_t cmsg_size)
+{
+ if (iofd->mode != OSMO_IO_FD_MODE_RECVMSG_SENDMSG)
+ return -EINVAL;
+
+ iofd->cmsg_size = cmsg_size;
+ return 0;
+}
+
+/*! Register the osmo_io_fd for active I/O.
+ *
+ * Calling this function will register a previously initialized osmo_io_fd for performing I/O.
+ *
+ * If the osmo_iofd has a read_cb/recvfrom_cb_recvmsg_cb set in its osmo_io_ops, read/receive will be
+ * automatically enabled and the respective call-back is called at any time data becomes available.
+ *
+ * If there is to-be-transmitted data in the transmit queue, write will be automatically enabled, allowing
+ * the transmit queue to be drained as soon as the fd/socket becomes writable.
+ *
+ * \param[in] iofd the iofd file descriptor
+ * \param[in] fd the system fd number that will be registered. If you did not yet specify the file descriptor
+ * number during osmo_fd_setup(), or if it has changed since then, you can state the [new] file descriptor
+ * number as argument. If you wish to proceed with the previously specified file descriptor number, pass -1.
+ * \returns zero on success, a negative value on error
+*/
+int osmo_iofd_register(struct osmo_io_fd *iofd, int fd)
+{
+ int rc = 0;
+
+ if (fd >= 0)
+ iofd->fd = fd;
+ else if (iofd->fd < 0) {
+ /* this might happen if both osmo_iofd_setup() and osmo_iofd_register() are called with -1 */
+ LOGPIO(iofd, LOGL_ERROR, "Cannot register io_fd using invalid fd == %d\n", iofd->fd);
+ return -EBADF;
+ }
+
+ rc = osmo_iofd_ops.register_fd(iofd);
+ if (rc)
+ return rc;
+
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_CLOSED);
+ if ((iofd->mode == OSMO_IO_FD_MODE_READ_WRITE && iofd->io_ops.read_cb) ||
+ (iofd->mode == OSMO_IO_FD_MODE_RECVFROM_SENDTO && iofd->io_ops.recvfrom_cb) ||
+ (iofd->mode == OSMO_IO_FD_MODE_RECVMSG_SENDMSG && iofd->io_ops.recvmsg_cb)) {
+ osmo_iofd_ops.read_enable(iofd);
+ }
+
+ if (iofd->tx_queue.current_length > 0)
+ osmo_iofd_ops.write_enable(iofd);
+
+ return rc;
+}
+
+/*! Unregister the given osmo_io_fd from osmo_io.
+ *
+ * After an osmo_io_fd has been successfully unregistered, it can no longer perform any I/O via osmo_io.
+ * However, it can be subsequently re-registered using osmo_iofd_register().
+ *
+ * \param[in] iofd the file descriptor
+ * \returns zero on success, a negative value on error
+ */
+int osmo_iofd_unregister(struct osmo_io_fd *iofd)
+{
+ return osmo_iofd_ops.unregister_fd(iofd);
+}
+
+/*! Retrieve the number of messages pending in the transmit queue.
+ *
+ * \param[in] iofd the file descriptor
+ */
+unsigned int osmo_iofd_txqueue_len(struct osmo_io_fd *iofd)
+{
+ return iofd->tx_queue.current_length;
+}
+
+/*! Clear the transmit queue of the given osmo_io_fd.
+ *
+ * This function frees all messages currently pending in the transmit queue
+ * \param[in] iofd the file descriptor
+ */
+void osmo_iofd_txqueue_clear(struct osmo_io_fd *iofd)
+{
+ struct iofd_msghdr *hdr;
+ while ((hdr = iofd_txqueue_dequeue(iofd))) {
+ msgb_free(hdr->msg);
+ iofd_msghdr_free(hdr);
+ }
+}
+
+/*! Free the given osmo_io_fd.
+ *
+ * The iofd will be automatically closed before via osmo_iofd_close() [which in turn will unregister
+ * it and clear any pending transmit queue items]. You must not reference the iofd
+ * after calling this function. However, it is safe to call this function from any of osmo_io
+ * call-backs; in this case, actual free will be internally delayed until that call-back completes.
+ *
+ * \param[in] iofd the file descriptor
+ */
+void osmo_iofd_free(struct osmo_io_fd *iofd)
+{
+ if (!iofd)
+ return;
+
+ osmo_iofd_close(iofd);
+
+ if (!IOFD_FLAG_ISSET(iofd, IOFD_FLAG_IN_CALLBACK)) {
+ talloc_free(iofd);
+ } else {
+ /* Prevent our parent context from freeing us prematurely */
+ talloc_steal(NULL, iofd);
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_TO_FREE);
+ }
+}
+
+/*! Close the given osmo_io_fd.
+ *
+ * This function closes the underlying fd, unregisters it from osmo_io and clears any messages in the tx
+ * queue. The iofd itself is not freed and can be assigned a new file descriptor with osmo_iofd_register()
+ * \param[in] iofd the file descriptor
+ * \returns 0 on success, a negative value otherwise
+ */
+int osmo_iofd_close(struct osmo_io_fd *iofd)
+{
+ int rc = 0;
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ return rc;
+
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_CLOSED);
+
+ /* Free pending msgs in tx queue */
+ osmo_iofd_txqueue_clear(iofd);
+ msgb_free(iofd->pending);
+
+ iofd->pending = NULL;
+
+ rc = osmo_iofd_ops.close(iofd);
+ iofd->fd = -1;
+ return rc;
+}
+
+/*! Set the size and headroom of the msgb allocated when receiving messages.
+ * \param[in] iofd the file descriptor
+ * \param[in] size the size of the msgb when receiving data
+ * \param[in] headroom the headroom of the msgb when receiving data
+ */
+void osmo_iofd_set_alloc_info(struct osmo_io_fd *iofd, unsigned int size, unsigned int headroom)
+{
+ iofd->msgb_alloc.headroom = headroom;
+ iofd->msgb_alloc.size = size;
+}
+
+/*! Set the maximum number of messages enqueued for sending.
+ * \param[in] iofd the file descriptor
+ * \param[in] size the maximum size of the transmit queue
+ */
+void osmo_iofd_set_txqueue_max_length(struct osmo_io_fd *iofd, unsigned int max_length)
+{
+ iofd->tx_queue.max_length = max_length;
+}
+
+/*! Retrieve the associated user-data from an osmo_io_fd.
+ *
+ * A call to this function will return the opaque user data pointer which was specified previously
+ * via osmo_iofd_setup() or via osmo_iofd_set_data().
+ *
+ * \param[in] iofd the file descriptor
+ * \returns the data that was previously set with \ref osmo_iofd_setup()
+ */
+void *osmo_iofd_get_data(const struct osmo_io_fd *iofd)
+{
+ return iofd->data;
+}
+
+/*! Set the associated user-data from an osmo_io_fd.
+ *
+ * Calling this function will set/overwrite the opaque user data pointer, which can later be retrieved using
+ * osmo_iofd_get_data().
+ *
+ * \param[in] iofd the file descriptor
+ * \param[in] data the data to set
+ */
+void osmo_iofd_set_data(struct osmo_io_fd *iofd, void *data)
+{
+ iofd->data = data;
+}
+
+/*! Retrieve the private number from an osmo_io_fd.
+ * Calling this function will retrieve the private user number previously set via osmo_iofd_set_priv_nr().
+ * \param[in] iofd the file descriptor
+ * \returns the private number that was previously set with \ref osmo_iofd_set_priv_nr()
+ */
+unsigned int osmo_iofd_get_priv_nr(const struct osmo_io_fd *iofd)
+{
+ return iofd->priv_nr;
+}
+
+/*! Set the private number of an osmo_io_fd.
+ * The priv_nr passed in via this call can later be retrieved via osmo_iofd_get_priv_nr(). It provides
+ * a way how additional context can be stored in the osmo_io_fd beyond the opaque 'data' pointer.
+ * \param[in] iofd the file descriptor
+ * \param[in] priv_nr the private number to set
+ */
+void osmo_iofd_set_priv_nr(struct osmo_io_fd *iofd, unsigned int priv_nr)
+{
+ iofd->priv_nr = priv_nr;
+}
+
+/*! Retrieve the underlying file descriptor from an osmo_io_fd.
+ * \param[in] iofd the file descriptor
+ * \returns the underlying file descriptor number */
+int osmo_iofd_get_fd(const struct osmo_io_fd *iofd)
+{
+ return iofd->fd;
+}
+
+/*! Retrieve the human-readable name of the given osmo_io_fd.
+ * \param[in] iofd the file descriptor
+ * \returns the name of the iofd as given in \ref osmo_iofd_setup() */
+const char *osmo_iofd_get_name(const struct osmo_io_fd *iofd)
+{
+ return iofd->name;
+}
+
+/*! Set the human-readable name of the file descriptor.
+ * The given name will be used as context by all related logging and future calls to osmo_iofd_get_name().
+ * \param[in] iofd the file descriptor
+ * \param[in] name the name to set on the file descriptor */
+void osmo_iofd_set_name(struct osmo_io_fd *iofd, const char *name)
+{
+ osmo_talloc_replace_string(iofd, &iofd->name, name);
+}
+
+/*! Set the osmo_io_ops calbacks for an osmo_io_fd.
+ * This function can be used to update/overwrite the call-back functions for the given osmo_io_fd; it
+ * replaces the currently-set call-back function pointers from a previous call to osmo_iofd_set_ioops()
+ * or the original osmo_iofd_setup().
+ * \param[in] iofd Target iofd file descriptor
+ * \param[in] ioops osmo_io_ops structure to be copied to the osmo_io_fd.
+ * \returns 0 on success, negative on error */
+int osmo_iofd_set_ioops(struct osmo_io_fd *iofd, const struct osmo_io_ops *ioops)
+{
+ if (!check_mode_callback_compat(iofd->mode, ioops)) {
+ LOGPIO(iofd, LOGL_ERROR, "rejecting call-backs incompatible with mode %s\n",
+ osmo_iofd_mode_name(iofd->mode));
+ return -EINVAL;
+ }
+
+ iofd->io_ops = *ioops;
+
+ switch (iofd->mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ if (iofd->io_ops.read_cb)
+ osmo_iofd_ops.read_enable(iofd);
+ else
+ osmo_iofd_ops.read_disable(iofd);
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ if (iofd->io_ops.recvfrom_cb)
+ osmo_iofd_ops.read_enable(iofd);
+ else
+ osmo_iofd_ops.read_disable(iofd);
+ break;
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ if (iofd->io_ops.recvmsg_cb)
+ osmo_iofd_ops.read_enable(iofd);
+ else
+ osmo_iofd_ops.read_disable(iofd);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ return 0;
+}
+
+/*! Retrieve the osmo_io_ops for an iofd.
+ * \param[in] iofd Target iofd file descriptor
+ * \param[in] ioops caller-allocated osmo_io_ops structure to be filled */
+void osmo_iofd_get_ioops(struct osmo_io_fd *iofd, struct osmo_io_ops *ioops)
+{
+ *ioops = iofd->io_ops;
+}
+
+/*! Request notification of the user if/when a client socket is connected.
+ * Calling this function will request osmo_io to notify the user (via
+ * write call-back) once a non-blocking outbound connect() of the
+ * socket completes.
+ *
+ * This only works for connection oriented sockets in either
+ * OSMO_IO_FD_MODE_READ_WRITE or OSMO_IO_FD_MODE_RECVMSG_SENDMSG mode.
+ *
+ * \param[in] iofd the file descriptor */
+void osmo_iofd_notify_connected(struct osmo_io_fd *iofd)
+{
+ OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_READ_WRITE ||
+ iofd->mode == OSMO_IO_FD_MODE_RECVMSG_SENDMSG);
+ osmo_iofd_ops.notify_connected(iofd);
+}
+
+/*! @} */
+
+#endif /* ifndef(EMBEDDED) */
diff --git a/src/core/osmo_io_internal.h b/src/core/osmo_io_internal.h
new file mode 100644
index 00000000..a4b0749d
--- /dev/null
+++ b/src/core/osmo_io_internal.h
@@ -0,0 +1,166 @@
+/*! \file osmo_io_internal.h */
+
+#pragma once
+
+#include <unistd.h>
+#include <stdbool.h>
+#include <netinet/sctp.h>
+
+#include <osmocom/core/osmo_io.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+
+#include "../config.h"
+
+#define OSMO_IO_DEFAULT_MSGB_SIZE 1024
+#define OSMO_IO_DEFAULT_MSGB_HEADROOM 128
+
+extern const struct iofd_backend_ops iofd_poll_ops;
+#define OSMO_IO_BACKEND_DEFAULT "POLL"
+
+#if defined(HAVE_URING)
+extern const struct iofd_backend_ops iofd_uring_ops;
+#endif
+
+struct iofd_backend_ops {
+ int (*register_fd)(struct osmo_io_fd *iofd);
+ int (*unregister_fd)(struct osmo_io_fd *iofd);
+ int (*close)(struct osmo_io_fd *iofd);
+ void (*write_enable)(struct osmo_io_fd *iofd);
+ void (*write_disable)(struct osmo_io_fd *iofd);
+ void (*read_enable)(struct osmo_io_fd *iofd);
+ void (*read_disable)(struct osmo_io_fd *iofd);
+ void (*notify_connected)(struct osmo_io_fd *iofd);
+};
+
+#define IOFD_FLAG_CLOSED (1<<0)
+#define IOFD_FLAG_IN_CALLBACK (1<<1)
+#define IOFD_FLAG_TO_FREE (1<<2)
+#define IOFD_FLAG_NOTIFY_CONNECTED (1<<3)
+#define IOFD_FLAG_FD_REGISTERED (1<<4)
+
+#define IOFD_FLAG_SET(iofd, flag) \
+ (iofd)->flags |= (flag)
+
+#define IOFD_FLAG_UNSET(iofd, flag) \
+ (iofd)->flags &= ~(flag)
+
+#define IOFD_FLAG_ISSET(iofd, flag) ((iofd)->flags & (flag))
+
+struct osmo_io_fd {
+ /*! linked list for internal management */
+ struct llist_head list;
+ /*! actual operating-system level file decriptor */
+ int fd;
+ /*! type of read/write mode to use */
+ enum osmo_io_fd_mode mode;
+
+ /*! flags to guard closing/freeing of iofd */
+ uint32_t flags;
+
+ /*! human-readable name to associte with fd */
+ char *name;
+
+ /*! send/recv (msg) callback functions */
+ struct osmo_io_ops io_ops;
+ /*! Pending msgb to keep partial data during segmentation */
+ struct msgb *pending;
+
+ /*! data pointer passed through to call-back function */
+ void *data;
+ /*! private number, extending \a data */
+ unsigned int priv_nr;
+
+ /*! size of iofd_msghdr.cmsg[] when allocated in recvmsg path */
+ size_t cmsg_size;
+
+ struct {
+ /*! talloc context from which to allocate msgb when reading */
+ const void *ctx;
+ /*! size of msgb to allocate (excluding headroom) */
+ unsigned int size;
+ /*! headroom to allocate when allocating msgb's */
+ unsigned int headroom;
+ } msgb_alloc;
+
+ struct {
+ /*! maximum length of write queue */
+ unsigned int max_length;
+ /*! current length of write queue */
+ unsigned int current_length;
+ /*! actual linked list implementing the transmit queue */
+ struct llist_head msg_queue;
+ } tx_queue;
+
+ union {
+ struct {
+ struct osmo_fd ofd;
+ } poll;
+ struct {
+ bool read_enabled;
+ bool write_enabled;
+ void *read_msghdr;
+ void *write_msghdr;
+ /* TODO: index into array of registered fd's? */
+ /* osmo_fd for non-blocking connect handling */
+ struct osmo_fd connect_ofd;
+ } uring;
+ } u;
+};
+
+enum iofd_msg_action {
+ IOFD_ACT_READ,
+ IOFD_ACT_WRITE,
+ IOFD_ACT_RECVFROM,
+ IOFD_ACT_SENDTO,
+ IOFD_ACT_RECVMSG,
+ IOFD_ACT_SENDMSG,
+};
+
+
+/*! serialized version of 'struct msghdr' employed by sendmsg/recvmsg */
+struct iofd_msghdr {
+ /*! entry into osmo_io_fd.tx_queue.msg_queue */
+ struct llist_head list;
+ enum iofd_msg_action action;
+ /*! the 'struct msghdr' we are wrapping/ecapsulating here */
+ struct msghdr hdr;
+ /*! socket address of the remote peer */
+ struct osmo_sockaddr osa;
+ /*! io-vector we need to pass as argument to sendmsg/recvmsg; is set up
+ * to point into msg below */
+ struct iovec iov[1];
+ /*! flags we pass as argument to sendmsg / recvmsg */
+ int flags;
+
+ /*! message-buffer containing data for this I/O operation */
+ struct msgb *msg;
+ /*! I/O file descriptor on which we perform this I/O operation */
+ struct osmo_io_fd *iofd;
+
+ /*! control message buffer for passing sctp_sndrcvinfo along */
+ char cmsg[0]; /* size is determined by iofd->cmsg_size on recvmsg, and by mcghdr->msg_controllen on sendmsg */
+};
+
+enum iofd_seg_act {
+ IOFD_SEG_ACT_HANDLE_ONE,
+ IOFD_SEG_ACT_HANDLE_MORE,
+ IOFD_SEG_ACT_DEFER,
+};
+
+struct iofd_msghdr *iofd_msghdr_alloc(struct osmo_io_fd *iofd, enum iofd_msg_action action, struct msgb *msg, size_t cmsg_size);
+void iofd_msghdr_free(struct iofd_msghdr *msghdr);
+
+struct msgb *iofd_msgb_alloc(struct osmo_io_fd *iofd);
+struct msgb *iofd_msgb_pending(struct osmo_io_fd *iofd);
+struct msgb *iofd_msgb_pending_or_alloc(struct osmo_io_fd *iofd);
+
+void iofd_handle_recv(struct osmo_io_fd *iofd, struct msgb *msg, int rc, struct iofd_msghdr *msghdr);
+void iofd_handle_send_completion(struct osmo_io_fd *iofd, int rc, struct iofd_msghdr *msghdr);
+void iofd_handle_segmented_read(struct osmo_io_fd *iofd, struct msgb *msg, int rc);
+
+int iofd_txqueue_enqueue(struct osmo_io_fd *iofd, struct iofd_msghdr *msghdr);
+void iofd_txqueue_enqueue_front(struct osmo_io_fd *iofd, struct iofd_msghdr *msghdr);
+struct iofd_msghdr *iofd_txqueue_dequeue(struct osmo_io_fd *iofd);
diff --git a/src/core/osmo_io_poll.c b/src/core/osmo_io_poll.c
new file mode 100644
index 00000000..c4bb376d
--- /dev/null
+++ b/src/core/osmo_io_poll.c
@@ -0,0 +1,201 @@
+/*! \file osmo_io_poll.c
+ * New osmocom async I/O API.
+ *
+ * (C) 2022 by Harald Welte <laforge@osmocom.org>
+ * (C) 2022-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Daniel Willmann <dwillmann@sysmocom.de>
+ *
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "../config.h"
+#ifndef EMBEDDED
+
+#include <errno.h>
+#include <stdio.h>
+#include <talloc.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+
+#include <osmocom/core/osmo_io.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+
+#include "osmo_io_internal.h"
+
+static void iofd_poll_ofd_cb_recvmsg_sendmsg(struct osmo_fd *ofd, unsigned int what)
+{
+ struct osmo_io_fd *iofd = ofd->data;
+ struct msgb *msg;
+ int rc, flags = 0;
+
+ if (what & OSMO_FD_READ) {
+ struct iofd_msghdr hdr;
+
+ msg = iofd_msgb_pending_or_alloc(iofd);
+ if (!msg) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not allocate msgb for reading\n");
+ OSMO_ASSERT(0);
+ }
+
+ hdr.msg = msg;
+ hdr.iov[0].iov_base = msg->tail;
+ hdr.iov[0].iov_len = msgb_tailroom(msg);
+ hdr.hdr = (struct msghdr) {
+ .msg_iov = &hdr.iov[0],
+ .msg_iovlen = 1,
+ .msg_name = &hdr.osa.u.sa,
+ .msg_namelen = sizeof(struct osmo_sockaddr),
+ };
+ if (iofd->mode == OSMO_IO_FD_MODE_RECVMSG_SENDMSG) {
+ hdr.hdr.msg_control = alloca(iofd->cmsg_size);
+ hdr.hdr.msg_controllen = iofd->cmsg_size;
+ }
+
+ rc = recvmsg(ofd->fd, &hdr.hdr, flags);
+ if (rc > 0)
+ msgb_put(msg, rc);
+
+ iofd_handle_recv(iofd, msg, (rc < 0 && errno > 0) ? -errno : rc, &hdr);
+ }
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ return;
+
+ if (what & OSMO_FD_WRITE) {
+ struct iofd_msghdr *msghdr = iofd_txqueue_dequeue(iofd);
+ if (msghdr) {
+ rc = sendmsg(ofd->fd, &msghdr->hdr, msghdr->flags);
+ iofd_handle_send_completion(iofd, (rc < 0 && errno > 0) ? -errno : rc, msghdr);
+ } else {
+ /* Socket is writable, but we have no data to send. A non-blocking/async
+ connect() is signalled this way. */
+ switch (iofd->mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ iofd->io_ops.write_cb(iofd, 0, NULL);
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ iofd->io_ops.sendto_cb(iofd, 0, NULL, NULL);
+ break;
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ iofd->io_ops.sendmsg_cb(iofd, 0, NULL);
+ break;
+ default:
+ break;
+ }
+ if (osmo_iofd_txqueue_len(iofd) == 0)
+ iofd_poll_ops.write_disable(iofd);
+ }
+ }
+}
+
+static int iofd_poll_ofd_cb_dispatch(struct osmo_fd *ofd, unsigned int what)
+{
+ struct osmo_io_fd *iofd = ofd->data;
+
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_IN_CALLBACK);
+ iofd_poll_ofd_cb_recvmsg_sendmsg(ofd, what);
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_IN_CALLBACK);
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_TO_FREE)) {
+ talloc_free(iofd);
+ return 0;
+ }
+
+ return 0;
+}
+
+static int iofd_poll_register(struct osmo_io_fd *iofd)
+{
+ struct osmo_fd *ofd = &iofd->u.poll.ofd;
+ int rc;
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_FD_REGISTERED))
+ return 0;
+ osmo_fd_setup(ofd, iofd->fd, 0, &iofd_poll_ofd_cb_dispatch, iofd, 0);
+ rc = osmo_fd_register(ofd);
+ if (!rc)
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_FD_REGISTERED);
+ return rc;
+}
+
+static int iofd_poll_unregister(struct osmo_io_fd *iofd)
+{
+ struct osmo_fd *ofd = &iofd->u.poll.ofd;
+
+ if (!IOFD_FLAG_ISSET(iofd, IOFD_FLAG_FD_REGISTERED))
+ return 0;
+ osmo_fd_unregister(ofd);
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_FD_REGISTERED);
+
+ return 0;
+}
+
+static int iofd_poll_close(struct osmo_io_fd *iofd)
+{
+ iofd_poll_unregister(iofd);
+ osmo_fd_close(&iofd->u.poll.ofd);
+
+ return 0;
+}
+
+static void iofd_poll_read_enable(struct osmo_io_fd *iofd)
+{
+ osmo_fd_read_enable(&iofd->u.poll.ofd);
+}
+
+static void iofd_poll_read_disable(struct osmo_io_fd *iofd)
+{
+ osmo_fd_read_disable(&iofd->u.poll.ofd);
+}
+
+static void iofd_poll_write_enable(struct osmo_io_fd *iofd)
+{
+ osmo_fd_write_enable(&iofd->u.poll.ofd);
+}
+
+static void iofd_poll_write_disable(struct osmo_io_fd *iofd)
+{
+ osmo_fd_write_disable(&iofd->u.poll.ofd);
+}
+
+static void iofd_poll_notify_connected(struct osmo_io_fd *iofd)
+{
+ int rc;
+
+ rc = iofd_poll_register(iofd);
+ if (rc < 0)
+ return;
+ osmo_fd_write_enable(&iofd->u.poll.ofd);
+}
+
+const struct iofd_backend_ops iofd_poll_ops = {
+ .register_fd = iofd_poll_register,
+ .unregister_fd = iofd_poll_unregister,
+ .close = iofd_poll_close,
+ .write_enable = iofd_poll_write_enable,
+ .write_disable = iofd_poll_write_disable,
+ .read_enable = iofd_poll_read_enable,
+ .read_disable = iofd_poll_read_disable,
+ .notify_connected = iofd_poll_notify_connected,
+};
+
+#endif /* ifndef EMBEDDED */
diff --git a/src/core/osmo_io_uring.c b/src/core/osmo_io_uring.c
new file mode 100644
index 00000000..569f1505
--- /dev/null
+++ b/src/core/osmo_io_uring.c
@@ -0,0 +1,532 @@
+/*! \file osmo_io_uring.c
+ * io_uring backend for osmo_io.
+ *
+ * (C) 2022-2023 by sysmocom s.f.m.c.
+ * Author: Daniel Willmann <daniel@sysmocom.de>
+ * (C) 2023-2024 by Harald Welte <laforge@osmocom.org>
+ *
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* TODO:
+ * Parameters:
+ * - number of simultaneous read/write in uring for given fd
+ *
+ */
+
+#include "../config.h"
+#if defined(__linux__)
+
+#include <stdio.h>
+#include <talloc.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <netinet/in.h>
+#include <netinet/sctp.h>
+#include <sys/eventfd.h>
+#include <liburing.h>
+
+#include <osmocom/core/osmo_io.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/socket.h>
+
+#include "osmo_io_internal.h"
+
+#define IOFD_URING_ENTRIES 4096
+
+struct osmo_io_uring {
+ struct osmo_fd event_ofd;
+ struct io_uring ring;
+};
+
+static __thread struct osmo_io_uring g_ring;
+
+static void iofd_uring_cqe(struct io_uring *ring);
+
+/*! read call-back for eventfd notifying us if entries are in the completion queue */
+static int iofd_uring_poll_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ struct io_uring *ring = ofd->data;
+ eventfd_t val;
+ int rc;
+
+ if (what & OSMO_FD_READ) {
+ rc = eventfd_read(ofd->fd, &val);
+ if (rc < 0) {
+ LOGP(DLIO, LOGL_ERROR, "eventfd_read() returned error\n");
+ return rc;
+ }
+
+ iofd_uring_cqe(ring);
+ }
+ if (what & OSMO_FD_WRITE)
+ OSMO_ASSERT(0);
+
+ return 0;
+}
+
+/*! initialize the uring and tie it into our event loop */
+void osmo_iofd_uring_init(void)
+{
+ int rc, evfd;
+
+ rc = io_uring_queue_init(IOFD_URING_ENTRIES, &g_ring.ring, 0);
+ if (rc < 0)
+ osmo_panic("failure during io_uring_queue_init(): %s\n", strerror(-rc));
+
+ rc = eventfd(0, 0);
+ if (rc < 0) {
+ io_uring_queue_exit(&g_ring.ring);
+ osmo_panic("failure creating eventfd(0, 0) for io_uring: %s\n", strerror(-rc));
+ }
+ evfd = rc;
+
+ osmo_fd_setup(&g_ring.event_ofd, evfd, OSMO_FD_READ, iofd_uring_poll_cb, &g_ring.ring, 0);
+ rc = osmo_fd_register(&g_ring.event_ofd);
+ if (rc < 0) {
+ close(evfd);
+ io_uring_queue_exit(&g_ring.ring);
+ osmo_panic("failure registering io_uring-eventfd as osmo_fd: %d\n", rc);
+ }
+ rc = io_uring_register_eventfd(&g_ring.ring, evfd);
+ if (rc < 0) {
+ osmo_fd_unregister(&g_ring.event_ofd);
+ close(evfd);
+ io_uring_queue_exit(&g_ring.ring);
+ osmo_panic("failure registering eventfd with io_uring: %s\n", strerror(-rc));
+ }
+}
+
+
+static void iofd_uring_submit_recv(struct osmo_io_fd *iofd, enum iofd_msg_action action)
+{
+ struct msgb *msg;
+ struct iofd_msghdr *msghdr;
+ struct io_uring_sqe *sqe;
+
+ msg = iofd_msgb_pending_or_alloc(iofd);
+ if (!msg) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not allocate msgb for reading\n");
+ OSMO_ASSERT(0);
+ }
+
+ msghdr = iofd_msghdr_alloc(iofd, action, msg, iofd->cmsg_size);
+ if (!msghdr) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not allocate msghdr for reading\n");
+ OSMO_ASSERT(0);
+ }
+
+ msghdr->iov[0].iov_base = msg->tail;
+ msghdr->iov[0].iov_len = msgb_tailroom(msg);
+
+ switch (action) {
+ case IOFD_ACT_READ:
+ break;
+ case IOFD_ACT_RECVMSG:
+ msghdr->hdr.msg_control = msghdr->cmsg;
+ msghdr->hdr.msg_controllen = iofd->cmsg_size;
+ /* fall-through */
+ case IOFD_ACT_RECVFROM:
+ msghdr->hdr.msg_iov = &msghdr->iov[0];
+ msghdr->hdr.msg_iovlen = 1;
+ msghdr->hdr.msg_name = &msghdr->osa.u.sa;
+ msghdr->hdr.msg_namelen = osmo_sockaddr_size(&msghdr->osa);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ sqe = io_uring_get_sqe(&g_ring.ring);
+ if (!sqe) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not get io_uring_sqe\n");
+ OSMO_ASSERT(0);
+ }
+
+ switch (action) {
+ case IOFD_ACT_READ:
+ io_uring_prep_readv(sqe, iofd->fd, msghdr->iov, 1, 0);
+ break;
+ case IOFD_ACT_RECVMSG:
+ case IOFD_ACT_RECVFROM:
+ io_uring_prep_recvmsg(sqe, iofd->fd, &msghdr->hdr, msghdr->flags);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ io_uring_sqe_set_data(sqe, msghdr);
+
+ io_uring_submit(&g_ring.ring);
+ /* NOTE: This only works if we have one read per fd */
+ iofd->u.uring.read_msghdr = msghdr;
+}
+
+/*! completion call-back for READ/RECVFROM */
+static void iofd_uring_handle_recv(struct iofd_msghdr *msghdr, int rc)
+{
+ struct osmo_io_fd *iofd = msghdr->iofd;
+ struct msgb *msg = msghdr->msg;
+
+ if (rc > 0)
+ msgb_put(msg, rc);
+
+ if (!IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ iofd_handle_recv(iofd, msg, rc, msghdr);
+
+ if (iofd->u.uring.read_enabled && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ iofd_uring_submit_recv(iofd, msghdr->action);
+ else
+ iofd->u.uring.read_msghdr = NULL;
+
+
+ iofd_msghdr_free(msghdr);
+}
+
+static int iofd_uring_submit_tx(struct osmo_io_fd *iofd);
+
+/*! completion call-back for WRITE/SENDTO */
+static void iofd_uring_handle_tx(struct iofd_msghdr *msghdr, int rc)
+{
+ struct osmo_io_fd *iofd = msghdr->iofd;
+
+ /* Detach msghdr from iofd. It might get freed here or it is freed during iofd_handle_send_completion().
+ * If there is pending data to send, iofd_uring_submit_tx() will attach it again.
+ * iofd_handle_send_completion() will invoke a callback function to signal the possibility of write/send.
+ * This callback function might close iofd, leading to the potential freeing of iofd->u.uring.write_msghdr if
+ * still attached. Since iofd_handle_send_completion() frees msghdr at the end of the function, detaching
+ * msghdr here prevents a double-free bug. */
+ if (iofd->u.uring.write_msghdr == msghdr)
+ iofd->u.uring.write_msghdr = NULL;
+
+ if (OSMO_UNLIKELY(IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))) {
+ msgb_free(msghdr->msg);
+ iofd_msghdr_free(msghdr);
+ } else {
+ iofd_handle_send_completion(iofd, rc, msghdr);
+ }
+
+ /* submit the next to-be-transmitted message for this file descriptor */
+ if (iofd->u.uring.write_enabled && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ iofd_uring_submit_tx(iofd);
+}
+
+/*! handle completion of a single I/O message */
+static void iofd_uring_handle_completion(struct iofd_msghdr *msghdr, int res)
+{
+ struct osmo_io_fd *iofd = msghdr->iofd;
+
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_IN_CALLBACK);
+
+ switch (msghdr->action) {
+ case IOFD_ACT_READ:
+ case IOFD_ACT_RECVFROM:
+ case IOFD_ACT_RECVMSG:
+ iofd_uring_handle_recv(msghdr, res);
+ break;
+ case IOFD_ACT_WRITE:
+ case IOFD_ACT_SENDTO:
+ case IOFD_ACT_SENDMSG:
+ iofd_uring_handle_tx(msghdr, res);
+ break;
+ default:
+ OSMO_ASSERT(0)
+ }
+
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_IN_CALLBACK);
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_TO_FREE) && !iofd->u.uring.read_msghdr && !iofd->u.uring.write_msghdr)
+ talloc_free(iofd);
+}
+
+/*! process all pending completion queue entries in given io_uring */
+static void iofd_uring_cqe(struct io_uring *ring)
+{
+ int rc;
+ struct io_uring_cqe *cqe;
+ struct iofd_msghdr *msghdr;
+
+ while (io_uring_peek_cqe(ring, &cqe) == 0) {
+
+ msghdr = io_uring_cqe_get_data(cqe);
+ if (!msghdr) {
+ LOGP(DLIO, LOGL_DEBUG, "Cancellation returned\n");
+ io_uring_cqe_seen(ring, cqe);
+ continue;
+ }
+ if (!msghdr->iofd) {
+ io_uring_cqe_seen(ring, cqe);
+ iofd_msghdr_free(msghdr);
+ continue;
+ }
+
+ rc = cqe->res;
+ /* Hand the entry back to the kernel before */
+ io_uring_cqe_seen(ring, cqe);
+
+ iofd_uring_handle_completion(msghdr, rc);
+
+ }
+}
+
+/*! will submit the next to-be-transmitted message for given iofd */
+static int iofd_uring_submit_tx(struct osmo_io_fd *iofd)
+{
+ struct io_uring_sqe *sqe;
+ struct iofd_msghdr *msghdr;
+
+ msghdr = iofd_txqueue_dequeue(iofd);
+ if (!msghdr)
+ return -ENODATA;
+
+ sqe = io_uring_get_sqe(&g_ring.ring);
+ if (!sqe) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not get io_uring_sqe\n");
+ OSMO_ASSERT(0);
+ }
+
+ io_uring_sqe_set_data(sqe, msghdr);
+
+ switch (msghdr->action) {
+ case IOFD_ACT_WRITE:
+ case IOFD_ACT_SENDTO:
+ case IOFD_ACT_SENDMSG:
+ io_uring_prep_sendmsg(sqe, msghdr->iofd->fd, &msghdr->hdr, msghdr->flags);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ io_uring_submit(&g_ring.ring);
+ iofd->u.uring.write_msghdr = msghdr;
+
+ return 0;
+}
+
+static void iofd_uring_write_enable(struct osmo_io_fd *iofd);
+static void iofd_uring_read_enable(struct osmo_io_fd *iofd);
+
+static int iofd_uring_register(struct osmo_io_fd *iofd)
+{
+ return 0;
+}
+
+static int iofd_uring_unregister(struct osmo_io_fd *iofd)
+{
+ struct io_uring_sqe *sqe;
+ struct iofd_msghdr *msghdr;
+
+ if (iofd->u.uring.read_msghdr) {
+ msghdr = iofd->u.uring.read_msghdr;
+ sqe = io_uring_get_sqe(&g_ring.ring);
+ OSMO_ASSERT(sqe != NULL);
+ io_uring_sqe_set_data(sqe, NULL);
+ LOGPIO(iofd, LOGL_DEBUG, "Cancelling read\n");
+ iofd->u.uring.read_msghdr = NULL;
+ talloc_steal(OTC_GLOBAL, msghdr);
+ msghdr->iofd = NULL;
+ io_uring_prep_cancel(sqe, msghdr, 0);
+ }
+
+ if (iofd->u.uring.write_msghdr) {
+ msghdr = iofd->u.uring.write_msghdr;
+ sqe = io_uring_get_sqe(&g_ring.ring);
+ OSMO_ASSERT(sqe != NULL);
+ io_uring_sqe_set_data(sqe, NULL);
+ LOGPIO(iofd, LOGL_DEBUG, "Cancelling write\n");
+ iofd->u.uring.write_msghdr = NULL;
+ talloc_steal(OTC_GLOBAL, msghdr);
+ msgb_free(msghdr->msg);
+ msghdr->iofd = NULL;
+ io_uring_prep_cancel(sqe, msghdr, 0);
+ }
+ io_uring_submit(&g_ring.ring);
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_NOTIFY_CONNECTED)) {
+ osmo_fd_unregister(&iofd->u.uring.connect_ofd);
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_NOTIFY_CONNECTED);
+ }
+
+ return 0;
+}
+
+static void iofd_uring_write_enable(struct osmo_io_fd *iofd)
+{
+ iofd->u.uring.write_enabled = true;
+
+ if (iofd->u.uring.write_msghdr)
+ return;
+
+ /* This function is called again, once the socket is connected. */
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_NOTIFY_CONNECTED))
+ return;
+
+ if (osmo_iofd_txqueue_len(iofd) > 0)
+ iofd_uring_submit_tx(iofd);
+ else if (iofd->mode == OSMO_IO_FD_MODE_READ_WRITE) {
+ /* Empty write request to check when the socket is connected */
+ struct iofd_msghdr *msghdr;
+ struct io_uring_sqe *sqe;
+ struct msgb *msg = msgb_alloc_headroom(0, 0, "io_uring write dummy");
+ if (!msg) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not allocate msgb for writing\n");
+ OSMO_ASSERT(0);
+ }
+ msghdr = iofd_msghdr_alloc(iofd, IOFD_ACT_WRITE, msg, 0);
+ if (!msghdr) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not allocate msghdr for writing\n");
+ OSMO_ASSERT(0);
+ }
+
+ msghdr->iov[0].iov_base = msgb_data(msg);
+ msghdr->iov[0].iov_len = msgb_length(msg);
+
+ sqe = io_uring_get_sqe(&g_ring.ring);
+ if (!sqe) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not get io_uring_sqe\n");
+ OSMO_ASSERT(0);
+ }
+ io_uring_prep_writev(sqe, iofd->fd, msghdr->iov, 1, 0);
+ io_uring_sqe_set_data(sqe, msghdr);
+
+ io_uring_submit(&g_ring.ring);
+ iofd->u.uring.write_msghdr = msghdr;
+ }
+}
+
+static void iofd_uring_write_disable(struct osmo_io_fd *iofd)
+{
+ iofd->u.uring.write_enabled = false;
+}
+
+static void iofd_uring_read_enable(struct osmo_io_fd *iofd)
+{
+ iofd->u.uring.read_enabled = true;
+
+ if (iofd->u.uring.read_msghdr)
+ return;
+
+ /* This function is called again, once the socket is connected. */
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_NOTIFY_CONNECTED))
+ return;
+
+ switch (iofd->mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ iofd_uring_submit_recv(iofd, IOFD_ACT_READ);
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ iofd_uring_submit_recv(iofd, IOFD_ACT_RECVFROM);
+ break;
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ iofd_uring_submit_recv(iofd, IOFD_ACT_RECVMSG);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void iofd_uring_read_disable(struct osmo_io_fd *iofd)
+{
+ iofd->u.uring.read_enabled = false;
+}
+
+static int iofd_uring_close(struct osmo_io_fd *iofd)
+{
+ iofd_uring_read_disable(iofd);
+ iofd_uring_write_disable(iofd);
+ iofd_uring_unregister(iofd);
+ return close(iofd->fd);
+}
+
+/* called via osmocom poll/select main handling once outbound non-blocking connect() completes */
+static int iofd_uring_connected_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ struct osmo_io_fd *iofd = ofd->data;
+
+ LOGPIO(iofd, LOGL_DEBUG, "Socket connected or failed.\n");
+
+ if (!(what & OSMO_FD_WRITE))
+ return 0;
+
+ /* Unregister from poll/select handling. */
+ osmo_fd_unregister(ofd);
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_NOTIFY_CONNECTED);
+
+ /* Notify the application about this via a zero-length write completion call-back. */
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_IN_CALLBACK);
+ switch (iofd->mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ iofd->io_ops.write_cb(iofd, 0, NULL);
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ iofd->io_ops.sendto_cb(iofd, 0, NULL, NULL);
+ break;
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ iofd->io_ops.sendmsg_cb(iofd, 0, NULL);
+ break;
+ }
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_IN_CALLBACK);
+
+ /* If write/read notifications are pending, enable it now. */
+ if (iofd->u.uring.write_enabled && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ iofd_uring_write_enable(iofd);
+ if (iofd->u.uring.read_enabled && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ iofd_uring_read_enable(iofd);
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_TO_FREE) && !iofd->u.uring.read_msghdr && !iofd->u.uring.write_msghdr)
+ talloc_free(iofd);
+ return 0;
+}
+
+static void iofd_uring_notify_connected(struct osmo_io_fd *iofd)
+{
+ if (iofd->mode == OSMO_IO_FD_MODE_RECVMSG_SENDMSG) {
+ /* Don't call this function after enabling read or write. */
+ OSMO_ASSERT(!iofd->u.uring.write_enabled && !iofd->u.uring.read_enabled);
+
+ /* Use a temporary osmo_fd which we can use to notify us once the connection is established
+ * or failed (indicated by FD becoming writable).
+ * This is needed as (at least for SCTP sockets) one cannot submit a zero-length writev/sendmsg
+ * in order to get notification when the socekt is writable.*/
+ if (!IOFD_FLAG_ISSET(iofd, IOFD_FLAG_NOTIFY_CONNECTED)) {
+ osmo_fd_setup(&iofd->u.uring.connect_ofd, iofd->fd, OSMO_FD_WRITE,
+ iofd_uring_connected_cb, iofd, 0);
+ if (osmo_fd_register(&iofd->u.uring.connect_ofd) < 0)
+ LOGPIO(iofd, LOGL_ERROR, "Failed to register FD for connect event.\n");
+ else
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_NOTIFY_CONNECTED);
+ }
+ } else
+ iofd_uring_write_enable(iofd);
+}
+
+const struct iofd_backend_ops iofd_uring_ops = {
+ .register_fd = iofd_uring_register,
+ .unregister_fd = iofd_uring_unregister,
+ .close = iofd_uring_close,
+ .write_enable = iofd_uring_write_enable,
+ .write_disable = iofd_uring_write_disable,
+ .read_enable = iofd_uring_read_enable,
+ .read_disable = iofd_uring_read_disable,
+ .notify_connected = iofd_uring_notify_connected,
+};
+
+#endif /* defined(__linux__) */
diff --git a/src/panic.c b/src/core/panic.c
index 072f458b..bbf6d081 100644
--- a/src/panic.c
+++ b/src/core/panic.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup utils
@@ -31,7 +27,7 @@
#include <osmocom/core/panic.h>
#include <osmocom/core/backtrace.h>
-#include "../config.h"
+#include "config.h"
static osmo_panic_handler_t osmo_panic_handler = (void*)0;
diff --git a/src/plugin.c b/src/core/plugin.c
index 40de4f8c..687ad406 100644
--- a/src/plugin.c
+++ b/src/core/plugin.c
@@ -17,17 +17,13 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup utils
* @{
* \file plugin.c */
-#include "../config.h"
+#include "config.h"
#if HAVE_DLFCN_H
diff --git a/src/prbs.c b/src/core/prbs.c
index 8fa04bb7..8fa04bb7 100644
--- a/src/prbs.c
+++ b/src/core/prbs.c
diff --git a/src/prim.c b/src/core/prim.c
index 3c8a7f12..3c8a7f12 100644
--- a/src/prim.c
+++ b/src/core/prim.c
diff --git a/src/probes.d b/src/core/probes.d
index e4150f0c..e4150f0c 100644
--- a/src/probes.d
+++ b/src/core/probes.d
diff --git a/src/rate_ctr.c b/src/core/rate_ctr.c
index 9043a2c6..44e26585 100644
--- a/src/rate_ctr.c
+++ b/src/core/rate_ctr.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup rate_ctr
@@ -57,14 +53,17 @@
*
* \file rate_ctr.c */
+#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
-#include <osmocom/core/timer.h>
+#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/logging.h>
@@ -263,6 +262,26 @@ void rate_ctr_group_free(struct rate_ctr_group *grp)
talloc_free(grp);
}
+/*! Get rate counter from group, identified by index idx
+ * \param[in] grp Rate counter group
+ * \param[in] idx Index of the counter to retrieve
+ * \returns rate counter requested
+ */
+struct rate_ctr *rate_ctr_group_get_ctr(struct rate_ctr_group *grp, unsigned int idx)
+{
+ return &grp->ctr[idx];
+}
+
+/*! Set a name for the group of counters be used instead of index value
+ at report time.
+ * \param[in] grp Rate counter group
+ * \param[in] name Name identifier to assign to the rate counter group
+ */
+void rate_ctr_group_set_name(struct rate_ctr_group *grp, const char *name)
+{
+ osmo_talloc_replace_string(grp, &grp->name, name);
+}
+
/*! Add a number to the counter */
void rate_ctr_add(struct rate_ctr *ctr, int inc)
{
@@ -287,14 +306,9 @@ static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv)
ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last;
/* save current counter for next interval */
ctr->intv[intv].last = ctr->current;
-
- /* update the rate of the next bigger interval. This will
- * be overwritten when that next larger interval expires */
- if (intv + 1 < ARRAY_SIZE(ctr->intv))
- ctr->intv[intv+1].rate += ctr->intv[intv].rate;
}
-static struct osmo_timer_list rate_ctr_timer;
+static struct osmo_fd rate_ctr_timer = { .fd = -1 };
static uint64_t timer_ticks;
/* The one-second interval has expired */
@@ -315,18 +329,35 @@ static void rate_ctr_group_intv(struct rate_ctr_group *grp)
}
}
-static void rate_ctr_timer_cb(void *data)
+static int rate_ctr_timer_cb(struct osmo_fd *ofd, unsigned int what)
{
struct rate_ctr_group *ctrg;
+ uint64_t expire_count;
+ int rc;
- /* Increment number of ticks before we calculate intervals,
- * as a counter value of 0 would already wrap all counters */
- timer_ticks++;
+ /* check that the timer has actually expired */
+ if (!(what & OSMO_FD_READ))
+ return 0;
- llist_for_each_entry(ctrg, &rate_ctr_groups, list)
- rate_ctr_group_intv(ctrg);
+ /* read from timerfd: number of expirations of periodic timer */
+ rc = read(ofd->fd, (void *) &expire_count, sizeof(expire_count));
+ if (rc < 0 && errno == EAGAIN)
+ return 0;
- osmo_timer_schedule(&rate_ctr_timer, 1, 0);
+ OSMO_ASSERT(rc == sizeof(expire_count));
+
+ if (expire_count > 1)
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Stats timer expire_count=%" PRIu64 ": We missed %" PRIu64 " timers\n",
+ expire_count, expire_count - 1);
+
+ do { /* Increment number of ticks before we calculate intervals,
+ * as a counter value of 0 would already wrap all counters */
+ timer_ticks++;
+ llist_for_each_entry(ctrg, &rate_ctr_groups, list)
+ rate_ctr_group_intv(ctrg);
+ } while (--expire_count);
+
+ return 0;
}
/*! Initialize the counter module. Call this once from your application.
@@ -334,9 +365,27 @@ static void rate_ctr_timer_cb(void *data)
* \returns 0 on success; negative on error */
int rate_ctr_init(void *tall_ctx)
{
+ struct timespec ts_interval = { .tv_sec = 1, .tv_nsec = 0 };
+ int rc;
+
+ /* ignore repeated initialization */
+ if (osmo_fd_is_registered(&rate_ctr_timer))
+ return 0;
+
tall_rate_ctr_ctx = tall_ctx;
- osmo_timer_setup(&rate_ctr_timer, rate_ctr_timer_cb, NULL);
- osmo_timer_schedule(&rate_ctr_timer, 1, 0);
+
+ rc = osmo_timerfd_setup(&rate_ctr_timer, rate_ctr_timer_cb, NULL);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Failed to setup the timer with error code %d (fd=%d)\n",
+ rc, rate_ctr_timer.fd);
+ return rc;
+ }
+
+ rc = osmo_timerfd_schedule(&rate_ctr_timer, NULL, &ts_interval);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Failed to schedule the timer with error code %d (fd=%d)\n",
+ rc, rate_ctr_timer.fd);
+ }
return 0;
}
diff --git a/src/rbtree.c b/src/core/rbtree.c
index 211978f1..f4dc219b 100644
--- a/src/rbtree.c
+++ b/src/core/rbtree.c
@@ -15,11 +15,6 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301, USA
-
linux/lib/rbtree.c
*/
diff --git a/src/select.c b/src/core/select.c
index 71ee7f60..70047f09 100644
--- a/src/select.c
+++ b/src/core/select.c
@@ -5,7 +5,7 @@
* of the linux 2.4 netfilter subsystem. */
/*
* (C) 2000-2020 by Harald Welte <laforge@gnumonks.org>
- * All Rights Reserverd.
+ * All Rights Reserved.
*
* SPDX-License-Identifier: GPL-2.0+
*
@@ -18,11 +18,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
*/
#include <fcntl.h>
@@ -38,8 +33,10 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/stat_item.h>
+#include <osmocom/core/stats_tcp.h>
-#include "../config.h"
+#include "config.h"
#if defined(HAVE_SYS_SELECT_H) && defined(HAVE_POLL_H)
#include <sys/select.h>
@@ -57,6 +54,39 @@ static __thread int maxfd = 0;
static __thread struct llist_head osmo_fds; /* TLS cannot use LLIST_HEAD() */
static __thread int unregistered_count;
+/* Array of struct osmo_fd * (size "max_fd") ordered by "ofd->fd" */
+static __thread struct {
+ struct osmo_fd **table;
+ unsigned int size;
+} osmo_fd_lookup;
+
+static void osmo_fd_lookup_table_extend(unsigned int new_max_fd)
+{
+ unsigned int min_new_size;
+ unsigned int pw2;
+ unsigned int new_size;
+
+ /* First calculate the minimally required new size of the array in bytes: */
+ min_new_size = (new_max_fd + 1) * sizeof(struct osmo_fd *);
+ /* Now find the lower power of two of min_new_size (in bytes): */
+ pw2 = 32 - __builtin_clz(min_new_size);
+ /* get next (upper side) power of 2: */
+ pw2++;
+ OSMO_ASSERT(pw2 <= 31); /* Avoid shifting more than 31 bits */
+ new_size = 1 << (pw2 + 1);
+
+ /* Let's make it at least 1024B, to avoid reallocating quickly at startup */
+ if (new_size < 1024)
+ new_size = 1024;
+ if (new_size > osmo_fd_lookup.size) {
+ uint8_t *ptr = talloc_realloc_size(OTC_GLOBAL, osmo_fd_lookup.table, new_size);
+ OSMO_ASSERT(ptr);
+ memset(ptr + osmo_fd_lookup.size, 0, new_size - osmo_fd_lookup.size);
+ osmo_fd_lookup.table = (struct osmo_fd **)ptr;
+ osmo_fd_lookup.size = new_size;
+ }
+}
+
#ifndef FORCE_IO_SELECT
struct poll_state {
/* array of pollfd */
@@ -69,6 +99,11 @@ struct poll_state {
static __thread struct poll_state g_poll;
#endif /* FORCE_IO_SELECT */
+/*! See osmo_select_shutdown_request() */
+static int _osmo_select_shutdown_requested = 0;
+/*! See osmo_select_shutdown_request() */
+static bool _osmo_select_shutdown_done = false;
+
/*! Set up an osmo-fd. Will not register it.
* \param[inout] ofd Osmo FD to be set-up
* \param[in] fd OS-level file descriptor number
@@ -116,6 +151,9 @@ bool osmo_fd_is_registered(struct osmo_fd *fd)
/*! Register a new file descriptor with select loop abstraction
* \param[in] fd osmocom file descriptor to be registered
* \returns 0 on success; negative in case of error
+ *
+ * The API expects fd field of the struct osmo_fd to remain unchanged while
+ * registered, ie until osmo_fd_unregister() is called on it.
*/
int osmo_fd_register(struct osmo_fd *fd)
{
@@ -140,8 +178,10 @@ int osmo_fd_register(struct osmo_fd *fd)
return flags;
/* Register FD */
- if (fd->fd > maxfd)
+ if (fd->fd > maxfd) {
maxfd = fd->fd;
+ osmo_fd_lookup_table_extend(maxfd);
+ }
#ifdef OSMO_FD_CHECK
if (osmo_fd_is_registered(fd)) {
@@ -164,12 +204,17 @@ int osmo_fd_register(struct osmo_fd *fd)
#endif /* FORCE_IO_SELECT */
llist_add_tail(&fd->list, &osmo_fds);
+ osmo_fd_lookup.table[fd->fd] = fd;
return 0;
}
/*! Unregister a file descriptor from select loop abstraction
* \param[in] fd osmocom file descriptor to be unregistered
+ *
+ * Caller is responsible for ensuring the fd is really registered before calling this API.
+ * This function must be called before changing the value of the fd field in
+ * the struct osmo_fd.
*/
void osmo_fd_unregister(struct osmo_fd *fd)
{
@@ -181,6 +226,24 @@ void osmo_fd_unregister(struct osmo_fd *fd)
#ifndef FORCE_IO_SELECT
g_poll.num_registered--;
#endif /* FORCE_IO_SELECT */
+
+ if (OSMO_UNLIKELY(fd->fd < 0 || fd->fd > maxfd)) {
+ /* Some old users used to incorrectly set fd = -1 *before* calling osmo_unregister().
+ * Hence, in order to keep backward compatibility it's not possible to assert() here.
+ * Instead, print an error message since this is actually a bug in the API user. */
+#ifdef OSMO_FD_CHECK
+ osmo_panic("osmo_fd_unregister(fd=%u) out of expected range (0..%u), fix your code!!!\n",
+ fd->fd, maxfd);
+#else
+ fprintf(stderr, "osmo_fd_unregister(fd=%u) out of expected range (0..%u), fix your code!!!\n",
+ fd->fd, maxfd);
+ return;
+#endif
+ }
+
+ osmo_fd_lookup.table[fd->fd] = NULL;
+ /* If existent, free any statistical data */
+ osmo_stats_tcp_osmo_fd_unregister(fd);
}
/*! Close a file descriptor, mark it as closed + unregister from select loop abstraction
@@ -311,11 +374,12 @@ static unsigned int poll_fill_fds(void)
}
/* iterate over first n_fd entries of g_poll.poll + dispatch */
-static int poll_disp_fds(int n_fd)
+static int poll_disp_fds(unsigned int n_fd)
{
struct osmo_fd *ufd;
unsigned int i;
int work = 0;
+ int shutdown_pending_writes = 0;
for (i = 0; i < n_fd; i++) {
struct pollfd *p = &g_poll.poll[i];
@@ -340,6 +404,11 @@ static int poll_disp_fds(int n_fd)
/* make sure we never report more than the user requested */
flags &= ufd->when;
+ if (_osmo_select_shutdown_requested > 0) {
+ if (ufd->when & OSMO_FD_WRITE)
+ shutdown_pending_writes++;
+ }
+
if (flags) {
work = 1;
/* make sure to clear any log context before processing the next incoming message
@@ -351,6 +420,9 @@ static int poll_disp_fds(int n_fd)
}
}
+ if (_osmo_select_shutdown_requested > 0 && !shutdown_pending_writes)
+ _osmo_select_shutdown_done = true;
+
return work;
}
@@ -358,19 +430,26 @@ static int _osmo_select_main(int polling)
{
unsigned int n_poll;
int rc;
+ int timeout = 0;
/* prepare read and write fdsets */
n_poll = poll_fill_fds();
- if (!polling)
+ if (!polling) {
osmo_timers_prepare();
+ timeout = osmo_timers_nearest_ms();
+
+ if (_osmo_select_shutdown_requested && timeout == -1)
+ timeout = 0;
+ }
- rc = poll(g_poll.poll, n_poll, polling ? 0 : osmo_timers_nearest_ms());
+ rc = poll(g_poll.poll, n_poll, timeout);
if (rc < 0)
return 0;
/* fire timers */
- osmo_timers_update();
+ if (!_osmo_select_shutdown_requested)
+ osmo_timers_update();
OSMO_ASSERT(osmo_ctx->select);
@@ -443,23 +522,21 @@ int osmo_select_main_ctx(int polling)
* \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
struct osmo_fd *osmo_fd_get_by_fd(int fd)
{
- struct osmo_fd *ofd;
-
- llist_for_each_entry(ofd, &osmo_fds, list) {
- if (ofd->fd == fd)
- return ofd;
- }
- return NULL;
+ if (fd > maxfd || fd < 0)
+ return NULL;
+ return osmo_fd_lookup.table[fd];
}
/*! initialize the osmocom select abstraction for the current thread */
void osmo_select_init(void)
{
INIT_LLIST_HEAD(&osmo_fds);
+ osmo_fd_lookup_table_extend(0);
}
-/* ensure main thread always has pre-initialized osmo_fds */
-static __attribute__((constructor)) void on_dso_load_select(void)
+/* ensure main thread always has pre-initialized osmo_fds
+ * priority 102: must run after on_dso_load_ctx */
+static __attribute__((constructor(102))) void on_dso_load_select(void)
{
osmo_select_init();
}
@@ -596,6 +673,59 @@ osmo_signalfd_setup(void *ctx, sigset_t set, osmo_signalfd_cb *cb, void *data)
#endif /* HAVE_SYS_SIGNALFD_H */
+/*! Request osmo_select_* to only service pending OSMO_FD_WRITE requests. Once all writes are done,
+ * osmo_select_shutdown_done() returns true. This allows for example to send all outbound packets before terminating the
+ * process.
+ *
+ * Usage example:
+ *
+ * static void signal_handler(int signum)
+ * {
+ * fprintf(stdout, "signal %u received\n", signum);
+ *
+ * switch (signum) {
+ * case SIGINT:
+ * case SIGTERM:
+ * // If the user hits Ctrl-C the third time, just terminate immediately.
+ * if (osmo_select_shutdown_requested() >= 2)
+ * exit(-1);
+ * // Request write-only mode in osmo_select_main_ctx()
+ * osmo_select_shutdown_request();
+ * break;
+ * [...]
+ * }
+ *
+ * main()
+ * {
+ * signal(SIGINT, &signal_handler);
+ * signal(SIGTERM, &signal_handler);
+ *
+ * [...]
+ *
+ * // After the signal_handler issued osmo_select_shutdown_request(), osmo_select_shutdown_done() returns true
+ * // as soon as all write queues are empty.
+ * while (!osmo_select_shutdown_done()) {
+ * osmo_select_main_ctx(0);
+ * }
+ * }
+ */
+void osmo_select_shutdown_request(void)
+{
+ _osmo_select_shutdown_requested++;
+};
+
+/*! Return the number of times osmo_select_shutdown_request() was called before. */
+int osmo_select_shutdown_requested(void)
+{
+ return _osmo_select_shutdown_requested;
+};
+
+/*! Return true after osmo_select_shutdown_requested() was called, and after an osmo_select poll loop found no more
+ * pending OSMO_FD_WRITE on any registered socket. */
+bool osmo_select_shutdown_done(void) {
+ return _osmo_select_shutdown_done;
+};
+
/*! @} */
#endif /* _HAVE_SYS_SELECT_H */
diff --git a/src/sercomm.c b/src/core/sercomm.c
index 2639bf8a..1798acec 100644
--- a/src/sercomm.c
+++ b/src/core/sercomm.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup sercomm
diff --git a/src/serial.c b/src/core/serial.c
index c3bf5e89..117c049b 100644
--- a/src/serial.c
+++ b/src/core/serial.c
@@ -16,10 +16,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*! \addtogroup serial
diff --git a/src/signal.c b/src/core/signal.c
index be3b7778..ba1555ae 100644
--- a/src/signal.c
+++ b/src/core/signal.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/signal.h>
diff --git a/src/sockaddr_str.c b/src/core/sockaddr_str.c
index f5508a03..bea61849 100644
--- a/src/sockaddr_str.c
+++ b/src/core/sockaddr_str.c
@@ -20,10 +20,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include "config.h"
@@ -37,6 +33,7 @@
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/byteswap.h>
+#include <osmocom/core/socket.h>
/*! \addtogroup sockaddr_str
*
@@ -363,6 +360,17 @@ int osmo_sockaddr_str_from_sockaddr(struct osmo_sockaddr_str *sockaddr_str, cons
return -EINVAL;
}
+/*! Convert IPv4 or IPv6 address and port to osmo_sockaddr_str.
+ * \param[out] sockaddr_str The instance to copy to.
+ * \param[in] src IPv4 or IPv6 address and port data.
+ * \return 0 on success, negative if src does not indicate AF_INET nor AF_INET6 (or if the conversion fails, which
+ * should not be possible in practice).
+ */
+int osmo_sockaddr_str_from_osa(struct osmo_sockaddr_str *sockaddr_str, const struct osmo_sockaddr *src)
+{
+ return osmo_sockaddr_str_from_sockaddr(sockaddr_str, &src->u.sas);
+}
+
/*! Convert osmo_sockaddr_str address string to IPv4 address data.
* \param[in] sockaddr_str The instance to convert the IP of.
* \param[out] dst IPv4 address data to write to.
@@ -513,5 +521,15 @@ int osmo_sockaddr_str_to_sockaddr(const struct osmo_sockaddr_str *sockaddr_str,
}
}
+/*! Convert osmo_sockaddr_str address string and port to IPv4 or IPv6 address and port data.
+ * \param[in] sockaddr_str The instance to convert the IP and port of.
+ * \param[out] dst IPv4/IPv6 address and port data to write to.
+ * \return 0 on success, negative on error (e.g. invalid IP address string for the family indicated by sockaddr_str->af).
+ */
+int osmo_sockaddr_str_to_osa(const struct osmo_sockaddr_str *sockaddr_str, struct osmo_sockaddr *dst)
+{
+ return osmo_sockaddr_str_to_sockaddr(sockaddr_str, &dst->u.sas);
+}
+
/*! @} */
#endif // HAVE_NETINET_IN_H
diff --git a/src/socket.c b/src/core/socket.c
index 59d0876f..80a9d0ec 100644
--- a/src/socket.c
+++ b/src/core/socket.c
@@ -15,13 +15,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
-#include "../config.h"
+#include "config.h"
/*! \addtogroup socket
* @{
@@ -30,6 +26,7 @@
* \file socket.c */
#ifdef HAVE_SYS_SOCKET_H
+#define _GNU_SOURCE /* for struct ucred on glibc >= 2.8 */
#include <osmocom/core/logging.h>
#include <osmocom/core/select.h>
@@ -118,7 +115,7 @@ static struct addrinfo *addrinfo_helper(uint16_t family, uint16_t type, uint8_t
static int addrinfo_helper_multi(struct addrinfo **addrinfo, uint16_t family, uint16_t type, uint8_t proto,
const char **hosts, size_t host_cnt, uint16_t port, bool passive)
{
- int i, j;
+ unsigned int i, j;
for (i = 0; i < host_cnt; i++) {
addrinfo[i] = addrinfo_helper(family, type, proto, hosts[i], port, passive);
@@ -214,7 +211,8 @@ static int socket_helper_osa(const struct osmo_sockaddr *addr, uint16_t type, ui
static int multiaddr_snprintf(char* buf, size_t buf_len, const char **hosts, size_t host_cnt)
{
int len = 0, offset = 0, rem = buf_len;
- int ret, i;
+ size_t i;
+ int ret;
char *after;
if (buf_len < 3)
@@ -252,7 +250,7 @@ static int osmo_sock_init_tail(int fd, uint16_t type, unsigned int flags)
if (rc < 0) {
LOGP(DLGLOBAL, LOGL_ERROR, "unable to listen on socket: %s\n",
strerror(errno));
- return rc;
+ return -errno;
}
break;
}
@@ -487,8 +485,8 @@ int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
}
#define _SOCKADDR_TO_STR(dest, sockaddr) do { \
- if (osmo_sockaddr_str_from_sockaddr(&dest, &sockaddr->u.sas)) \
- osmo_strlcpy(dest.ip, "Invalid IP", 11); \
+ if (osmo_sockaddr_str_from_sockaddr(dest, &sockaddr->u.sas)) \
+ osmo_strlcpy((dest)->ip, "Invalid IP", 11); \
} while (0)
/*! Initialize a socket (including bind and/or connect)
@@ -523,7 +521,8 @@ int osmo_sock_init_osa(uint16_t type, uint8_t proto,
unsigned int flags)
{
int sfd = -1, rc, on = 1;
- struct osmo_sockaddr_str sastr = {};
+ struct osmo_sockaddr_str _sastr = {};
+ struct osmo_sockaddr_str *sastr = &_sastr;
if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
@@ -554,8 +553,8 @@ int osmo_sock_init_osa(uint16_t type, uint8_t proto,
sfd = socket_helper_osa(local, type, proto, flags);
if (sfd < 0) {
_SOCKADDR_TO_STR(sastr, local);
- LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: %s:%u\n",
- sastr.ip, sastr.port);
+ LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(sastr));
return -ENODEV;
}
@@ -563,21 +562,21 @@ int osmo_sock_init_osa(uint16_t type, uint8_t proto,
rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
&on, sizeof(on));
if (rc < 0) {
+ int err = errno;
_SOCKADDR_TO_STR(sastr, local);
LOGP(DLGLOBAL, LOGL_ERROR,
- "cannot setsockopt socket:"
- " %s:%u: %s\n",
- sastr.ip, sastr.port,
- strerror(errno));
+ "cannot setsockopt socket: " OSMO_SOCKADDR_STR_FMT ": %s\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(sastr), strerror(err));
close(sfd);
return rc;
}
}
if (bind(sfd, &local->u.sa, sizeof(struct osmo_sockaddr)) == -1) {
+ int err = errno;
_SOCKADDR_TO_STR(sastr, local);
- LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n",
- sastr.ip, sastr.port, strerror(errno));
+ LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: " OSMO_SOCKADDR_STR_FMT ": %s\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(sastr), strerror(err));
close(sfd);
return -1;
}
@@ -598,9 +597,10 @@ int osmo_sock_init_osa(uint16_t type, uint8_t proto,
rc = connect(sfd, &remote->u.sa, sizeof(struct osmo_sockaddr));
if (rc != 0 && errno != EINPROGRESS) {
+ int err = errno;
_SOCKADDR_TO_STR(sastr, remote);
- LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n",
- sastr.ip, sastr.port, strerror(errno));
+ LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: " OSMO_SOCKADDR_STR_FMT ": %s\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(sastr), strerror(err));
close(sfd);
return rc;
}
@@ -617,18 +617,45 @@ int osmo_sock_init_osa(uint16_t type, uint8_t proto,
#ifdef HAVE_LIBSCTP
-/* Check whether there's an IPv6 Addr as first option of any addrinfo item in the addrinfo set */
+/* Check whether there's an addrinfo item in the addrinfo set with an IPv4 or IPv6 option */
static void addrinfo_has_v4v6addr(const struct addrinfo **result, size_t result_count, bool *has_v4, bool *has_v6)
{
size_t host_idx;
+ const struct addrinfo *rp;
*has_v4 = false;
*has_v6 = false;
for (host_idx = 0; host_idx < result_count; host_idx++) {
- if (result[host_idx]->ai_family == AF_INET)
- *has_v4 = true;
- else if (result[host_idx]->ai_family == AF_INET6)
- *has_v6 = true;
+ for (rp = result[host_idx]; rp != NULL; rp = rp->ai_next) {
+ if (result[host_idx]->ai_family == AF_INET)
+ *has_v4 = true;
+ else if (result[host_idx]->ai_family == AF_INET6)
+ *has_v6 = true;
+ }
+ }
+}
+
+/* Check whether there's an addrinfo item in the addrinfo set with only an IPv4 or IPv6 option */
+static void addrinfo_has_v4v6only_addr(const struct addrinfo **result, size_t result_count, bool *has_v4only, bool *has_v6only)
+{
+ size_t host_idx;
+ const struct addrinfo *rp;
+ *has_v4only = false;
+ *has_v6only = false;
+
+ for (host_idx = 0; host_idx < result_count; host_idx++) {
+ bool has_v4 = false;
+ bool has_v6 = false;
+ for (rp = result[host_idx]; rp != NULL; rp = rp->ai_next) {
+ if (rp->ai_family == AF_INET6)
+ has_v6 = true;
+ else
+ has_v4 = true;
+ }
+ if (has_v4 && !has_v6)
+ *has_v4only = true;
+ else if (has_v6 && !has_v4)
+ *has_v6only = true;
}
}
@@ -637,13 +664,16 @@ static bool addrinfo_has_in6addr_any(const struct addrinfo **result, size_t resu
{
size_t host_idx;
struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+ const struct addrinfo *rp;
for (host_idx = 0; host_idx < result_count; host_idx++) {
- if (result[host_idx]->ai_family != AF_INET6)
- continue;
- if (memcmp(&((struct sockaddr_in6 *)result[host_idx]->ai_addr)->sin6_addr,
- &in6addr_any, sizeof(in6addr_any)) == 0)
- return true;
+ for (rp = result[host_idx]; rp != NULL; rp = rp->ai_next) {
+ if (rp->ai_family != AF_INET6)
+ continue;
+ if (memcmp(&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr,
+ &in6addr_any, sizeof(in6addr_any)) == 0)
+ return true;
+ }
}
return false;
}
@@ -669,7 +699,7 @@ static int socket_helper_multiaddr(uint16_t family, uint16_t type, uint8_t proto
/* Build array of addresses taking first addrinfo result of the requested family
* for each host in addrs_buf. */
static int addrinfo_to_sockaddr(uint16_t family, const struct addrinfo **result,
- const char **hosts, int host_cont,
+ const char **hosts, unsigned int host_cont,
uint8_t *addrs_buf, size_t addrs_buf_len) {
size_t host_idx, offset = 0;
const struct addrinfo *rp;
@@ -677,26 +707,102 @@ static int addrinfo_to_sockaddr(uint16_t family, const struct addrinfo **result,
for (host_idx = 0; host_idx < host_cont; host_idx++) {
/* Addresses are ordered based on RFC 3484, see man getaddrinfo */
for (rp = result[host_idx]; rp != NULL; rp = rp->ai_next) {
- if (family != AF_UNSPEC && rp->ai_family != family)
- continue;
- if (offset + rp->ai_addrlen > addrs_buf_len) {
- LOGP(DLGLOBAL, LOGL_ERROR, "Output buffer to small: %zu\n",
- addrs_buf_len);
- return -ENOSPC;
+ if (family == AF_UNSPEC || rp->ai_family == family)
+ break;
+ }
+ if (!rp && family == AF_INET6) {
+ /* See if we can find an AF_INET addr for the AF_INET6 socket instead: */
+ for (rp = result[host_idx]; rp != NULL; rp = rp->ai_next) {
+ if (rp->ai_family == AF_INET)
+ break;
}
- memcpy(addrs_buf + offset, rp->ai_addr, rp->ai_addrlen);
- offset += rp->ai_addrlen;
- break;
}
if (!rp) { /* No addr could be bound for this host! */
LOGP(DLGLOBAL, LOGL_ERROR, "No suitable remote address found for host: %s\n",
hosts[host_idx]);
return -ENODEV;
}
+ if (offset + rp->ai_addrlen > addrs_buf_len) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Output buffer to small: %zu\n",
+ addrs_buf_len);
+ return -ENOSPC;
+ }
+ memcpy(addrs_buf + offset, rp->ai_addr, rp->ai_addrlen);
+ offset += rp->ai_addrlen;
}
return 0;
}
+static int setsockopt_sctp_auth_supported(int fd, uint32_t val)
+{
+#ifdef SCTP_AUTH_SUPPORTED
+ struct sctp_assoc_value assoc_val = {
+ .assoc_id = SCTP_FUTURE_ASSOC,
+ .assoc_value = val,
+ };
+ return setsockopt(fd, IPPROTO_SCTP, SCTP_AUTH_SUPPORTED, &assoc_val, sizeof(assoc_val));
+#else
+#pragma message "setsockopt(SCTP_AUTH_SUPPORTED) not supported! some SCTP features may not be available!"
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Built without support for setsockopt(SCTP_AUTH_SUPPORTED), skipping\n");
+ return -ENOTSUP;
+#endif
+}
+
+static int setsockopt_sctp_asconf_supported(int fd, uint32_t val)
+{
+#ifdef SCTP_ASCONF_SUPPORTED
+ struct sctp_assoc_value assoc_val = {
+ .assoc_id = SCTP_FUTURE_ASSOC,
+ .assoc_value = val,
+ };
+ return setsockopt(fd, IPPROTO_SCTP, SCTP_ASCONF_SUPPORTED, &assoc_val, sizeof(assoc_val));
+#else
+#pragma message "setsockopt(SCTP_ASCONF_SUPPORTED) not supported! some SCTP features may not be available!"
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Built without support for setsockopt(SCTP_ASCONF_SUPPORTED), skipping\n");
+ return -ENOTSUP;
+#endif
+}
+
+static int setsockopt_sctp_initmsg(int fd, const struct osmo_sock_init2_multiaddr_pars *pars)
+{
+ if (!pars->sctp.sockopt_initmsg.num_ostreams_present &&
+ !pars->sctp.sockopt_initmsg.max_instreams_present &&
+ !pars->sctp.sockopt_initmsg.max_attempts_present &&
+ !pars->sctp.sockopt_initmsg.max_init_timeo_present)
+ return 0; /* nothing to set/do */
+
+#ifdef SCTP_INITMSG
+ struct sctp_initmsg si = {0};
+ socklen_t si_len = sizeof(si);
+ int rc;
+
+ /* If at least one field not present, obtain current value from kernel: */
+ if (!pars->sctp.sockopt_initmsg.num_ostreams_present ||
+ !pars->sctp.sockopt_initmsg.max_instreams_present ||
+ !pars->sctp.sockopt_initmsg.max_attempts_present ||
+ !pars->sctp.sockopt_initmsg.max_init_timeo_present) {
+ rc = getsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &si, &si_len);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (pars->sctp.sockopt_initmsg.num_ostreams_present)
+ si.sinit_num_ostreams = pars->sctp.sockopt_initmsg.num_ostreams_value;
+ if (pars->sctp.sockopt_initmsg.max_instreams_present)
+ si.sinit_max_instreams = pars->sctp.sockopt_initmsg.max_instreams_value;
+ if (pars->sctp.sockopt_initmsg.max_attempts_present)
+ si.sinit_max_attempts = pars->sctp.sockopt_initmsg.max_attempts_value;
+ if (pars->sctp.sockopt_initmsg.max_init_timeo_present)
+ si.sinit_max_init_timeo = pars->sctp.sockopt_initmsg.max_init_timeo_value;
+
+ return setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &si, sizeof(si));
+#else
+#pragma message "setsockopt(SCTP_INITMSG) not supported! some SCTP features may not be available!"
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Built without support for setsockopt(SCTP_INITMSG), skipping\n");
+ return -ENOTSUP
+#endif
+}
+
/*! Initialize a socket (including bind and/or connect) with multiple local or remote addresses.
* \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
* \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
@@ -721,25 +827,58 @@ int osmo_sock_init2_multiaddr(uint16_t family, uint16_t type, uint8_t proto,
const char **local_hosts, size_t local_hosts_cnt, uint16_t local_port,
const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port,
unsigned int flags)
+{
+ return osmo_sock_init2_multiaddr2(family, type, proto, local_hosts, local_hosts_cnt, local_port,
+ remote_hosts, remote_hosts_cnt, remote_port, flags, NULL);
+}
+
+/*! Initialize a socket (including bind and/or connect) with multiple local or remote addresses.
+ * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] local_hosts array of char pointers (strings), each containing local host name or IP address in string form
+ * \param[in] local_hosts_cnt length of local_hosts (in items)
+ * \param[in] local_port local port number in host byte order
+ * \param[in] remote_host array of char pointers (strings), each containing remote host name or IP address in string form
+ * \param[in] remote_hosts_cnt length of remote_hosts (in items)
+ * \param[in] remote_port remote port number in host byte order
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \param[in] pars Extra parameters for multi-address specific protocols, such as SCTP. Can be NULL.
+ * \returns socket file descriptor on success; negative on error
+ *
+ * This function is similar to \ref osmo_sock_init2(), but can be passed an
+ * array of local or remote addresses for protocols supporting multiple
+ * addresses per socket, like SCTP (currently only one supported). This function
+ * should not be used by protocols not supporting this kind of features, but
+ * rather \ref osmo_sock_init2() should be used instead.
+ * See \ref osmo_sock_init2() for more information on flags and general behavior.
+ *
+ * pars: If "pars" parameter is passed to the function, sctp.version shall be set to 0.
+ */
+int osmo_sock_init2_multiaddr2(uint16_t family, uint16_t type, uint8_t proto,
+ const char **local_hosts, size_t local_hosts_cnt, uint16_t local_port,
+ const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port,
+ unsigned int flags, struct osmo_sock_init2_multiaddr_pars *pars)
{
struct addrinfo *res_loc[OSMO_SOCK_MAX_ADDRS], *res_rem[OSMO_SOCK_MAX_ADDRS];
int sfd = -1, rc, on = 1;
- int i;
- bool loc_has_v4addr, rem_has_v4addr;
- bool loc_has_v6addr, rem_has_v6addr;
+ unsigned int i;
+ bool loc_has_v4addr = false, loc_has_v6addr = false;
+ bool rem_has_v4addr = false, rem_has_v6addr = false;
+ bool loc_has_v4only_addr, rem_has_v4only_addr;
+ bool loc_has_v6only_addr, rem_has_v6only_addr;
struct sockaddr_in6 addrs_buf[OSMO_SOCK_MAX_ADDRS];
char strbuf[512];
- /* updated later in case of AF_UNSPEC */
- loc_has_v4addr = rem_has_v4addr = (family == AF_INET);
- loc_has_v6addr = rem_has_v6addr = (family == AF_INET6);
-
/* TODO: So far this function is only aimed for SCTP, but could be
reused in the future for other protocols with multi-addr support */
if (proto != IPPROTO_SCTP)
return -ENOTSUP;
+ if (pars && pars->sctp.version != 0)
+ return -EINVAL;
+
if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
"BIND or CONNECT flags\n");
@@ -758,10 +897,17 @@ int osmo_sock_init2_multiaddr(uint16_t family, uint16_t type, uint8_t proto,
local_hosts_cnt, local_port, true);
if (rc < 0)
return -EINVAL;
- /* Figure out if there's any IPV4 or IPv6 addr in the set */
- if (family == AF_UNSPEC)
- addrinfo_has_v4v6addr((const struct addrinfo **)res_loc, local_hosts_cnt,
- &loc_has_v4addr, &loc_has_v6addr);
+ /* Figure out if there's any IPv4 or IPv6 entry in the result set */
+ addrinfo_has_v4v6addr((const struct addrinfo **)res_loc, local_hosts_cnt,
+ &loc_has_v4addr, &loc_has_v6addr);
+ /* Figure out if there's any IPv4-only or IPv6-only addr in the result set */
+ addrinfo_has_v4v6only_addr((const struct addrinfo **)res_loc, local_hosts_cnt,
+ &loc_has_v4only_addr, &loc_has_v6only_addr);
+ if (family == AF_INET && loc_has_v6only_addr) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Cannot bind an IPv6 address to an AF_INET socket\n");
+ rc = -EINVAL;
+ goto ret_freeaddrinfo_loc;
+ }
}
/* figure out remote side of socket */
if (flags & OSMO_SOCK_F_CONNECT) {
@@ -771,39 +917,109 @@ int osmo_sock_init2_multiaddr(uint16_t family, uint16_t type, uint8_t proto,
rc = -EINVAL;
goto ret_freeaddrinfo_loc;
}
- /* Figure out if there's any IPv4 or IPv6 addr in the set */
- if (family == AF_UNSPEC)
- addrinfo_has_v4v6addr((const struct addrinfo **)res_rem, remote_hosts_cnt,
- &rem_has_v4addr, &rem_has_v6addr);
+ /* Figure out if there's any IPv4 or IPv6 entry in the result set */
+ addrinfo_has_v4v6addr((const struct addrinfo **)res_rem, remote_hosts_cnt,
+ &rem_has_v4addr, &rem_has_v6addr);
+ /* Figure out if there's any IPv4-only or IPv6-only addr in the result set */
+ addrinfo_has_v4v6only_addr((const struct addrinfo **)res_rem, remote_hosts_cnt,
+ &rem_has_v4only_addr, &rem_has_v6only_addr);
+ if (family == AF_INET && rem_has_v6only_addr) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Cannot connect to an IPv6 address in an AF_INET socket\n");
+ rc = -EINVAL;
+ goto ret_freeaddrinfo;
+ }
+ }
+
+ /* Find out the socket family now if not established by caller:
+ * Both are checked here through "or" here to account for "bind flag set,
+ * connect flag not set" and viceversa. */
+ if (family == AF_UNSPEC) {
+ if (!loc_has_v6addr && !rem_has_v6addr)
+ family = AF_INET;
+ else
+ family = AF_INET6;
}
- if (((flags & OSMO_SOCK_F_BIND) && (flags & OSMO_SOCK_F_CONNECT)) &&
- !addrinfo_has_in6addr_any((const struct addrinfo **)res_loc, local_hosts_cnt) &&
+ /* if both sets are used, make sure there's at least 1 address of the
+ * same type on each set so that SCTP INIT/INIT-ACK can work. */
+ if (family == AF_INET6 && ((flags & OSMO_SOCK_F_BIND) && (flags & OSMO_SOCK_F_CONNECT)) &&
(loc_has_v4addr != rem_has_v4addr || loc_has_v6addr != rem_has_v6addr)) {
- LOGP(DLGLOBAL, LOGL_ERROR, "Invalid v4 vs v6 in local vs remote addresses\n");
- rc = -EINVAL;
- goto ret_freeaddrinfo;
+ if (!addrinfo_has_in6addr_any((const struct addrinfo **)res_loc, local_hosts_cnt)) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Invalid v4 vs v6 in local vs remote addresses: "
+ "local:%s%s remote:%s%s\n",
+ loc_has_v4addr ? " v4" : "", loc_has_v6addr ? " v6" : "",
+ rem_has_v4addr ? " v4" : "", rem_has_v6addr ? " v6" : "");
+ rc = -EINVAL;
+ goto ret_freeaddrinfo;
+ }
}
- sfd = socket_helper_multiaddr(loc_has_v6addr ? AF_INET6 : AF_INET,
- type, proto, flags);
+ sfd = socket_helper_multiaddr(family, type, proto, flags);
if (sfd < 0) {
rc = sfd;
goto ret_freeaddrinfo;
}
+ if (pars) {
+ if (pars->sctp.sockopt_auth_supported.set) {
+ /* RFC 5061 4.2.7: ASCONF also requires AUTH feature. */
+ rc = setsockopt_sctp_auth_supported(sfd, pars->sctp.sockopt_auth_supported.value);
+ if (rc < 0) {
+ int err = errno;
+ multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt);
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "cannot setsockopt(SCTP_AUTH_SUPPORTED) socket: %s:%u: %s\n",
+ strbuf, local_port, strerror(err));
+ if (pars->sctp.sockopt_auth_supported.abort_on_failure)
+ goto ret_close;
+ /* do not fail, some features such as Peer Primary Address won't be available
+ * unless configured system-wide through sysctl */
+ }
+ }
+
+ if (pars->sctp.sockopt_asconf_supported.set) {
+ rc = setsockopt_sctp_asconf_supported(sfd, pars->sctp.sockopt_asconf_supported.value);
+ if (rc < 0) {
+ int err = errno;
+ multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt);
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "cannot setsockopt(SCTP_ASCONF_SUPPORTED) socket: %s:%u: %s\n",
+ strbuf, local_port, strerror(err));
+ if (pars->sctp.sockopt_asconf_supported.abort_on_failure)
+ goto ret_close;
+ /* do not fail, some features such as Peer Primary Address won't be available
+ * unless configured system-wide through sysctl */
+ }
+ }
+
+ if (pars->sctp.sockopt_initmsg.set) {
+ rc = setsockopt_sctp_initmsg(sfd, pars);
+ if (rc < 0) {
+ int err = errno;
+ multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt);
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "cannot setsockopt(SCTP_INITMSG) socket: %s:%u: %s\n",
+ strbuf, local_port, strerror(err));
+ if (pars->sctp.sockopt_initmsg.abort_on_failure)
+ goto ret_close;
+ /* do not fail, some parameters will be left as the global default */
+ }
+ }
+ }
+
if (flags & OSMO_SOCK_F_BIND) {
/* Since so far we only allow IPPROTO_SCTP in this function,
no need to check below for "proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR" */
rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
&on, sizeof(on));
if (rc < 0) {
+ int err = errno;
multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt);
LOGP(DLGLOBAL, LOGL_ERROR,
"cannot setsockopt socket:"
" %s:%u: %s\n",
strbuf, local_port,
- strerror(errno));
+ strerror(err));
goto ret_close;
}
@@ -821,9 +1037,10 @@ int osmo_sock_init2_multiaddr(uint16_t family, uint16_t type, uint8_t proto,
rc = sctp_bindx(sfd, (struct sockaddr *)addrs_buf, local_hosts_cnt, SCTP_BINDX_ADD_ADDR);
if (rc == -1) {
+ int err = errno;
multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt);
LOGP(DLGLOBAL, LOGL_NOTICE, "unable to bind socket: %s:%u: %s\n",
- strbuf, local_port, strerror(errno));
+ strbuf, local_port, strerror(err));
rc = -ENODEV;
goto ret_close;
}
@@ -843,9 +1060,10 @@ int osmo_sock_init2_multiaddr(uint16_t family, uint16_t type, uint8_t proto,
rc = sctp_connectx(sfd, (struct sockaddr *)addrs_buf, remote_hosts_cnt, NULL);
if (rc != 0 && errno != EINPROGRESS) {
+ int err = errno;
multiaddr_snprintf(strbuf, sizeof(strbuf), remote_hosts, remote_hosts_cnt);
LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n",
- strbuf, remote_port, strerror(errno));
+ strbuf, remote_port, strerror(err));
rc = -ENODEV;
goto ret_close;
}
@@ -894,7 +1112,9 @@ int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
const char *host, uint16_t port, unsigned int flags)
{
struct addrinfo *result, *rp;
- int sfd, rc, on = 1;
+ int sfd = -1; /* initialize to avoid uninitialized false warnings on some gcc versions (11.1.0) */
+ int on = 1;
+ int rc;
if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
(OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) {
@@ -972,11 +1192,12 @@ int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
/*! fill \ref osmo_fd for a give sfd
* \param[out] ofd file descriptor (will be filled in)
* \param[in] sfd socket file descriptor
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
* \returns socket fd on success; negative on error
*
* This function fills the \a ofd structure.
*/
-static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
+static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd, unsigned int flags)
{
int rc;
@@ -986,6 +1207,14 @@ static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
ofd->fd = sfd;
ofd->when = OSMO_FD_READ;
+ /* if we're doing a non-blocking connect, the completion will be signaled
+ * by marking the fd as WRITE-able. So in this exceptional case, we're
+ * also interested in when the socket becomes write-able */
+ if ((flags & (OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_NONBLOCK)) ==
+ (OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_NONBLOCK)) {
+ ofd->when |= OSMO_FD_WRITE;
+ }
+
rc = osmo_fd_register(ofd);
if (rc < 0) {
close(sfd);
@@ -1011,7 +1240,7 @@ static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
const char *host, uint16_t port, unsigned int flags)
{
- return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags));
+ return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags), flags);
}
/*! Initialize a socket and fill \ref osmo_fd
@@ -1034,14 +1263,14 @@ int osmo_sock_init2_ofd(struct osmo_fd *ofd, int family, int type, int proto,
const char *remote_host, uint16_t remote_port, unsigned int flags)
{
return osmo_fd_init_ofd(ofd, osmo_sock_init2(family, type, proto, local_host,
- local_port, remote_host, remote_port, flags));
+ local_port, remote_host, remote_port, flags), flags);
}
int osmo_sock_init_osa_ofd(struct osmo_fd *ofd, int type, int proto,
const struct osmo_sockaddr *local,
const struct osmo_sockaddr *remote, unsigned int flags)
{
- return osmo_fd_init_ofd(ofd, osmo_sock_init_osa(type, proto, local, remote, flags));
+ return osmo_fd_init_ofd(ofd, osmo_sock_init_osa(type, proto, local, remote, flags), flags);
}
/*! Initialize a socket and fill \ref sockaddr
@@ -1090,6 +1319,160 @@ int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
}
+#ifdef HAVE_LIBSCTP
+/*! Add addresses to the multi-address (SCTP) socket active binding set
+ * \param[in] sfd The multi-address (SCTP) socket
+ * \param[in] addrs array of char pointers (strings), each containing local host name or IP address in string form
+ * \param[in] addrs_cnt length of addrs_hosts (in items)
+ * \returns 0 on success; negative on error
+ *
+ * This function only supports SCTP sockets so far, and hence it should be
+ * called only on socket file descriptions referencing that kind of sockets.
+ */
+int osmo_sock_multiaddr_add_local_addr(int sfd, const char **addrs, size_t addrs_cnt)
+{
+ struct osmo_sockaddr osa;
+ socklen_t slen = sizeof(osa);
+ uint16_t sfd_family;
+ uint16_t type = SOCK_STREAM ;/* Fixme: we assume fd is SOCK_STREAM */
+ uint8_t proto = IPPROTO_SCTP; /* Fixme: we assume fd is IPPROTO_SCTP */
+ struct addrinfo *res[OSMO_SOCK_MAX_ADDRS];
+ uint16_t port;
+ struct sockaddr_in6 addrs_buf[OSMO_SOCK_MAX_ADDRS];
+ unsigned int i;
+ int rc;
+ bool res_has_v4addr = false, res_has_v6addr = false;
+
+ rc = getsockname(sfd, &osa.u.sa, &slen);
+ if (rc < 0)
+ return rc; /* TODO: log error? */
+ sfd_family = osa.u.sa.sa_family;
+ port = osmo_sockaddr_port(&osa.u.sa);
+
+ if (sfd_family != AF_INET && sfd_family != AF_INET6)
+ return -EINVAL;
+
+ rc = addrinfo_helper_multi(res, AF_UNSPEC, type, proto, addrs,
+ addrs_cnt, port, true);
+ if (rc < 0)
+ return -EINVAL;
+
+ addrinfo_has_v4v6addr((const struct addrinfo **)res, addrs_cnt,
+ &res_has_v4addr, &res_has_v6addr);
+ if (sfd_family == AF_INET && !res_has_v4addr) {
+ rc = -EINVAL;
+ goto ret_free;
+ }
+
+ uint16_t new_addr_family;
+ if (sfd_family == AF_INET)
+ new_addr_family = AF_INET;
+ else if (sfd_family == AF_INET6 && !res_has_v4addr)
+ new_addr_family = AF_INET6;
+ else
+ new_addr_family = AF_UNSPEC;
+ rc = addrinfo_to_sockaddr(new_addr_family, (const struct addrinfo **)res,
+ addrs, addrs_cnt,
+ (uint8_t *)addrs_buf, sizeof(addrs_buf));
+ if (rc < 0) {
+ rc = -ENODEV;
+ goto ret_free;
+ }
+
+ rc = sctp_bindx(sfd, (struct sockaddr *)addrs_buf, addrs_cnt, SCTP_BINDX_ADD_ADDR);
+ if (rc == -1) {
+ int err = errno;
+ char strbuf[512];
+ multiaddr_snprintf(strbuf, sizeof(strbuf), addrs, addrs_cnt);
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Unable to bind socket to new addresses: %s:%u: %s\n",
+ strbuf, port, strerror(err));
+ rc = -ENODEV;
+ goto ret_free;
+ }
+
+ret_free:
+ for (i = 0; i < addrs_cnt; i++)
+ freeaddrinfo(res[i]);
+ return rc;
+}
+
+/*! Remove addresses from the multi-address (SCTP) socket active binding set
+ * \param[in] sfd The multi-address (SCTP) socket
+ * \param[in] addrs array of char pointers (strings), each containing local host name or IP address in string form
+ * \param[in] addrs_cnt length of addrs_hosts (in items)
+ * \returns 0 on success; negative on error
+ *
+ * This function only supports SCTP sockets so far, and hence it should be
+ * called only on socket file descriptions referencing that kind of sockets.
+ */
+int osmo_sock_multiaddr_del_local_addr(int sfd, const char **addrs, size_t addrs_cnt)
+{
+ struct osmo_sockaddr osa;
+ socklen_t slen = sizeof(osa);
+ uint16_t sfd_family;
+ uint16_t type = SOCK_STREAM ;/* Fixme: we assume fd is SOCK_STREAM */
+ uint8_t proto = IPPROTO_SCTP; /* Fixme: we assume fd is IPPROTO_SCTP */
+ struct addrinfo *res[OSMO_SOCK_MAX_ADDRS];
+ uint16_t port;
+ struct sockaddr_in6 addrs_buf[OSMO_SOCK_MAX_ADDRS];
+ unsigned int i;
+ int rc;
+ bool res_has_v4addr = false, res_has_v6addr = false;
+
+ rc = getsockname(sfd, &osa.u.sa, &slen);
+ if (rc < 0)
+ return rc; /* TODO: log error? */
+ sfd_family = osa.u.sa.sa_family;
+ port = osmo_sockaddr_port(&osa.u.sa);
+
+ if (sfd_family != AF_INET && sfd_family != AF_INET6)
+ return -EINVAL;
+
+ rc = addrinfo_helper_multi(res, AF_UNSPEC, type, proto, addrs,
+ addrs_cnt, port, true);
+ if (rc < 0)
+ return -EINVAL;
+
+ addrinfo_has_v4v6addr((const struct addrinfo **)res, addrs_cnt,
+ &res_has_v4addr, &res_has_v6addr);
+ if (sfd_family == AF_INET && !res_has_v4addr) {
+ rc = -EINVAL;
+ goto ret_free;
+ }
+
+ uint16_t del_addr_family;
+ if (sfd_family == AF_INET)
+ del_addr_family = AF_INET;
+ else if (sfd_family == AF_INET6 && !res_has_v4addr)
+ del_addr_family = AF_INET6;
+ else
+ del_addr_family = AF_UNSPEC;
+ rc = addrinfo_to_sockaddr(del_addr_family, (const struct addrinfo **)res,
+ addrs, addrs_cnt,
+ (uint8_t *)addrs_buf, sizeof(addrs_buf));
+ if (rc < 0) {
+ rc = -ENODEV;
+ goto ret_free;
+ }
+
+ rc = sctp_bindx(sfd, (struct sockaddr *)addrs_buf, addrs_cnt, SCTP_BINDX_REM_ADDR);
+ if (rc == -1) {
+ int err = errno;
+ char strbuf[512];
+ multiaddr_snprintf(strbuf, sizeof(strbuf), addrs, addrs_cnt);
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Unable to unbind socket from addresses: %s:%u: %s\n",
+ strbuf, port, strerror(err));
+ rc = -ENODEV;
+ goto ret_free;
+ }
+
+ret_free:
+ for (i = 0; i < addrs_cnt; i++)
+ freeaddrinfo(res[i]);
+ return rc;
+}
+#endif /* HAVE_LIBSCTP */
+
static int sockaddr_equal(const struct sockaddr *a,
const struct sockaddr *b, unsigned int len)
{
@@ -1118,6 +1501,25 @@ static int sockaddr_equal(const struct sockaddr *a,
return 0;
}
+/* linux has a default route:
+local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
+*/
+static int sockaddr_is_local_routed(const struct sockaddr *a)
+{
+#if __linux__
+ if (a->sa_family != AF_INET)
+ return 0;
+
+ uint32_t address = ((struct sockaddr_in *)a)->sin_addr.s_addr; /* already BE */
+ uint32_t eightmask = htonl(0xff000000); /* /8 mask */
+ uint32_t local_prefix_127 = htonl(0x7f000000); /* 127.0.0.0 */
+
+ if ((address & eightmask) == local_prefix_127)
+ return 1;
+#endif
+ return 0;
+}
+
/*! Determine if the given address is a local address
* \param[in] addr Socket Address
* \param[in] addrlen Length of socket address in bytes
@@ -1127,6 +1529,9 @@ int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
{
struct ifaddrs *ifaddr, *ifa;
+ if (sockaddr_is_local_routed(addr))
+ return 1;
+
if (getifaddrs(&ifaddr) == -1) {
LOGP(DLGLOBAL, LOGL_ERROR, "getifaddrs:"
" %s\n", strerror(errno));
@@ -1146,6 +1551,26 @@ int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
return 0;
}
+/*! Determine if the given address is an ANY address ("0.0.0.0", "::"). Port is not checked.
+ * \param[in] addr Socket Address
+ * \param[in] addrlen Length of socket address in bytes
+ * \returns 1 if address is ANY, 0 otherwise. -1 is address family not supported/detected.
+ */
+int osmo_sockaddr_is_any(const struct osmo_sockaddr *addr)
+{
+ switch (addr->u.sa.sa_family) {
+ case AF_INET6: {
+ struct in6_addr ip6_any = IN6ADDR_ANY_INIT;
+ return memcmp(&addr->u.sin6.sin6_addr,
+ &ip6_any, sizeof(ip6_any)) == 0;
+ }
+ case AF_INET:
+ return addr->u.sin.sin_addr.s_addr == INADDR_ANY;
+ default:
+ return -1;
+ }
+}
+
/*! Convert sockaddr_in to IP address as char string and port as uint16_t.
* \param[out] addr String buffer to write IP address to, or NULL.
* \param[out] addr_len Size of \a addr.
@@ -1225,6 +1650,101 @@ uint16_t osmo_sockaddr_port(const struct sockaddr *sa)
return 0;
}
+/*! Set sockaddr port content (to network byte order).
+ * \param[out] sa sockaddr to set the port of.
+ * \param[in] port port nr to set.
+ */
+void osmo_sockaddr_set_port(struct sockaddr *sa, uint16_t port)
+{
+ struct osmo_sockaddr *osa = (struct osmo_sockaddr *)sa;
+ switch (osa->u.sa.sa_family) {
+ case AF_INET6:
+ osa->u.sin6.sin6_port = htons(port);
+ return;
+ case AF_INET:
+ osa->u.sin.sin_port = htons(port);
+ return;
+ }
+}
+
+static unsigned int in6_addr_netmask_to_prefixlen(const struct in6_addr *netmask)
+{
+ #if defined(__linux__)
+ #define ADDRFIELD(i) s6_addr32[i]
+ #else
+ #define ADDRFIELD(i) __u6_addr.__u6_addr32[i]
+ #endif
+
+ unsigned int i, j, prefix = 0;
+
+ for (j = 0; j < 4; j++) {
+ uint32_t bits = netmask->ADDRFIELD(j);
+ uint8_t *b = (uint8_t *)&bits;
+ for (i = 0; i < 4; i++) {
+ while (b[i] & 0x80) {
+ prefix++;
+ b[i] = b[i] << 1;
+ }
+ }
+ }
+
+ #undef ADDRFIELD
+
+ return prefix;
+}
+
+static unsigned int in_addr_netmask_to_prefixlen(const struct in_addr *netmask)
+{
+ uint32_t bits = netmask->s_addr;
+ uint8_t *b = (uint8_t *)&bits;
+ unsigned int i, prefix = 0;
+
+ for (i = 0; i < 4; i++) {
+ while (b[i] & 0x80) {
+ prefix++;
+ b[i] = b[i] << 1;
+ }
+ }
+ return prefix;
+}
+
+/*! Convert netmask to prefix length representation
+ * \param[in] netmask sockaddr containing a netmask (consecutive list of 1-bit followed by consecutive list of 0-bit)
+ * \returns prefix length representation of the netmask (count of 1-bit from the start of the netmask), negative on error.
+ */
+int osmo_sockaddr_netmask_to_prefixlen(const struct osmo_sockaddr *netmask)
+{
+ switch (netmask->u.sa.sa_family) {
+ case AF_INET6:
+ return in6_addr_netmask_to_prefixlen(&netmask->u.sin6.sin6_addr);
+ case AF_INET:
+ return in_addr_netmask_to_prefixlen(&netmask->u.sin.sin_addr);
+ default:
+ return -ENOTSUP;
+ }
+}
+
+/*! Convert an IP address string (and port number) into a 'struct osmo_sockaddr'.
+ * \param[out] osa_out caller-allocated osmo_sockaddr storage
+ * \param[in] ipstr IP[v4,v6] address in string format
+ * \param[in] port port number (host byte order)
+ * \returns 0 on success; negative on error. */
+int osmo_sockaddr_from_str_and_uint(struct osmo_sockaddr *osa_out, const char *ipstr, uint16_t port)
+{
+ struct addrinfo *ai = addrinfo_helper(AF_UNSPEC, 0, 0, ipstr, port, true);
+
+ if (!ai)
+ return -EIO;
+
+ if (ai->ai_addrlen > sizeof(*osa_out))
+ return -ENOSPC;
+
+ memcpy(&osa_out->u.sa, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
+
+ return 0;
+}
+
/*! Initialize a unix domain socket (including bind/connect)
* \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
* \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
@@ -1270,7 +1790,7 @@ int osmo_sock_unix_init(uint16_t type, uint8_t proto,
sfd = socket(AF_UNIX, type, proto);
if (sfd < 0)
- return -1;
+ return -errno;
if (flags & OSMO_SOCK_F_CONNECT) {
rc = connect(sfd, (struct sockaddr *)&local, namelen);
@@ -1290,13 +1810,13 @@ int osmo_sock_unix_init(uint16_t type, uint8_t proto,
rc = osmo_sock_init_tail(sfd, type, flags);
if (rc < 0) {
close(sfd);
- sfd = -1;
+ sfd = rc;
}
return sfd;
err:
close(sfd);
- return -1;
+ return -errno;
}
/*! Initialize a unix domain socket and fill \ref osmo_fd
@@ -1313,7 +1833,7 @@ err:
int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
const char *socket_path, unsigned int flags)
{
- return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
+ return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags), flags);
}
/*! Get the IP and/or port number on socket in separate string buffers.
@@ -1349,6 +1869,97 @@ int osmo_sock_get_ip_and_port(int fd, char *ip, size_t ip_len, char *port, size_
return 0;
}
+#ifdef HAVE_LIBSCTP
+/*! Get multiple IP addresses and/or port number on socket in separate string buffers
+ * \param[in] fd file descriptor of socket.
+ * \param[out] ip_proto IPPROTO of the socket, eg: IPPROTO_SCTP.
+ * \param[out] ip Pointer to memory holding consecutive buffers of size ip_len.
+ * \param[out] ip_cnt length ip array pointer. on return it contains the number of addresses found.
+ * \param[in] ip_len length of each of the string buffer in the the ip array.
+ * \param[out] port number (will be filled in when not NULL).
+ * \param[in] port_len length of the port buffer.
+ * \param[in] local (true) or remote (false) name will get looked at.
+ * \returns 0 on success; negative otherwise.
+ *
+ * Upon return, ip_cnt can be set to a higher value than the one set by the
+ * caller. This can be used by the caller to find out the required array length
+ * and then obtaining by calling the function twice. Only up to ip_cnt addresses
+ * are filed in, as per the value provided by the caller.
+ *
+ * Usage example retrieving all (up to OSMO_SOCK_MAX_ADDRS, 32) bound IP addresses and bound port:
+ * char hostbuf[OSMO_SOCK_MAX_ADDRS][INET6_ADDRSTRLEN];
+ * size_t num_hostbuf = ARRAY_SIZE(hostbuf);
+ * char portbuf[6];
+ * rc = osmo_sock_multiaddr_get_ip_and_port(fd, IPPROTO_SCTP, &hostbuf[0][0], &num_hostbuf,
+ * sizeof(hostbuf[0]), portbuf, sizeof(portbuf), true);
+ * if (rc < 0)
+ * goto error;
+ * if (num_hostbuf > ARRAY_SIZE(hostbuf))
+ * goto not_enough_buffers;
+ */
+int osmo_sock_multiaddr_get_ip_and_port(int fd, int ip_proto, char *ip, size_t *ip_cnt, size_t ip_len,
+ char *port, size_t port_len, bool local)
+{
+ struct sockaddr *addrs = NULL;
+ unsigned int n_addrs, i;
+ void *addr_buf;
+ int rc;
+
+ switch (ip_proto) {
+ case IPPROTO_SCTP:
+ break; /* continue below */
+ default:
+ if (*ip_cnt == 0) {
+ *ip_cnt = 1;
+ return 0;
+ }
+ *ip_cnt = 1;
+ return osmo_sock_get_ip_and_port(fd, ip, ip_len, port, port_len, local);
+ }
+
+ rc = local ? sctp_getladdrs(fd, 0, &addrs) : sctp_getpaddrs(fd, 0, &addrs);
+ if (rc < 0)
+ return rc;
+ if (rc == 0)
+ return -ENOTCONN;
+
+ n_addrs = rc;
+ addr_buf = (void *)addrs;
+ for (i = 0; i < n_addrs; i++) {
+ struct sockaddr *sa_addr = (struct sockaddr *)addr_buf;
+ size_t addrlen;
+
+ if (i >= *ip_cnt)
+ break;
+
+ switch (sa_addr->sa_family) {
+ case AF_INET:
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ rc = -EINVAL;
+ goto free_addrs_ret;
+ }
+
+ rc = getnameinfo(sa_addr, addrlen, &ip[i * ip_len], ip_len,
+ port, port_len,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc < 0)
+ goto free_addrs_ret;
+ addr_buf += addrlen;
+ }
+
+ *ip_cnt = n_addrs;
+ rc = 0;
+free_addrs_ret:
+ local ? sctp_freeladdrs(addrs) : sctp_freepaddrs(addrs);
+ return rc;
+}
+#endif
+
/*! Get local IP address on socket
* \param[in] fd file descriptor of socket
* \param[out] ip IP address (will be filled in)
@@ -1410,6 +2021,114 @@ char *osmo_sock_get_name(const void *ctx, int fd)
return talloc_asprintf(ctx, "(%s)", str);
}
+#ifdef HAVE_LIBSCTP
+/*! Format multiple IP addresses and/or port number into a combined string buffer
+ * \param[out] str Destination string buffer.
+ * \param[in] str_len sizeof(str), usually OSMO_SOCK_MULTIADDR_PEER_STR_MAXLEN.
+ * \param[out] ip Pointer to memory holding ip_cnt consecutive buffers of size ip_len.
+ * \param[out] ip_cnt length ip array pointer. on return it contains the number of addresses found.
+ * \param[in] ip_len length of each of the string buffer in the the ip array.
+ * \param[out] port number (will be printed in when not NULL).
+ * \return String length as returned by snprintf(), or negative on error.
+ *
+ * This API expects an ip array as the one filled in by
+ * osmo_sock_multiaddr_get_ip_and_port(), and hence it's a good companion for
+ * that API.
+ */
+int osmo_multiaddr_ip_and_port_snprintf(char *str, size_t str_len,
+ const char *ip, size_t ip_cnt, size_t ip_len,
+ const char *portbuf)
+{
+ struct osmo_strbuf sb = { .buf = str, .len = str_len };
+ bool is_v6 = false;
+ unsigned int i;
+
+ if (ip_cnt == 0) {
+ OSMO_STRBUF_PRINTF(sb, "NULL:%s", portbuf);
+ return sb.chars_needed;
+ }
+ if (ip_cnt > 1)
+ OSMO_STRBUF_PRINTF(sb, "(");
+ else if ((is_v6 = !!strchr(&ip[0], ':'))) /* IPv6, add [] to separate from port. */
+ OSMO_STRBUF_PRINTF(sb, "[");
+
+ for (i = 0; i < ip_cnt - 1; i++)
+ OSMO_STRBUF_PRINTF(sb, "%s|", &ip[i * ip_len]);
+ OSMO_STRBUF_PRINTF(sb, "%s", &ip[i * ip_len]);
+
+ if (ip_cnt > 1)
+ OSMO_STRBUF_PRINTF(sb, ")");
+ else if (is_v6)
+ OSMO_STRBUF_PRINTF(sb, "]");
+ if (portbuf)
+ OSMO_STRBUF_PRINTF(sb, ":%s", portbuf);
+
+ return sb.chars_needed;
+}
+
+/*! Get address/port information on socket in provided string buffer, like "r=1.2.3.4:5<->l=6.7.8.9:10".
+ * This does not include braces like osmo_sock_get_name().
+ * \param[out] str Destination string buffer.
+ * \param[in] str_len sizeof(str), usually OSMO_SOCK_MULTIADDR_NAME_MAXLEN.
+ * \param[in] fd File descriptor of socket.
+ * \param[in] fd IPPROTO of the socket, eg: IPPROTO_SCTP.
+ * \return String length as returned by snprintf(), or negative on error.
+ */
+int osmo_sock_multiaddr_get_name_buf(char *str, size_t str_len, int fd, int sk_proto)
+{
+ char hostbuf[OSMO_SOCK_MAX_ADDRS][INET6_ADDRSTRLEN];
+ size_t num_hostbuf = ARRAY_SIZE(hostbuf);
+ char portbuf[6];
+ struct osmo_strbuf sb = { .buf = str, .len = str_len };
+
+ if (fd < 0) {
+ osmo_strlcpy(str, "<error-bad-fd>", str_len);
+ return sb.chars_needed;
+ }
+
+ switch (sk_proto) {
+ case IPPROTO_SCTP:
+ break; /* continue below */
+ default:
+ return osmo_sock_get_name_buf(str, str_len, fd);
+ }
+
+ /* get remote */
+ OSMO_STRBUF_PRINTF(sb, "r=");
+ if (osmo_sock_multiaddr_get_ip_and_port(fd, sk_proto, &hostbuf[0][0], &num_hostbuf,
+ sizeof(hostbuf[0]), portbuf, sizeof(portbuf), false) != 0) {
+ OSMO_STRBUF_PRINTF(sb, "NULL");
+ } else {
+ const bool need_more_bufs = num_hostbuf > ARRAY_SIZE(hostbuf);
+ if (need_more_bufs)
+ num_hostbuf = ARRAY_SIZE(hostbuf);
+ OSMO_STRBUF_APPEND(sb, osmo_multiaddr_ip_and_port_snprintf,
+ &hostbuf[0][0], num_hostbuf, sizeof(hostbuf[0]), portbuf);
+ if (need_more_bufs)
+ OSMO_STRBUF_PRINTF(sb, "<need-more-bufs!>");
+ }
+
+ OSMO_STRBUF_PRINTF(sb, "<->l=");
+
+ /* get local */
+ num_hostbuf = ARRAY_SIZE(hostbuf);
+ if (osmo_sock_multiaddr_get_ip_and_port(fd, sk_proto, &hostbuf[0][0], &num_hostbuf,
+ sizeof(hostbuf[0]), portbuf, sizeof(portbuf), true) != 0) {
+ OSMO_STRBUF_PRINTF(sb, "NULL");
+ } else {
+ const bool need_more_bufs = num_hostbuf > ARRAY_SIZE(hostbuf);
+ if (need_more_bufs)
+ num_hostbuf = ARRAY_SIZE(hostbuf);
+ OSMO_STRBUF_APPEND(sb, osmo_multiaddr_ip_and_port_snprintf,
+ &hostbuf[0][0], num_hostbuf, sizeof(hostbuf[0]), portbuf);
+ if (need_more_bufs)
+ OSMO_STRBUF_PRINTF(sb, "<need-more-bufs!>");
+ }
+
+ return sb.chars_needed;
+}
+#endif
+
/*! Get address/port information on socket in provided string buffer, like "r=1.2.3.4:5<->l=6.7.8.9:10".
* This does not include braces like osmo_sock_get_name().
* \param[out] str Destination string buffer.
@@ -1419,21 +2138,89 @@ char *osmo_sock_get_name(const void *ctx, int fd)
*/
int osmo_sock_get_name_buf(char *str, size_t str_len, int fd)
{
- char hostbuf_l[INET6_ADDRSTRLEN], hostbuf_r[INET6_ADDRSTRLEN];
- char portbuf_l[6], portbuf_r[6];
+ struct osmo_strbuf sb = { .buf = str, .len = str_len };
+ struct osmo_sockaddr osa;
+ struct sockaddr_un *sun;
+ socklen_t len;
int rc;
- /* get local */
- if ((rc = osmo_sock_get_ip_and_port(fd, hostbuf_l, sizeof(hostbuf_l), portbuf_l, sizeof(portbuf_l), true))) {
+ if (fd < 0) {
+ osmo_strlcpy(str, "<error-bad-fd>", str_len);
+ return -EBADF;
+ }
+
+
+ len = sizeof(osa.u.sas);
+ rc = getsockname(fd, &osa.u.sa, &len);
+ if (rc < 0) {
osmo_strlcpy(str, "<error-in-getsockname>", str_len);
return rc;
}
- /* get remote */
- if (osmo_sock_get_ip_and_port(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r, sizeof(portbuf_r), false) != 0)
- return snprintf(str, str_len, "r=NULL<->l=%s:%s", hostbuf_l, portbuf_l);
-
- return snprintf(str, str_len, "r=%s:%s<->l=%s:%s", hostbuf_r, portbuf_r, hostbuf_l, portbuf_l);
+ switch (osa.u.sa.sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ {
+ char hostbuf_l[INET6_ADDRSTRLEN], hostbuf_r[INET6_ADDRSTRLEN];
+ char portbuf_l[6], portbuf_r[6];
+
+ len = sizeof(osa.u.sas);
+ rc = getnameinfo(&osa.u.sa, len, hostbuf_l, sizeof(hostbuf_l),
+ portbuf_l, sizeof(portbuf_l),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc < 0) {
+ osmo_strlcpy(str, "<error-in-getnameinfo>", str_len);
+ return rc;
+ }
+ /* Now attempt to get remote: */
+ len = sizeof(osa.u.sas);
+ rc = getpeername(fd, &osa.u.sa, &len);
+ if (rc < 0) {
+ OSMO_STRBUF_PRINTF(sb, "r=NULL<->l=%s:%s", hostbuf_l, portbuf_l);
+ return sb.chars_needed;
+ }
+ len = sizeof(osa.u.sas);
+ rc = getnameinfo(&osa.u.sa, len, hostbuf_r, sizeof(hostbuf_r),
+ portbuf_r, sizeof(portbuf_r),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc < 0) {
+ OSMO_STRBUF_PRINTF(sb, "r=NULL<->l=%s:%s", hostbuf_l, portbuf_l);
+ return sb.chars_needed;
+ }
+ OSMO_STRBUF_PRINTF(sb, "r=%s:%s<->l=%s:%s", hostbuf_r, portbuf_r, hostbuf_l, portbuf_l);
+ return sb.chars_needed;
+ }
+ case AF_UNIX:
+ {
+ unsigned long long remote_pid;
+ bool have_remote_pid;
+#if defined(SO_PEERCRED)
+ struct ucred ucred;
+ len = sizeof(struct ucred);
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
+ have_remote_pid = false;
+ } else {
+ have_remote_pid = true;
+ remote_pid = (unsigned long long)ucred.pid;
+ }
+#else
+ #pragma message "SO_PEERCRED not available"
+ have_remote_pid = false;
+#endif
+ /* Make sure sun_path is NULL terminated: */
+ sun = (struct sockaddr_un *)&osa.u.sa;
+ sun->sun_path[sizeof(sun->sun_path) - 1] = '\0';
+ if (have_remote_pid)
+ OSMO_STRBUF_PRINTF(sb, "r=%llu<->", remote_pid);
+ else
+ OSMO_STRBUF_PRINTF(sb, "r=NULL<->");
+ OSMO_STRBUF_PRINTF(sb, "l=%s:%d", sun->sun_path, fd);
+ return sb.chars_needed;
+ }
+ default:
+ osmo_strlcpy(str, "<socket-family-no-supported>", str_len);
+ return -ENOTSUP;
+ }
}
/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10".
@@ -1704,6 +2491,65 @@ int osmo_sockaddr_local_ip(struct osmo_sockaddr *local_ip, const struct osmo_soc
return rc;
}
+/*! Copy the addr part, the IP address octets in network byte order, to a buffer.
+ * Useful for encoding network protocols.
+ * \param[out] dst Write octets to this buffer.
+ * \param[in] dst_maxlen Space available in buffer.
+ * \param[in] os Sockaddr to copy IP of.
+ * \return nr of octets written on success, negative on error.
+ */
+int osmo_sockaddr_to_octets(uint8_t *dst, size_t dst_maxlen, const struct osmo_sockaddr *os)
+{
+ const void *addr;
+ size_t len;
+ switch (os->u.sa.sa_family) {
+ case AF_INET:
+ addr = &os->u.sin.sin_addr;
+ len = sizeof(os->u.sin.sin_addr);
+ break;
+ case AF_INET6:
+ addr = &os->u.sin6.sin6_addr;
+ len = sizeof(os->u.sin6.sin6_addr);
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ if (dst_maxlen < len)
+ return -ENOSPC;
+ memcpy(dst, addr, len);
+ return len;
+}
+
+/*! Copy the addr part, the IP address octets in network byte order, from a buffer.
+ * Useful for decoding network protocols.
+ * \param[out] os Write IP address to this sockaddr.
+ * \param[in] src Source buffer to read IP address octets from.
+ * \param[in] src_len Number of octets to copy.
+ * \return number of octets read on success, negative on error.
+ */
+int osmo_sockaddr_from_octets(struct osmo_sockaddr *os, const void *src, size_t src_len)
+{
+ void *addr;
+ size_t len;
+ *os = (struct osmo_sockaddr){0};
+ switch (src_len) {
+ case sizeof(os->u.sin.sin_addr):
+ os->u.sa.sa_family = AF_INET;
+ addr = &os->u.sin.sin_addr;
+ len = sizeof(os->u.sin.sin_addr);
+ break;
+ case sizeof(os->u.sin6.sin6_addr):
+ os->u.sin6.sin6_family = AF_INET6;
+ addr = &os->u.sin6.sin6_addr;
+ len = sizeof(os->u.sin6.sin6_addr);
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ memcpy(addr, src, len);
+ return len;
+}
+
/*! Compare two osmo_sockaddr.
* \param[in] a
* \param[in] b
@@ -1746,52 +2592,68 @@ const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *sockaddr)
return osmo_sockaddr_to_str_buf(buf, sizeof(buf), sockaddr);
}
-/*! string-format a given osmo_sockaddr address into a user-supplied buffer
+/*! string-format a given osmo_sockaddr address into a user-supplied buffer.
+ * Same as osmo_sockaddr_to_str_buf() but returns a would-be length in snprintf() style.
* \param[in] buf user-supplied output buffer
* \param[in] buf_len size of the user-supplied output buffer in bytes
* \param[in] sockaddr the osmo_sockaddr to print
- * \return pointer to the string on success; NULL on error
+ * \return number of characters that would be written if the buffer is large enough, like snprintf().
*/
-char *osmo_sockaddr_to_str_buf(char *buf, size_t buf_len,
- const struct osmo_sockaddr *sockaddr)
+int osmo_sockaddr_to_str_buf2(char *buf, size_t buf_len, const struct osmo_sockaddr *sockaddr)
{
+ struct osmo_strbuf sb = { .buf = buf, .len = buf_len };
uint16_t port = 0;
- size_t written;
- if (buf_len < 5)
- return NULL;
- if (!sockaddr)
- return NULL;
+ if (!sockaddr) {
+ OSMO_STRBUF_PRINTF(sb, "NULL");
+ return sb.chars_needed;
+ }
switch (sockaddr->u.sa.sa_family) {
case AF_INET:
- written = osmo_sockaddr_to_str_and_uint(buf, buf_len, &port, &sockaddr->u.sa);
- if (written + 1 >= buf_len && port)
- return NULL;
+ OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_and_uint, &port, &sockaddr->u.sa);
if (port)
- snprintf(buf + written, buf_len - written, ":%u", port);
+ OSMO_STRBUF_PRINTF(sb, ":%u", port);
break;
case AF_INET6:
- buf[0] = '[';
- written = osmo_sockaddr_to_str_and_uint(buf + 1, buf_len - 1, &port, &sockaddr->u.sa);
- if (written + 2 >= buf_len)
- return NULL;
-
- if (written + 3 >= buf_len && port)
- return NULL;
-
+ OSMO_STRBUF_PRINTF(sb, "[");
+ OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_and_uint, &port, &sockaddr->u.sa);
+ OSMO_STRBUF_PRINTF(sb, "]");
if (port)
- snprintf(buf + 1 + written, buf_len - written - 1, "]:%u", port);
- else {
- buf[written + 1] = ']';
- buf[written + 2] = 0;
- }
+ OSMO_STRBUF_PRINTF(sb, ":%u", port);
break;
default:
- snprintf(buf, buf_len, "unsupported family %d", sockaddr->u.sa.sa_family);
- return buf;
+ OSMO_STRBUF_PRINTF(sb, "unsupported family %d", sockaddr->u.sa.sa_family);
+ break;
}
+ return sb.chars_needed;
+}
+
+/*! string-format a given osmo_sockaddr address into a talloc allocated buffer.
+ * Like osmo_sockaddr_to_str_buf2() but returns a talloc allocated string.
+ * \param[in] ctx talloc context to allocate from, e.g. OTC_SELECT.
+ * \param[in] sockaddr the osmo_sockaddr to print.
+ * \return human readable string.
+ */
+char *osmo_sockaddr_to_str_c(void *ctx, const struct osmo_sockaddr *sockaddr)
+{
+ OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_sockaddr_to_str_buf2, sockaddr)
+}
+
+/*! string-format a given osmo_sockaddr address into a user-supplied buffer.
+ * Like osmo_sockaddr_to_str_buf2() but returns buf, or NULL if too short.
+ * \param[in] buf user-supplied output buffer
+ * \param[in] buf_len size of the user-supplied output buffer in bytes
+ * \param[in] sockaddr the osmo_sockaddr to print
+ * \return pointer to the string on success; NULL on error
+ */
+char *osmo_sockaddr_to_str_buf(char *buf, size_t buf_len,
+ const struct osmo_sockaddr *sockaddr)
+{
+ int chars_needed = osmo_sockaddr_to_str_buf2(buf, buf_len, sockaddr);
+ if (chars_needed >= buf_len)
+ return NULL;
return buf;
}
@@ -1859,6 +2721,83 @@ int osmo_sock_set_priority(int fd, int prio)
return setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio));
}
+#ifdef HAVE_LIBSCTP
+/*! Fill in array of struct sctp_paddrinfo with each of the remote addresses of an SCTP socket
+ * \param[in] fd file descriptor of SCTP socket
+ * \param[out] pinfo Pointer to memory holding an array of struct sctp_paddrinfo (pinfo_cnt length).
+ * \param[in,out] pinfo_cnt length of pinfo array (in elements). On return it contains the number of addresses found.
+ * \returns 0 on success; negative otherwise
+ *
+ * Upon return, pinfo_cnt can be set to a higher value than the one set by the
+ * caller. This can be used by the caller to find out the required array length
+ * and then obtaining by calling the function twice. Only up to pinfo_cnt addresses
+ * are filled in, as per the value provided by the caller.
+ *
+ * Usage example retrieving struct sctp_paddrinfo for all (up to OSMO_SOCK_MAX_ADDRS, 32) remote IP addresses:
+ * struct sctp_paddrinfo pinfo[OSMO_SOCK_MAX_ADDRS];
+ * size_t pinfo_cnt = ARRAY_SIZE(pinfo);
+ * rc = osmo_sock_sctp_get_peer_addr_info(fd, &pinfo[0], &num_hostbuf, pinfo_cnt);
+ * if (rc < 0)
+ * goto error;
+ * if (pinfo_cnt > ARRAY_SIZE(hostbuf))
+ * goto not_enough_buffers;
+ */
+int osmo_sock_sctp_get_peer_addr_info(int fd, struct sctp_paddrinfo *pinfo, size_t *pinfo_cnt)
+{
+ struct sockaddr *addrs = NULL;
+ unsigned int n_addrs, i;
+ void *addr_buf;
+ int rc;
+ socklen_t optlen;
+
+ rc = sctp_getpaddrs(fd, 0, &addrs);
+
+ if (rc < 0)
+ return rc;
+ if (rc == 0)
+ return -ENOTCONN;
+
+ n_addrs = rc;
+ addr_buf = (void *)addrs;
+ for (i = 0; i < n_addrs; i++) {
+ struct sockaddr *sa_addr = (struct sockaddr *)addr_buf;
+ size_t addrlen;
+
+ switch (sa_addr->sa_family) {
+ case AF_INET:
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ rc = -EINVAL;
+ goto free_addrs_ret;
+ }
+
+ if (i >= *pinfo_cnt) {
+ addr_buf += addrlen;
+ continue;
+ }
+
+ memset(&pinfo[i], 0, sizeof(pinfo[0]));
+ memcpy(&pinfo[i].spinfo_address, sa_addr, addrlen);
+ optlen = sizeof(pinfo[0]);
+ rc = getsockopt(fd, SOL_SCTP, SCTP_GET_PEER_ADDR_INFO, &pinfo[i], &optlen);
+ if (rc < 0)
+ goto free_addrs_ret;
+
+ addr_buf += addrlen;
+ }
+
+ *pinfo_cnt = n_addrs;
+ rc = 0;
+free_addrs_ret:
+ sctp_freepaddrs(addrs);
+ return rc;
+}
+#endif
+
#endif /* HAVE_SYS_SOCKET_H */
/*! @} */
diff --git a/src/core/soft_uart.c b/src/core/soft_uart.c
new file mode 100644
index 00000000..f969ab70
--- /dev/null
+++ b/src/core/soft_uart.c
@@ -0,0 +1,518 @@
+/*! \file soft_uart.c
+ * Software UART implementation. */
+/*
+ * (C) 2022 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/soft_uart.h>
+
+/*! Rx/Tx flow state of a soft-UART */
+enum suart_flow_state {
+ SUART_FLOW_ST_IDLE, /*!< waiting for a start bit or Tx data */
+ SUART_FLOW_ST_DATA, /*!< receiving/transmitting data bits */
+ SUART_FLOW_ST_PARITY, /*!< receiving/transmitting parity bits */
+ SUART_FLOW_ST_STOP, /*!< receiving/transmitting stop bits */
+};
+
+/*! Internal state of a soft-UART */
+struct osmo_soft_uart {
+ struct osmo_soft_uart_cfg cfg;
+ const char *name;
+ /* modem status (bitmask of OSMO_SUART_STATUS_F_*) */
+ unsigned int status;
+ struct {
+ bool running;
+ uint8_t bit_count;
+ uint8_t shift_reg;
+ struct msgb *msg;
+ ubit_t parity_bit; /* 0 (even) / 1 (odd) */
+ unsigned int flags;
+ struct osmo_timer_list timer;
+ enum suart_flow_state flow_state;
+ } rx;
+ struct {
+ bool running;
+ uint8_t bit_count;
+ uint8_t shift_reg;
+ ubit_t parity_bit; /* 0 (even) / 1 (odd) */
+ enum suart_flow_state flow_state;
+ } tx;
+};
+
+/*! Default soft-UART configuration (8-N-1) */
+const struct osmo_soft_uart_cfg osmo_soft_uart_default_cfg = {
+ .num_data_bits = 8,
+ .num_stop_bits = 1,
+ .parity_mode = OSMO_SUART_PARITY_NONE,
+ .rx_buf_size = 1024,
+ .rx_timeout_ms = 100,
+ .flow_ctrl_mode = OSMO_SUART_FLOW_CTRL_NONE,
+};
+
+/*************************************************************************
+ * Receiver
+ *************************************************************************/
+
+/*! Flush the receive buffer, passing ownership of the msgb to the .rx_cb().
+ * \param[in] suart soft-UART instance holding the receive buffer. */
+void osmo_soft_uart_flush_rx(struct osmo_soft_uart *suart)
+{
+ if (suart->rx.msg && msgb_length(suart->rx.msg)) {
+ osmo_timer_del(&suart->rx.timer);
+ if (suart->cfg.rx_cb) {
+ suart->cfg.rx_cb(suart->cfg.priv, suart->rx.msg, suart->rx.flags);
+ /* call-back has taken ownership of msgb, no need to free() here */
+ suart->rx.msg = msgb_alloc_c(suart, suart->cfg.rx_buf_size, "soft_uart_rx");
+ } else {
+ msgb_reset(suart->rx.msg);
+ }
+ }
+}
+
+/* one character was received; add to receive buffer and notify user, if needed */
+static void suart_rx_ch(struct osmo_soft_uart *suart, uint8_t ch)
+{
+ unsigned int msg_len;
+
+ OSMO_ASSERT(suart->rx.msg);
+ msgb_put_u8(suart->rx.msg, ch);
+ msg_len = msgb_length(suart->rx.msg);
+
+ if (msg_len >= suart->cfg.rx_buf_size || suart->rx.flags) {
+ /* either the buffer is full, or we hit a parity and/or a framing error */
+ osmo_soft_uart_flush_rx(suart);
+ } else if (msg_len == 1) {
+ /* first character in new message: start timer */
+ osmo_timer_schedule(&suart->rx.timer, suart->cfg.rx_timeout_ms / 1000,
+ (suart->cfg.rx_timeout_ms % 1000) * 1000);
+ }
+}
+
+/* receive a single bit */
+static inline void suart_rx_bit(struct osmo_soft_uart *suart, const ubit_t bit)
+{
+ switch (suart->rx.flow_state) {
+ case SUART_FLOW_ST_IDLE:
+ if (bit == 0) { /* start bit condition */
+ suart->rx.flow_state = SUART_FLOW_ST_DATA;
+ suart->rx.flags = 0x00;
+ suart->rx.shift_reg = 0;
+ suart->rx.bit_count = 0;
+ suart->rx.parity_bit = 0;
+ }
+ break;
+ case SUART_FLOW_ST_DATA:
+ suart->rx.bit_count++;
+ suart->rx.shift_reg >>= 1;
+ if (bit != 0) {
+ suart->rx.parity_bit = !suart->rx.parity_bit; /* flip */
+ suart->rx.shift_reg |= 0x80;
+ }
+ if (suart->rx.bit_count >= suart->cfg.num_data_bits) {
+ /* we have accumulated enough data bits */
+ if (suart->cfg.parity_mode != OSMO_SUART_PARITY_NONE)
+ suart->rx.flow_state = SUART_FLOW_ST_PARITY;
+ else
+ suart->rx.flow_state = SUART_FLOW_ST_STOP;
+ /* align the register if needed */
+ if (suart->cfg.num_data_bits < 8)
+ suart->rx.shift_reg >>= (8 - suart->cfg.num_data_bits);
+ }
+ break;
+ case SUART_FLOW_ST_PARITY:
+ switch (suart->cfg.parity_mode) {
+ case OSMO_SUART_PARITY_EVEN:
+ /* number of 1-bits (in both data and parity) shall be even */
+ if (suart->rx.parity_bit != bit)
+ suart->rx.flags |= OSMO_SUART_F_PARITY_ERROR;
+ break;
+ case OSMO_SUART_PARITY_ODD:
+ /* number of 1-bits (in both data and parity) shall be odd */
+ if (suart->rx.parity_bit == bit)
+ suart->rx.flags |= OSMO_SUART_F_PARITY_ERROR;
+ break;
+ case OSMO_SUART_PARITY_MARK:
+ /* parity bit must always be 1 */
+ if (bit != 1)
+ suart->rx.flags |= OSMO_SUART_F_PARITY_ERROR;
+ break;
+ case OSMO_SUART_PARITY_SPACE:
+ /* parity bit must always be 0 */
+ if (bit != 0)
+ suart->rx.flags |= OSMO_SUART_F_PARITY_ERROR;
+ break;
+ case OSMO_SUART_PARITY_NONE: /* shall not happen */
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ suart->rx.flow_state = SUART_FLOW_ST_STOP;
+ break;
+ case SUART_FLOW_ST_STOP:
+ suart->rx.bit_count++;
+ if (bit != 1)
+ suart->rx.flags |= OSMO_SUART_F_FRAMING_ERROR;
+
+ if (suart->rx.bit_count >= (suart->cfg.num_data_bits + suart->cfg.num_stop_bits)) {
+ /* we have accumulated enough stop bits */
+ suart_rx_ch(suart, suart->rx.shift_reg);
+ suart->rx.flow_state = SUART_FLOW_ST_IDLE;
+ }
+ break;
+ }
+}
+
+/* receive timer expiration: flush rx-buffer to user call-back */
+static void suart_rx_timer_cb(void *data)
+{
+ struct osmo_soft_uart *suart = data;
+ osmo_soft_uart_flush_rx(suart);
+}
+
+/*! Feed a number of unpacked bits into the soft-UART receiver.
+ * \param[in] suart soft-UART instance to feed bits into.
+ * \param[in] ubits pointer to the unpacked bits.
+ * \param[in] n_ubits number of unpacked bits to be fed.
+ * \returns 0 on success; negative on error.
+ * -EAGAIN indicates that the receiver is disabled. */
+int osmo_soft_uart_rx_ubits(struct osmo_soft_uart *suart, const ubit_t *ubits, size_t n_ubits)
+{
+ if (!suart->rx.running)
+ return -EAGAIN;
+ for (size_t i = 0; i < n_ubits; i++)
+ suart_rx_bit(suart, ubits[i]);
+ return 0;
+}
+
+/*************************************************************************
+ * Transmitter
+ *************************************************************************/
+
+/* pull a single bit out of the UART transmitter */
+static inline ubit_t suart_tx_bit(struct osmo_soft_uart *suart, struct msgb *msg)
+{
+ ubit_t tx_bit = 1;
+
+ switch (suart->tx.flow_state) {
+ case SUART_FLOW_ST_IDLE:
+ if (msg && msgb_length(msg) > 0) { /* if we have pending data */
+ suart->tx.shift_reg = msgb_pull_u8(msg);
+ suart->tx.flow_state = SUART_FLOW_ST_DATA;
+ suart->tx.bit_count = 0;
+ suart->tx.parity_bit = 0;
+ tx_bit = 0;
+ }
+ break;
+ case SUART_FLOW_ST_DATA:
+ tx_bit = suart->tx.shift_reg & 1;
+ suart->tx.parity_bit ^= tx_bit;
+ suart->tx.shift_reg >>= 1;
+ suart->tx.bit_count++;
+ if (suart->tx.bit_count >= suart->cfg.num_data_bits) {
+ /* we have transmitted all data bits */
+ if (suart->cfg.parity_mode != OSMO_SUART_PARITY_NONE)
+ suart->tx.flow_state = SUART_FLOW_ST_PARITY;
+ else
+ suart->tx.flow_state = SUART_FLOW_ST_STOP;
+ }
+ break;
+ case SUART_FLOW_ST_PARITY:
+ switch (suart->cfg.parity_mode) {
+ case OSMO_SUART_PARITY_EVEN:
+ /* number of 1-bits (in both data and parity) shall be even */
+ tx_bit = suart->tx.parity_bit;
+ break;
+ case OSMO_SUART_PARITY_ODD:
+ /* number of 1-bits (in both data and parity) shall be odd */
+ tx_bit = !suart->tx.parity_bit;
+ break;
+ case OSMO_SUART_PARITY_MARK:
+ /* parity bit must always be 1 */
+ tx_bit = 1;
+ break;
+ case OSMO_SUART_PARITY_SPACE:
+ /* parity bit must always be 0 */
+ tx_bit = 0;
+ break;
+ case OSMO_SUART_PARITY_NONE:
+ default: /* shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ suart->tx.flow_state = SUART_FLOW_ST_STOP;
+ break;
+ case SUART_FLOW_ST_STOP:
+ suart->tx.bit_count++;
+ if (suart->tx.bit_count >= (suart->cfg.num_data_bits + suart->cfg.num_stop_bits)) {
+ /* we have transmitted all stop bits, we're done */
+ suart->tx.flow_state = SUART_FLOW_ST_IDLE;
+ }
+ break;
+ }
+
+ return tx_bit;
+}
+
+/* pull pending bits out of the UART */
+static size_t suart_tx_pending(struct osmo_soft_uart *suart, ubit_t *ubits, size_t n_ubits)
+{
+ size_t i;
+
+ for (i = 0; i < n_ubits; i++) {
+ if (suart->tx.flow_state == SUART_FLOW_ST_IDLE)
+ break;
+ ubits[i] = suart_tx_bit(suart, NULL);
+ }
+
+ return i;
+}
+
+/*! Pull a number of unpacked bits out of the soft-UART transmitter.
+ * \param[in] suart soft-UART instance to pull the bits from.
+ * \param[out] ubits pointer to a buffer where to store pulled bits.
+ * \param[in] n_ubits number of unpacked bits to be pulled.
+ * \returns number of bits pulled (may be less than n_ubits); negative on error.
+ * -EAGAIN indicates that the transmitter is disabled. */
+int osmo_soft_uart_tx_ubits(struct osmo_soft_uart *suart, ubit_t *ubits, size_t n_ubits)
+{
+ const struct osmo_soft_uart_cfg *cfg = &suart->cfg;
+ size_t n_frame_bits, n_chars;
+ struct msgb *msg = NULL;
+
+ if (OSMO_UNLIKELY(n_ubits == 0))
+ return -EINVAL;
+
+ if (!suart->tx.running)
+ return -EAGAIN;
+
+ switch (suart->cfg.flow_ctrl_mode) {
+ case OSMO_SUART_FLOW_CTRL_DTR_DSR:
+ /* if DSR is de-asserted, Tx pending bits and suspend */
+ if (~suart->status & OSMO_SUART_STATUS_F_DSR)
+ return suart_tx_pending(suart, ubits, n_ubits);
+ /* else: keep transmitting as usual */
+ break;
+ case OSMO_SUART_FLOW_CTRL_RTS_CTS:
+ /* if CTS is de-asserted, Tx pending bits and suspend */
+ if (~suart->status & OSMO_SUART_STATUS_F_CTS)
+ return suart_tx_pending(suart, ubits, n_ubits);
+ /* else: keep transmitting as usual */
+ break;
+ case OSMO_SUART_FLOW_CTRL_NONE:
+ default:
+ break;
+ }
+
+ /* calculate UART frame size for the effective config */
+ n_frame_bits = 1 + cfg->num_data_bits + cfg->num_stop_bits;
+ if (cfg->parity_mode != OSMO_SUART_PARITY_NONE)
+ n_frame_bits += 1;
+
+ /* calculate the number of characters we can fit into n_ubits */
+ n_chars = n_ubits / n_frame_bits;
+ if (n_chars == 0) {
+ /* we can transmit at least one character */
+ if (suart->tx.flow_state == SUART_FLOW_ST_IDLE)
+ n_chars = 1;
+ }
+
+ if (n_chars > 0) {
+ /* allocate a Tx buffer msgb */
+ msg = msgb_alloc_c(suart, n_chars, "soft_uart_tx");
+ OSMO_ASSERT(msg != NULL);
+
+ /* call the .tx_cb() to populate the Tx buffer */
+ OSMO_ASSERT(cfg->tx_cb != NULL);
+ suart->cfg.tx_cb(cfg->priv, msg);
+ }
+
+ for (size_t i = 0; i < n_ubits; i++)
+ ubits[i] = suart_tx_bit(suart, msg);
+ msgb_free(msg);
+
+ return n_ubits;
+}
+
+/*! Get the modem status bitmask of the given soft-UART.
+ * \param[in] suart soft-UART instance to get the modem status.
+ * \returns bitmask of OSMO_SUART_STATUS_F_*. */
+unsigned int osmo_soft_uart_get_status(const struct osmo_soft_uart *suart)
+{
+ return suart->status;
+}
+
+/*! Set the modem status bitmask of the given soft-UART.
+ * \param[in] suart soft-UART instance to set the modem status.
+ * \param[in] status bitmask of OSMO_SUART_STATUS_F_*.
+ * \returns 0 on success; negative on error. */
+int osmo_soft_uart_set_status(struct osmo_soft_uart *suart, unsigned int status)
+{
+ const struct osmo_soft_uart_cfg *cfg = &suart->cfg;
+
+ if (cfg->status_change_cb != NULL) {
+ if (suart->status != status)
+ cfg->status_change_cb(cfg->priv, status);
+ }
+
+ suart->status = status;
+ return 0;
+}
+
+/*! Activate/deactivate a modem status line of the given soft-UART.
+ * \param[in] suart soft-UART instance to update the modem status.
+ * \param[in] line a modem status line, one of OSMO_SUART_STATUS_F_*.
+ * \param[in] active activate (true) or deactivate (false) the line. */
+void osmo_soft_uart_set_status_line(struct osmo_soft_uart *suart,
+ enum osmo_soft_uart_status line,
+ bool active)
+{
+ unsigned int status = suart->status;
+
+ if (active) /* assert the given line */
+ status |= line;
+ else /* de-assert the given line */
+ status &= ~line;
+
+ osmo_soft_uart_set_status(suart, status);
+}
+
+
+/*************************************************************************
+ * Management / Initialization
+ *************************************************************************/
+
+/*! Allocate a soft-UART instance.
+ * \param[in] ctx parent talloc context.
+ * \param[in] name name of the soft-UART instance.
+ * \param[in] cfg initial configuration of the soft-UART instance.
+ * \returns pointer to allocated soft-UART instance; NULL on error. */
+struct osmo_soft_uart *osmo_soft_uart_alloc(void *ctx, const char *name,
+ const struct osmo_soft_uart_cfg *cfg)
+{
+ struct osmo_soft_uart *suart = talloc_zero(ctx, struct osmo_soft_uart);
+ if (!suart)
+ return NULL;
+ suart->name = talloc_strdup(suart, name);
+
+ OSMO_ASSERT(cfg != NULL);
+ suart->cfg = *cfg;
+
+ return suart;
+}
+
+/*! Release memory taken by the given soft-UART.
+ * \param[in] suart soft-UART instance to be free()d. */
+void osmo_soft_uart_free(struct osmo_soft_uart *suart)
+{
+ if (suart == NULL)
+ return;
+
+ osmo_timer_del(&suart->rx.timer);
+ msgb_free(suart->rx.msg);
+
+ talloc_free((void *)suart->name);
+ talloc_free(suart);
+}
+
+/*! Change soft-UART configuration to the user-provided config.
+ * \param[in] suart soft-UART instance to be re-configured.
+ * \param[in] cfg the user-provided config to be applied.
+ * \returns 0 on success; negative on error. */
+int osmo_soft_uart_configure(struct osmo_soft_uart *suart, const struct osmo_soft_uart_cfg *cfg)
+{
+ /* consistency checks on the configuration */
+ if (cfg->num_data_bits > 8 || cfg->num_data_bits == 0)
+ return -EINVAL;
+ if (cfg->num_stop_bits == 0)
+ return -EINVAL;
+ if (cfg->parity_mode < 0 || cfg->parity_mode >= _OSMO_SUART_PARITY_NUM)
+ return -EINVAL;
+ if (cfg->rx_buf_size == 0)
+ return -EINVAL;
+
+ if (suart->cfg.rx_buf_size > cfg->rx_buf_size ||
+ suart->cfg.rx_timeout_ms > cfg->rx_timeout_ms) {
+ osmo_soft_uart_flush_rx(suart);
+ }
+
+ suart->cfg = *cfg;
+
+ osmo_timer_setup(&suart->rx.timer, suart_rx_timer_cb, suart);
+
+ return 0;
+}
+
+/*! Get a name for the given soft-UART instance.
+ * \param[in] suart soft-UART instance to get the name from.
+ * \returns name of the given soft-UART instance. */
+const char *osmo_soft_uart_get_name(const struct osmo_soft_uart *suart)
+{
+ return suart->name;
+}
+
+/*! Set a new name for the given soft-UART instance.
+ * \param[in] suart soft-UART instance to set the name for.
+ * \param[in] name the new name. */
+void osmo_soft_uart_set_name(struct osmo_soft_uart *suart, const char *name)
+{
+ osmo_talloc_replace_string(suart, (char **)&suart->name, name);
+}
+
+/*! Enable/disable receiver of the given soft-UART.
+ * \param[in] suart soft-UART instance to be re-configured.
+ * \param[in] enable enable/disable state of the receiver.
+ * \returns 0 on success; negative on error. */
+int osmo_soft_uart_set_rx(struct osmo_soft_uart *suart, bool enable)
+{
+ if (!enable && suart->rx.running) {
+ osmo_soft_uart_flush_rx(suart);
+ suart->rx.running = false;
+ suart->rx.flow_state = SUART_FLOW_ST_IDLE;
+ } else if (enable && !suart->rx.running) {
+ if (!suart->rx.msg)
+ suart->rx.msg = msgb_alloc_c(suart, suart->cfg.rx_buf_size, "soft_uart_rx");
+ suart->rx.running = true;
+ suart->rx.flow_state = SUART_FLOW_ST_IDLE;
+ }
+
+ return 0;
+}
+
+/*! Enable/disable transmitter of the given soft-UART.
+ * \param[in] suart soft-UART instance to be re-configured.
+ * \param[in] enable enable/disable state of the transmitter.
+ * \returns 0 on success; negative on error. */
+int osmo_soft_uart_set_tx(struct osmo_soft_uart *suart, bool enable)
+{
+ if (!enable && suart->tx.running) {
+ suart->tx.running = false;
+ suart->tx.flow_state = SUART_FLOW_ST_IDLE;
+ } else if (enable && !suart->tx.running) {
+ suart->tx.running = true;
+ suart->tx.flow_state = SUART_FLOW_ST_IDLE;
+ }
+
+ return 0;
+}
diff --git a/src/core/stat_item.c b/src/core/stat_item.c
new file mode 100644
index 00000000..804972bf
--- /dev/null
+++ b/src/core/stat_item.c
@@ -0,0 +1,463 @@
+/*! \file stat_item.c
+ * utility routines for keeping statistical values */
+/*
+ * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2015 by sysmocom - s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*! \addtogroup osmo_stat_item
+ * @{
+ *
+ * This osmo_stat_item module adds instrumentation capabilities to
+ * gather measurement and statistical values in a similar fashion to
+ * what we have as \ref osmo_counter_group.
+ *
+ * As opposed to counters, osmo_stat_item do not increment but consist
+ * of a configurable-sized FIFO, which can store not only the current
+ * (most recent) value, but also historic values.
+ *
+ * The only supported value type is an int32_t.
+ *
+ * Getting values from osmo_stat_item is usually done at a high level
+ * through the stats API (stats.c). It uses item->stats_next_id to
+ * store what has been sent to all enabled reporters. It is also
+ * possible to read from osmo_stat_item directly, without modifying
+ * its state, by storing next_id outside of osmo_stat_item.
+ *
+ * Each value stored in the FIFO of an osmo_stat_item has an associated
+ * value_id. The value_id is increased with each value, so (until the
+ * counter wraps) more recent values will have higher values.
+ *
+ * When a new value is set, the oldest value in the FIFO gets silently
+ * overwritten. Lost values are skipped when getting values from the
+ * item.
+ *
+ */
+
+/* Struct overview:
+ *
+ * Group and item descriptions:
+ * Each group description exists once as osmo_stat_item_group_desc,
+ * each such group description lists N osmo_stat_item_desc, i.e. describes N stat items.
+ *
+ * Actual stats:
+ * The global osmo_stat_item_groups llist contains all group instances, each points at a group description.
+ * This list mixes all types of groups in a single llist, where each instance points at its group desc and has an index.
+ * There are one or more instances of each group, each storing stats for a distinct object (for example, one description
+ * for a BTS group, and any number of BTS instances with independent stats). A group is identified by a group index nr
+ * and possibly also a given name for that particular index (e.g. in osmo-mgw, a group instance is named
+ * "virtual-trunk-0" and can be looked up by that name instead of its more or less arbitrary group index number).
+ *
+ * Each group instance contains one osmo_stat_item instance per global stat item description.
+ * Each osmo_stat_item keeps track of the values for the current reporting period (min, last, max, sum, n),
+ * and also stores the set of values reported at the end of the previous reporting period.
+ *
+ * const osmo_stat_item_group_desc foo
+ * +-- group_name_prefix = "foo"
+ * +-- item_desc[] (array of osmo_stat_item_desc)
+ * +-- osmo_stat_item_desc bar
+ * | +-- name = "bar"
+ * | +-- description
+ * | +-- unit
+ * | +-- default_value
+ * |
+ * +-- osmo_stat_item_desc: baz
+ * +-- ...
+ *
+ * const osmo_stat_item_group_desc moo
+ * +-- group_name_prefix = "moo"
+ * +-- item_desc[]
+ * +-- osmo_stat_item_desc goo
+ * | +-- name = "goo"
+ * | +-- description
+ * | +-- unit
+ * | +-- default_value
+ * |
+ * +-- osmo_stat_item_desc: loo
+ * +-- ...
+ *
+ * osmo_stat_item_groups (llist of osmo_stat_item_group)
+ * |
+ * +-- group: foo[0]
+ * | +-- desc --> osmo_stat_item_group_desc foo
+ * | +-- idx = 0
+ * | +-- name = NULL (no given name for this group instance)
+ * | +-- items[]
+ * | |
+ * | [0] --> osmo_stat_item instance for "bar"
+ * | | +-- desc --> osmo_stat_item_desc bar (see above)
+ * | | +-- value.{min, last, max, n, sum}
+ * | | +-- reported.{min, last, max, n, sum}
+ * | |
+ * | [1] --> osmo_stat_item instance for "baz"
+ * | | +-- desc --> osmo_stat_item_desc baz
+ * | | +-- value.{min, last, max, n, sum}
+ * | | +-- reported.{min, last, max, n, sum}
+ * | .
+ * | :
+ * |
+ * +-- group: foo[1]
+ * | +-- desc --> osmo_stat_item_group_desc foo
+ * | +-- idx = 1
+ * | +-- name = "special-foo" (instance can be looked up by this index-name)
+ * | +-- items[]
+ * | |
+ * | [0] --> osmo_stat_item instance for "bar"
+ * | | +-- desc --> osmo_stat_item_desc bar
+ * | | +-- value.{min, last, max, n, sum}
+ * | | +-- reported.{min, last, max, n, sum}
+ * | |
+ * | [1] --> osmo_stat_item instance for "baz"
+ * | | +-- desc --> osmo_stat_item_desc baz
+ * | | +-- value.{min, last, max, n, sum}
+ * | | +-- reported.{min, last, max, n, sum}
+ * | .
+ * | :
+ * |
+ * +-- group: moo[0]
+ * | +-- desc --> osmo_stat_item_group_desc moo
+ * | +-- idx = 0
+ * | +-- name = NULL
+ * | +-- items[]
+ * | |
+ * | [0] --> osmo_stat_item instance for "goo"
+ * | | +-- desc --> osmo_stat_item_desc goo
+ * | | +-- value.{min, last, max, n, sum}
+ * | | +-- reported.{min, last, max, n, sum}
+ * | |
+ * | [1] --> osmo_stat_item instance for "loo"
+ * | | +-- desc --> osmo_stat_item_desc loo
+ * | | +-- value.{min, last, max, n, sum}
+ * | | +-- reported.{min, last, max, n, sum}
+ * | .
+ * | :
+ * .
+ * :
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/stat_item.h>
+
+#include <stat_item_internal.h>
+
+/*! global list of stat_item groups */
+static LLIST_HEAD(osmo_stat_item_groups);
+
+/*! talloc context from which we allocate */
+static void *tall_stat_item_ctx;
+
+/*! Allocate a new group of counters according to description.
+ * Allocate a group of stat items described in \a desc from talloc context \a ctx,
+ * giving the new group the index \a idx.
+ * \param[in] ctx \ref talloc context
+ * \param[in] desc Statistics item group description
+ * \param[in] idx Index of new stat item group
+ */
+struct osmo_stat_item_group *osmo_stat_item_group_alloc(void *ctx,
+ const struct osmo_stat_item_group_desc *group_desc,
+ unsigned int idx)
+{
+ unsigned int group_size;
+ unsigned int item_idx;
+ struct osmo_stat_item *items;
+
+ struct osmo_stat_item_group *group;
+
+ group_size = sizeof(struct osmo_stat_item_group) +
+ group_desc->num_items * sizeof(struct osmo_stat_item *);
+
+ if (!ctx)
+ ctx = tall_stat_item_ctx;
+
+ group = talloc_zero_size(ctx, group_size);
+ if (!group)
+ return NULL;
+
+ group->desc = group_desc;
+ group->idx = idx;
+
+ items = talloc_array(group, struct osmo_stat_item, group_desc->num_items);
+ OSMO_ASSERT(items);
+ for (item_idx = 0; item_idx < group_desc->num_items; item_idx++) {
+ struct osmo_stat_item *item = &items[item_idx];
+ const struct osmo_stat_item_desc *item_desc = &group_desc->item_desc[item_idx];
+ group->items[item_idx] = item;
+ *item = (struct osmo_stat_item){
+ .desc = item_desc,
+ .value = {
+ .n = 0,
+ .last = item_desc->default_value,
+ .min = item_desc->default_value,
+ .max = item_desc->default_value,
+ .sum = 0,
+ },
+ };
+ }
+
+ llist_add(&group->list, &osmo_stat_item_groups);
+ return group;
+}
+
+/*! Free the memory for the specified group of stat items */
+void osmo_stat_item_group_free(struct osmo_stat_item_group *grp)
+{
+ if (!grp)
+ return;
+
+ llist_del(&grp->list);
+ talloc_free(grp);
+}
+
+/*! Get statistics item from group, identified by index idx
+ * \param[in] grp Rate counter group
+ * \param[in] idx Index of the counter to retrieve
+ * \returns rate counter requested
+ */
+struct osmo_stat_item *osmo_stat_item_group_get_item(struct osmo_stat_item_group *grp, unsigned int idx)
+{
+ return grp->items[idx];
+}
+
+/*! Set a name for the statistics item group to be used instead of index value
+ at report time.
+ * \param[in] statg Statistics item group
+ * \param[in] name Name identifier to assign to the statistics item group
+ */
+void osmo_stat_item_group_set_name(struct osmo_stat_item_group *statg, const char *name)
+{
+ osmo_talloc_replace_string(statg, &statg->name, name);
+}
+
+/*! Increase the stat_item to the given value.
+ * This function adds a new value for the given stat_item at the end of
+ * the FIFO.
+ * \param[in] item The stat_item whose \a value we want to set
+ * \param[in] value The numeric value we want to store at end of FIFO
+ */
+void osmo_stat_item_inc(struct osmo_stat_item *item, int32_t value)
+{
+ osmo_stat_item_set(item, item->value.last + value);
+}
+
+/*! Descrease the stat_item to the given value.
+ * This function adds a new value for the given stat_item at the end of
+ * the FIFO.
+ * \param[in] item The stat_item whose \a value we want to set
+ * \param[in] value The numeric value we want to store at end of FIFO
+ */
+void osmo_stat_item_dec(struct osmo_stat_item *item, int32_t value)
+{
+ osmo_stat_item_set(item, item->value.last - value);
+}
+
+/*! Set the a given stat_item to the given value.
+ * This function adds a new value for the given stat_item at the end of
+ * the FIFO.
+ * \param[in] item The stat_item whose \a value we want to set
+ * \param[in] value The numeric value we want to store at end of FIFO
+ */
+void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value)
+{
+ item->value.last = value;
+ if (item->value.n == 0) {
+ /* No values recorded yet, clamp min and max to this first value. */
+ item->value.min = item->value.max = value;
+ /* Overwrite any cruft remaining in value.sum */
+ item->value.sum = value;
+ item->value.n = 1;
+ } else {
+ item->value.min = OSMO_MIN(item->value.min, value);
+ item->value.max = OSMO_MAX(item->value.max, value);
+ item->value.sum += value;
+ item->value.n++;
+ }
+}
+
+/*! Indicate that a reporting period has elapsed, and prepare the stat item for a new period of collecting min/max/avg.
+ * \param item Stat item to flush.
+ */
+void osmo_stat_item_flush(struct osmo_stat_item *item)
+{
+ item->reported = item->value;
+
+ /* Indicate a new reporting period: no values have been received, but the previous value.last remains unchanged
+ * for the case that an entire period elapses without a new value appearing. */
+ item->value.n = 0;
+ item->value.sum = 0;
+
+ /* Also for the case that an entire period elapses without any osmo_stat_item_set(), put the min and max to the
+ * last value. As soon as one osmo_stat_item_set() occurs, these are both set to the new value (when n is still
+ * zero from above). */
+ item->value.min = item->value.max = item->value.last;
+}
+
+/*! Initialize the stat item module. Call this once from your program.
+ * \param[in] tall_ctx Talloc context from which this module allocates */
+int osmo_stat_item_init(void *tall_ctx)
+{
+ tall_stat_item_ctx = tall_ctx;
+
+ return 0;
+}
+
+/*! Search for item group based on group name and index
+ * \param[in] name Name of stats_item_group we want to find
+ * \param[in] idx Index of the group we want to find
+ * \returns pointer to group, if found; NULL otherwise */
+struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx(
+ const char *name, const unsigned int idx)
+{
+ struct osmo_stat_item_group *statg;
+
+ llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
+ if (!statg->desc)
+ continue;
+
+ if (!strcmp(statg->desc->group_name_prefix, name) &&
+ statg->idx == idx)
+ return statg;
+ }
+ return NULL;
+}
+
+/*! Search for item group based on group name and index's name.
+ * \param[in] name Name of stats_item_group we want to find.
+ * \param[in] idx_name Index of the group we want to find, by the index's name (osmo_stat_item_group->name).
+ * \returns pointer to group, if found; NULL otherwise. */
+struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idxname(const char *group_name, const char *idx_name)
+{
+ struct osmo_stat_item_group *statg;
+
+ llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
+ if (!statg->desc || !statg->name)
+ continue;
+ if (strcmp(statg->desc->group_name_prefix, group_name))
+ continue;
+ if (strcmp(statg->name, idx_name))
+ continue;
+ return statg;
+ }
+ return NULL;
+}
+
+/*! Search for item based on group + item name
+ * \param[in] statg group in which to search for the item
+ * \param[in] name name of item to search within \a statg
+ * \returns pointer to item, if found; NULL otherwise */
+const struct osmo_stat_item *osmo_stat_item_get_by_name(
+ const struct osmo_stat_item_group *statg, const char *name)
+{
+ int i;
+ const struct osmo_stat_item_desc *item_desc;
+
+ if (!statg->desc)
+ return NULL;
+
+ for (i = 0; i < statg->desc->num_items; i++) {
+ item_desc = &statg->desc->item_desc[i];
+
+ if (!strcmp(item_desc->name, name)) {
+ return statg->items[i];
+ }
+ }
+ return NULL;
+}
+
+/*! Iterate over all items in group, call user-supplied function on each
+ * \param[in] statg stat_item group over whose items to iterate
+ * \param[in] handle_item Call-back function, aborts if rc < 0
+ * \param[in] data Private data handed through to \a handle_item
+ */
+int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg,
+ osmo_stat_item_handler_t handle_item, void *data)
+{
+ int rc = 0;
+ int i;
+
+ for (i = 0; i < statg->desc->num_items; i++) {
+ struct osmo_stat_item *item = statg->items[i];
+ rc = handle_item(statg, item, data);
+ if (rc < 0)
+ return rc;
+ }
+
+ return rc;
+}
+
+/*! Iterate over all stat_item groups in system, call user-supplied function on each
+ * \param[in] handle_group Call-back function, aborts if rc < 0
+ * \param[in] data Private data handed through to \a handle_group
+ */
+int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data)
+{
+ struct osmo_stat_item_group *statg;
+ int rc = 0;
+
+ llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
+ rc = handle_group(statg, data);
+ if (rc < 0)
+ return rc;
+ }
+
+ return rc;
+}
+
+/*! Get the last (freshest) value. */
+int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item)
+{
+ return item->value.last;
+}
+
+/*! Remove all values of a stat item
+ * \param[in] item stat item to reset
+ */
+void osmo_stat_item_reset(struct osmo_stat_item *item)
+{
+ item->value.sum = 0;
+ item->value.n = 0;
+ item->value.last = item->value.min = item->value.max = item->desc->default_value;
+}
+
+/*! Reset all osmo stat items in a group
+ * \param[in] statg stat item group to reset
+ */
+void osmo_stat_item_group_reset(struct osmo_stat_item_group *statg)
+{
+ int i;
+
+ for (i = 0; i < statg->desc->num_items; i++) {
+ struct osmo_stat_item *item = statg->items[i];
+ osmo_stat_item_reset(item);
+ }
+}
+
+/*! Return the description for an osmo_stat_item. */
+const struct osmo_stat_item_desc *osmo_stat_item_get_desc(struct osmo_stat_item *item)
+{
+ return item->desc;
+}
+
+/*! @} */
diff --git a/src/core/stat_item_internal.h b/src/core/stat_item_internal.h
new file mode 100644
index 00000000..9ede8c41
--- /dev/null
+++ b/src/core/stat_item_internal.h
@@ -0,0 +1,35 @@
+/*! \file stat_item_internal.h
+ * internal definitions for the osmo_stat_item API */
+#pragma once
+
+/*! \addtogroup osmo_stat_item
+ * @{
+ */
+
+struct osmo_stat_item_period {
+ /*! Number of osmo_stat_item_set() that occurred during the reporting period, zero if none. */
+ uint32_t n;
+ /*! Smallest value seen in a reporting period. */
+ int32_t min;
+ /*! Most recent value passed to osmo_stat_item_set(), or the item->desc->default_value if none. */
+ int32_t last;
+ /*! Largest value seen in a reporting period. */
+ int32_t max;
+ /*! Sum of all values passed to osmo_stat_item_set() in the reporting period. */
+ int64_t sum;
+};
+
+/*! data we keep for each actual item */
+struct osmo_stat_item {
+ /*! back-reference to the item description */
+ const struct osmo_stat_item_desc *desc;
+
+ /*! Current reporting period / current value. */
+ struct osmo_stat_item_period value;
+
+ /*! The results of the previous reporting period. According to these, the stats reporter decides whether to
+ * re-send values or omit an unchanged value from a report. */
+ struct osmo_stat_item_period reported;
+};
+
+/*! @} */
diff --git a/src/stats.c b/src/core/stats.c
index 411ecff9..16e6f620 100644
--- a/src/stats.c
+++ b/src/core/stats.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup stats
@@ -89,6 +85,7 @@
#include <osmocom/core/select.h>
#include <osmocom/core/counter.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/stats_tcp.h>
#ifdef HAVE_SYSTEMTAP
/* include the generated probes header and put markers in code */
@@ -101,10 +98,12 @@
#define TRACE_ENABLED(probe) (0)
#endif /* HAVE_SYSTEMTAP */
+#include <stat_item_internal.h>
+
#define STATS_DEFAULT_INTERVAL 5 /* secs */
#define STATS_DEFAULT_BUFLEN 256
-static LLIST_HEAD(osmo_stats_reporter_list);
+LLIST_HEAD(osmo_stats_reporter_list);
static void *osmo_stats_ctx = NULL;
static int is_initialised = 0;
@@ -176,7 +175,7 @@ static int osmo_stats_timer_cb(struct osmo_fd *ofd, unsigned int what)
return 0;
}
-static int start_timer()
+static int start_timer(void)
{
int rc;
int interval = osmo_stats_config->interval;
@@ -215,13 +214,15 @@ struct osmo_stats_reporter *osmo_stats_reporter_alloc(enum osmo_stats_reporter_t
{
struct osmo_stats_reporter *srep;
srep = talloc_zero(osmo_stats_ctx, struct osmo_stats_reporter);
- OSMO_ASSERT(srep);
+ if (!srep)
+ return NULL;
+
srep->type = type;
if (name)
srep->name = talloc_strdup(srep, name);
srep->fd = -1;
- llist_add(&srep->list, &osmo_stats_reporter_list);
+ llist_add_tail(&srep->list, &osmo_stats_reporter_list);
return srep;
}
@@ -235,30 +236,17 @@ void osmo_stats_reporter_free(struct osmo_stats_reporter *srep)
talloc_free(srep);
}
-static int osmo_stats_discard_item(struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)
-{
- return osmo_stat_item_discard(item, &item->stats_next_id);
-}
-
-static int osmo_stats_discard_group(struct osmo_stat_item_group *statg, void *sctx_)
-{
- return osmo_stat_item_for_each_item(statg, &osmo_stats_discard_item, NULL);
-}
-
-static int osmo_stats_discard_all()
-{
- return osmo_stat_item_for_each_group(&osmo_stats_discard_group, NULL);
-}
-
-/*! Initilize the stats reporting module; call this once in your program
+/*! Initialize the stats reporting module; call this once in your program.
* \param[in] ctx Talloc context from which stats related memory is allocated */
void osmo_stats_init(void *ctx)
{
osmo_stats_ctx = ctx;
- osmo_stats_discard_all();
-
is_initialised = 1;
start_timer();
+
+ /* Make sure that the tcp-stats interval timer also runs at its
+ * preconfigured rate. The vty might change this setting later. */
+ osmo_stats_tcp_set_interval(osmo_tcp_stats_config->interval);
}
/*! Find a stats_reporter of given \a type and \a name.
@@ -426,7 +414,7 @@ int osmo_stats_reporter_set_flush_period(struct osmo_stats_reporter *srep, unsig
/*! Set the name prefix of a given stats_reporter.
* \param[in] srep stats_reporter whose name prefix is to be set
- * \param[in] prefix NAme perfix to pre-pend for any reported value
+ * \param[in] prefix Name prefix to pre-pend for any reported value
* \returns 0 on success; negative on error */
int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix)
{
@@ -500,6 +488,8 @@ int osmo_stats_reporter_udp_open(struct osmo_stats_reporter *srep)
}
srep->buffer = msgb_alloc(buffer_size, "stats buffer");
+ if (!srep->buffer)
+ goto failed;
return 0;
@@ -583,6 +573,8 @@ struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name)
{
struct osmo_stats_reporter *srep;
srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_LOG, name);
+ if (!srep)
+ return NULL;
srep->have_net_config = 0;
@@ -708,36 +700,29 @@ static int osmo_stat_item_handler(
struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)
{
struct osmo_stats_reporter *srep;
- int32_t value;
- bool have_value;
-
- have_value = osmo_stat_item_get_next(item, &item->stats_next_id, &value) > 0;
- if (!have_value) {
- /* Send the last value in case a flush is requested */
- value = osmo_stat_item_get_last(item);
- } else {
- int32_t next_val;
- /* If we have multiple values only send the max */
- while (osmo_stat_item_get_next(item, &item->stats_next_id, &next_val) > 0)
- value = OSMO_MAX(value, next_val);
- }
+ int32_t prev_reported_value = item->reported.max;
+ int32_t new_value = item->value.max;
llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
if (!srep->running)
continue;
- if (!have_value && !srep->force_single_flush)
+ /* If the previously reported value is the same as the current value, skip resending the value.
+ * However, if the stats reporter is set to resend all values, do resend the current value regardless of
+ * repetitions.
+ */
+ if (new_value == prev_reported_value && !srep->force_single_flush)
continue;
if (!osmo_stats_reporter_check_config(srep,
statg->idx, statg->desc->class_id))
continue;
- osmo_stats_reporter_send_item(srep, statg,
- item->desc, value);
-
+ osmo_stats_reporter_send_item(srep, statg, item->desc, new_value);
}
+ osmo_stat_item_flush(item);
+
return 0;
}
@@ -779,7 +764,7 @@ static int handle_counter(struct osmo_counter *counter, void *sctx_)
/*** main reporting function ***/
-static void flush_all_reporters()
+static void flush_all_reporters(void)
{
struct osmo_stats_reporter *srep;
@@ -802,7 +787,7 @@ static void flush_all_reporters()
}
}
-int osmo_stats_report()
+int osmo_stats_report(void)
{
/* per group actions */
TRACE(LIBOSMOCORE_STATS_START());
@@ -811,7 +796,6 @@ int osmo_stats_report()
osmo_stat_item_for_each_group(osmo_stat_item_group_handler, NULL);
/* global actions */
- osmo_stats_discard_all();
flush_all_reporters();
TRACE(LIBOSMOCORE_STATS_DONE());
diff --git a/src/stats_statsd.c b/src/core/stats_statsd.c
index 99764e64..b27baff8 100644
--- a/src/stats_statsd.c
+++ b/src/core/stats_statsd.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup stats
@@ -58,6 +54,8 @@ struct osmo_stats_reporter *osmo_stats_reporter_create_statsd(const char *name)
{
struct osmo_stats_reporter *srep;
srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_STATSD, name);
+ if (!srep)
+ return NULL;
srep->have_net_config = 1;
@@ -89,7 +87,7 @@ static void osmo_stats_reporter_sanitize_name(char *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 *name1, const char *index1, const char *name2, int64_t value,
const char *unit)
{
char *buf;
@@ -101,13 +99,13 @@ static int osmo_stats_reporter_statsd_send(struct osmo_stats_reporter *srep,
if (prefix) {
if (name1)
- fmt = "%1$s.%2$s.%6$u.%3$s:%4$" PRId64 "|%5$s";
+ fmt = "%1$s.%2$s.%6$s.%3$s:%4$" PRId64 "|%5$s";
else
fmt = "%1$s.%2$0.0s%3$s:%4$" PRId64 "|%5$s";
} else {
prefix = "";
if (name1)
- fmt = "%1$s%2$s.%6$u.%3$s:%4$" PRId64 "|%5$s";
+ fmt = "%1$s%2$s.%6$s.%3$s:%4$" PRId64 "|%5$s";
else
fmt = "%1$s%2$0.0s%3$s:%4$" PRId64 "|%5$s";
}
@@ -162,32 +160,42 @@ static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *s
const struct rate_ctr_desc *desc,
int64_t value, int64_t delta)
{
- if (ctrg)
- return osmo_stats_reporter_statsd_send(srep,
- ctrg->desc->group_name_prefix,
- ctrg->idx,
- desc->name, delta, "c");
- else
- return osmo_stats_reporter_statsd_send(srep,
- NULL, 0,
- desc->name, delta, "c");
+ char buf_idx[64];
+ const char *idx_name = buf_idx;
+ const char *prefix;
+
+ if (ctrg) {
+ prefix = ctrg->desc->group_name_prefix;
+ if (ctrg->name)
+ idx_name = ctrg->name;
+ else
+ snprintf(buf_idx, sizeof(buf_idx), "%u", ctrg->idx);
+ } else {
+ prefix = NULL;
+ buf_idx[0] = '0';
+ buf_idx[1] = '\n';
+ }
+ return osmo_stats_reporter_statsd_send(srep, prefix, idx_name, desc->name, delta, "c");
}
static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep,
const struct osmo_stat_item_group *statg,
const struct osmo_stat_item_desc *desc, int64_t value)
{
- if (value < 0) {
- return osmo_stats_reporter_statsd_send(srep,
- statg->desc->group_name_prefix,
- statg->idx,
- desc->name, 0, "g");
- } else {
- return osmo_stats_reporter_statsd_send(srep,
- statg->desc->group_name_prefix,
- statg->idx,
- desc->name, value, "g");
+ char buf_idx[64];
+ char *idx_name;
+ if (statg->name)
+ idx_name = statg->name;
+ else {
+ snprintf(buf_idx, sizeof(buf_idx), "%u", statg->idx);
+ idx_name = buf_idx;
}
+
+ if (value < 0)
+ value = 0;
+
+ return osmo_stats_reporter_statsd_send(srep, statg->desc->group_name_prefix,
+ idx_name, desc->name, value, "g");
}
#endif /* !EMBEDDED */
diff --git a/src/core/stats_tcp.c b/src/core/stats_tcp.c
new file mode 100644
index 00000000..c6459fe8
--- /dev/null
+++ b/src/core/stats_tcp.c
@@ -0,0 +1,327 @@
+/*
+ * (C) 2021 by sysmocom - s.f.m.c. GmbH
+ * Author: Philipp Maier <pmaier@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*! \addtogroup stats
+ * @{
+ * \file stats_tcp.c */
+
+#include "config.h"
+#if !defined(EMBEDDED)
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <linux/tcp.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/stat_item.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/stats_tcp.h>
+
+static struct osmo_tcp_stats_config s_tcp_stats_config = {
+ .interval = TCP_STATS_DEFAULT_INTERVAL,
+};
+
+struct osmo_tcp_stats_config *osmo_tcp_stats_config = &s_tcp_stats_config;
+
+static struct osmo_timer_list stats_tcp_poll_timer;
+
+static LLIST_HEAD(stats_tcp);
+static struct stats_tcp_entry *stats_tcp_entry_cur;
+pthread_mutex_t stats_tcp_lock;
+
+struct stats_tcp_entry {
+ struct llist_head entry;
+ const struct osmo_fd *fd;
+ struct osmo_stat_item_group *stats_tcp;
+ const char *name;
+};
+
+enum {
+ STATS_TCP_UNACKED,
+ STATS_TCP_LOST,
+ STATS_TCP_RETRANS,
+ STATS_TCP_RTT,
+ STATS_TCP_RCV_RTT,
+ STATS_TCP_NOTSENT_BYTES,
+ STATS_TCP_RWND_LIMITED,
+ STATS_TCP_SNDBUF_LIMITED,
+ STATS_TCP_REORD_SEEN,
+};
+
+static struct osmo_stat_item_desc stats_tcp_item_desc[] = {
+ [STATS_TCP_UNACKED] = { "tcp:unacked", "unacknowledged packets", "", 60, 0 },
+ [STATS_TCP_LOST] = { "tcp:lost", "lost packets", "", 60, 0 },
+ [STATS_TCP_RETRANS] = { "tcp:retrans", "retransmitted packets", "", 60, 0 },
+ [STATS_TCP_RTT] = { "tcp:rtt", "roundtrip-time", "", 60, 0 },
+ [STATS_TCP_RCV_RTT] = { "tcp:rcv_rtt", "roundtrip-time (receive)", "", 60, 0 },
+ [STATS_TCP_NOTSENT_BYTES] = { "tcp:notsent_bytes", "bytes not yet sent", "", 60, 0 },
+ [STATS_TCP_RWND_LIMITED] = { "tcp:rwnd_limited", "time (usec) limited by receive window", "", 60, 0 },
+ [STATS_TCP_SNDBUF_LIMITED] = { "tcp:sndbuf_limited", "Time (usec) limited by send buffer", "", 60, 0 },
+ [STATS_TCP_REORD_SEEN] = { "tcp:reord_seen", "reordering events seen", "", 60, 0 },
+};
+
+static struct osmo_stat_item_group_desc stats_tcp_desc = {
+ .group_name_prefix = "tcp",
+ .group_description = "stats tcp",
+ .class_id = OSMO_STATS_CLASS_GLOBAL,
+ .num_items = ARRAY_SIZE(stats_tcp_item_desc),
+ .item_desc = stats_tcp_item_desc,
+};
+
+static void fill_stats(struct stats_tcp_entry *stats_tcp_entry)
+{
+ int rc;
+ struct tcp_info tcp_info;
+ socklen_t tcp_info_len = sizeof(tcp_info);
+ char stat_name[256];
+
+ /* Do not fill in anything before the socket is connected to a remote end */
+ if (osmo_sock_get_ip_and_port(stats_tcp_entry->fd->fd, NULL, 0, NULL, 0, false) != 0)
+ return;
+
+ /* Gather TCP statistics and update the stats items */
+ rc = getsockopt(stats_tcp_entry->fd->fd, IPPROTO_TCP, TCP_INFO, &tcp_info, &tcp_info_len);
+ if (rc < 0)
+ return;
+
+ /* Create stats items if they do not exist yet */
+ if (!stats_tcp_entry->stats_tcp) {
+ stats_tcp_entry->stats_tcp =
+ osmo_stat_item_group_alloc(stats_tcp_entry, &stats_tcp_desc, stats_tcp_entry->fd->fd);
+ OSMO_ASSERT(stats_tcp_entry->stats_tcp);
+ }
+
+ /* Update statistics */
+ if (stats_tcp_entry->name)
+ snprintf(stat_name, sizeof(stat_name), "%s", stats_tcp_entry->name);
+ else
+ snprintf(stat_name, sizeof(stat_name), "%s", osmo_sock_get_name2(stats_tcp_entry->fd->fd));
+ osmo_stat_item_group_set_name(stats_tcp_entry->stats_tcp, stat_name);
+
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_UNACKED),
+ tcp_info.tcpi_unacked);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_LOST),
+ tcp_info.tcpi_lost);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RETRANS),
+ tcp_info.tcpi_retrans);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RTT), tcp_info.tcpi_rtt);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RCV_RTT),
+ tcp_info.tcpi_rcv_rtt);
+#if HAVE_TCP_INFO_TCPI_NOTSENT_BYTES == 1
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_NOTSENT_BYTES),
+ tcp_info.tcpi_notsent_bytes);
+#else
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_NOTSENT_BYTES), -1);
+#endif
+
+#if HAVE_TCP_INFO_TCPI_RWND_LIMITED == 1
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RWND_LIMITED),
+ tcp_info.tcpi_rwnd_limited);
+#else
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RWND_LIMITED), -1);
+#endif
+
+#if STATS_TCP_SNDBUF_LIMITED == 1
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_REORD_SEEN),
+ tcp_info.tcpi_sndbuf_limited);
+#else
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_REORD_SEEN), -1);
+#endif
+
+#if HAVE_TCP_INFO_TCPI_REORD_SEEN == 1
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_REORD_SEEN),
+ tcp_info.tcpi_reord_seen);
+#else
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_REORD_SEEN), -1);
+#endif
+
+}
+
+static bool is_tcp(const struct osmo_fd *fd)
+{
+ int rc;
+ struct stat fd_stat;
+ int so_protocol = 0;
+ socklen_t so_protocol_len = sizeof(so_protocol);
+
+ /* Is this a socket? */
+ rc = fstat(fd->fd, &fd_stat);
+ if (rc < 0)
+ return false;
+ if (!S_ISSOCK(fd_stat.st_mode))
+ return false;
+
+ /* Is it a TCP socket? */
+ rc = getsockopt(fd->fd, SOL_SOCKET, SO_PROTOCOL, &so_protocol, &so_protocol_len);
+ if (rc < 0)
+ return false;
+ if (so_protocol == IPPROTO_TCP)
+ return true;
+
+ return false;
+}
+
+/*! Register an osmo_fd for TCP stats monitoring.
+ * \param[in] fd osmocom file descriptor to be registered.
+ * \param[in] human readbla name that is used as prefix for the related stats item.
+ * \returns 0 on success; negative in case of error. */
+int osmo_stats_tcp_osmo_fd_register(const struct osmo_fd *fd, const char *name)
+{
+ struct stats_tcp_entry *stats_tcp_entry;
+
+ /* Only TCP sockets can be registered for monitoring, anything else will fall through. */
+ if (!is_tcp(fd))
+ return -EINVAL;
+
+ /* When the osmo_fd is registered and unregistered properly there shouldn't be any leftovers from already closed
+ * osmo_fds in the stats_tcp list. But lets proactively make sure that any leftovers are cleaned up. */
+ osmo_stats_tcp_osmo_fd_unregister(fd);
+
+ /* Make a new list object, attach the osmo_fd... */
+ stats_tcp_entry = talloc_zero(OTC_GLOBAL, struct stats_tcp_entry);
+ OSMO_ASSERT(stats_tcp_entry);
+ stats_tcp_entry->fd = fd;
+ stats_tcp_entry->name = talloc_strdup(stats_tcp_entry, name);
+
+ pthread_mutex_lock(&stats_tcp_lock);
+ llist_add_tail(&stats_tcp_entry->entry, &stats_tcp);
+ pthread_mutex_unlock(&stats_tcp_lock);
+
+ return 0;
+}
+
+static void next_stats_tcp_entry(void)
+{
+ struct stats_tcp_entry *last;
+
+ if (llist_empty(&stats_tcp)) {
+ stats_tcp_entry_cur = NULL;
+ return;
+ }
+
+ last = (struct stats_tcp_entry *)llist_last_entry(&stats_tcp, struct stats_tcp_entry, entry);
+
+ if (!stats_tcp_entry_cur || stats_tcp_entry_cur == last)
+ stats_tcp_entry_cur =
+ (struct stats_tcp_entry *)llist_first_entry(&stats_tcp, struct stats_tcp_entry, entry);
+ else
+ stats_tcp_entry_cur =
+ (struct stats_tcp_entry *)llist_entry(stats_tcp_entry_cur->entry.next, struct stats_tcp_entry,
+ entry);
+}
+
+/*! Register an osmo_fd for TCP stats monitoring.
+ * \param[in] fd osmocom file descriptor to be unregistered.
+ * \returns 0 on success; negative in case of error. */
+int osmo_stats_tcp_osmo_fd_unregister(const struct osmo_fd *fd)
+{
+ struct stats_tcp_entry *stats_tcp_entry;
+ int rc = -EINVAL;
+
+ pthread_mutex_lock(&stats_tcp_lock);
+ llist_for_each_entry(stats_tcp_entry, &stats_tcp, entry) {
+ if (fd->fd == stats_tcp_entry->fd->fd) {
+ /* In case we want to remove exactly that item which is also
+ * selected as the current item, we must designate either a
+ * different item or invalidate the current item.
+ */
+ if (stats_tcp_entry == stats_tcp_entry_cur) {
+ if (llist_count(&stats_tcp) > 2)
+ next_stats_tcp_entry();
+ else
+ stats_tcp_entry_cur = NULL;
+ }
+
+ /* Date item from list */
+ llist_del(&stats_tcp_entry->entry);
+ osmo_stat_item_group_free(stats_tcp_entry->stats_tcp);
+ talloc_free(stats_tcp_entry);
+ rc = 0;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&stats_tcp_lock);
+
+ return rc;
+}
+
+static void stats_tcp_poll_timer_cb(void *data)
+{
+ int i;
+ int batch_size;
+ int llist_size;
+
+ pthread_mutex_lock(&stats_tcp_lock);
+
+ /* Make sure we do not run over the same sockets multiple times if the
+ * configured llist_size is larger then the actual list */
+ batch_size = osmo_tcp_stats_config->batch_size;
+ llist_size = llist_count(&stats_tcp);
+ if (llist_size < batch_size)
+ batch_size = llist_size;
+
+ /* Process a batch of sockets */
+ for (i = 0; i < batch_size; i++) {
+ next_stats_tcp_entry();
+ if (stats_tcp_entry_cur)
+ fill_stats(stats_tcp_entry_cur);
+ }
+
+ pthread_mutex_unlock(&stats_tcp_lock);
+
+ if (osmo_tcp_stats_config->interval > 0)
+ osmo_timer_schedule(&stats_tcp_poll_timer, osmo_tcp_stats_config->interval, 0);
+}
+
+/*! Set the polling interval (common for all sockets)
+ * \param[in] interval Poll interval in seconds
+ * \returns 0 on success; negative on error */
+int osmo_stats_tcp_set_interval(int interval)
+{
+ osmo_tcp_stats_config->interval = interval;
+ if (osmo_tcp_stats_config->interval > 0)
+ osmo_timer_schedule(&stats_tcp_poll_timer, osmo_tcp_stats_config->interval, 0);
+ return 0;
+}
+
+static __attribute__((constructor))
+void on_dso_load_stats_tcp(void)
+{
+ stats_tcp_entry_cur = NULL;
+ pthread_mutex_init(&stats_tcp_lock, NULL);
+
+ osmo_tcp_stats_config->interval = TCP_STATS_DEFAULT_INTERVAL;
+ osmo_tcp_stats_config->batch_size = TCP_STATS_DEFAULT_BATCH_SIZE;
+
+ osmo_timer_setup(&stats_tcp_poll_timer, stats_tcp_poll_timer_cb, NULL);
+}
+
+#endif /* !EMBEDDED */
+
+/* @} */
diff --git a/src/strrb.c b/src/core/strrb.c
index 461fdecc..c5a5ed6e 100644
--- a/src/strrb.c
+++ b/src/core/strrb.c
@@ -31,10 +31,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup utils
@@ -56,12 +52,12 @@
* This function creates and initializes a ringbuffer.
* Note that the ringbuffer stores at most rb_size - 1 messages.
*/
-struct osmo_strrb *osmo_strrb_create(TALLOC_CTX * ctx, size_t rb_size)
+struct osmo_strrb *osmo_strrb_create(void *talloc_ctx, size_t rb_size)
{
struct osmo_strrb *rb = NULL;
unsigned int i;
- rb = talloc_zero(ctx, struct osmo_strrb);
+ rb = talloc_zero(talloc_ctx, struct osmo_strrb);
if (!rb)
goto alloc_error;
diff --git a/src/tdef.c b/src/core/tdef.c
index 9890f95a..f0c0f2e8 100644
--- a/src/tdef.c
+++ b/src/core/tdef.c
@@ -52,18 +52,18 @@
* By keeping separate osmo_tdef arrays, several groups of timers can be kept
* separately. The VTY tests in tests/tdef/ showcase different schemes:
*
- * - \ref tests/vty/tdef_vty_test_config_root.c:
+ * - \ref tests/tdef/tdef_vty_config_root_test.c:
* Keep several timer definitions in separately named groups: showcase the
* osmo_tdef_vty_groups*() API. Each timer group exists exactly once.
*
- * - \ref tests/vty/tdef_vty_test_config_subnode.c:
+ * - \ref tests/tdef/tdef_vty_config_subnode_test.c:
* Keep a single list of timers without separate grouping.
* Put this list on a specific subnode below the CONFIG_NODE.
* There could be several separate subnodes with timers like this, i.e.
* continuing from this example, sets of timers could be separated by placing
* timers in specific config subnodes instead of using the global group name.
*
- * - \ref tests/vty/tdef_vty_test_dynamic.c:
+ * - \ref tests/tdef/tdef_vty_dynamic_test.c:
* Dynamically allocate timer definitions per each new created object.
* Thus there can be an arbitrary number of independent timer definitions, one
* per allocated object.
@@ -200,8 +200,10 @@ void osmo_tdefs_reset(struct osmo_tdef *tdefs)
* \param[in] tdefs Array of timer definitions, last entry must be fully zero initialized.
* \param[in] T Timer number to get the value for.
* \param[in] as_unit Return timeout value in this unit.
- * \param[in] val_if_not_present Fallback value to return if no timeout is defined.
+ * \param[in] val_if_not_present Fallback value to return if no timeout is defined; if this is a negative number, a
+ * missing T timer definition aborts the program via OSMO_ASSERT().
* \return Timeout value in the unit given by as_unit, rounded up if necessary, or val_if_not_present.
+ * If val_if_not_present is negative and no T timer is defined, trigger OSMO_ASSERT() and do not return.
*/
unsigned long osmo_tdef_get(const struct osmo_tdef *tdefs, int T, enum osmo_tdef_unit as_unit, long val_if_not_present)
{
@@ -335,26 +337,37 @@ int _osmo_tdef_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t state,
const char *file, int line)
{
const struct osmo_tdef_state_timeout *t = osmo_tdef_get_state_timeout(state, timeouts_array);
- unsigned long val = 0;
+ unsigned long val_ms = 0;
/* No timeout defined for this state? */
if (!t)
return _osmo_fsm_inst_state_chg(fi, state, 0, 0, file, line);
- if (t->T)
- val = osmo_tdef_get(tdefs, t->T, OSMO_TDEF_S, default_timeout);
+ if (t->T) {
+ const struct osmo_tdef *tdef = osmo_tdef_get_entry((struct osmo_tdef *)tdefs, t->T);
+ if (tdef == NULL) {
+ /* emulate the old behavior: treat default_timeout as OSMO_TDEF_S */
+ OSMO_ASSERT(default_timeout >= 0);
+ val_ms = default_timeout * 1000;
+ } else {
+ val_ms = osmo_tdef_round(tdef->val, tdef->unit, OSMO_TDEF_MS);
+ /* emulate the old behavior: treat OSMO_TDEF_CUSTOM as OSMO_TDEF_S */
+ if (tdef->unit == OSMO_TDEF_CUSTOM)
+ val_ms *= 1000;
+ }
+ }
if (t->keep_timer) {
if (t->T)
- return _osmo_fsm_inst_state_chg_keep_or_start_timer(fi, state, val, t->T, file, line);
+ return _osmo_fsm_inst_state_chg_keep_or_start_timer_ms(fi, state, val_ms, t->T, file, line);
else
return _osmo_fsm_inst_state_chg_keep_timer(fi, state, file, line);
}
- /* val is always initialized here, because if t->keep_timer is false, t->T must be != 0.
+ /* val_ms is always initialized here, because if t->keep_timer is false, t->T must be != 0.
* Otherwise osmo_tdef_get_state_timeout() would have returned NULL. */
OSMO_ASSERT(t->T);
- return _osmo_fsm_inst_state_chg(fi, state, val, t->T, file, line);
+ return _osmo_fsm_inst_state_chg_ms(fi, state, val_ms, t->T, file, line);
}
const struct value_string osmo_tdef_unit_names[] = {
diff --git a/src/thread.c b/src/core/thread.c
index 956fee76..d9a98422 100644
--- a/src/thread.c
+++ b/src/core/thread.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup thread
diff --git a/src/core/time_cc.c b/src/core/time_cc.c
new file mode 100644
index 00000000..0e6879e5
--- /dev/null
+++ b/src/core/time_cc.c
@@ -0,0 +1,228 @@
+/*! \file foo.c
+ * Report the cumulative counter of time for which a flag is true as rate counter.
+ */
+/* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * 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/>.
+ *
+ */
+
+/*! \addtogroup time_cc
+ *
+ * Report the cumulative counter of time for which a flag is true as rate counter.
+ *
+ * Useful for reporting cumulative time counters as defined in 3GPP TS 52.402, for example allAvailableSDCCHAllocated,
+ * allAvailableTCHAllocated, availablePDCHAllocatedTime.
+ *
+ * For a usage example, see the description of struct osmo_time_cc.
+ *
+ * @{
+ * \file time_cc.c
+ */
+#include "config.h"
+#ifdef HAVE_CLOCK_GETTIME
+
+#include <limits.h>
+#include <time.h>
+
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/time_cc.h>
+
+#define GRAN_USEC(TIME_CC) ((TIME_CC)->cfg.gran_usec ? : 1000000)
+#define ROUND_THRESHOLD_USEC(TIME_CC) ((TIME_CC)->cfg.round_threshold_usec ? \
+ OSMO_MIN((TIME_CC)->cfg.round_threshold_usec, GRAN_USEC(TIME_CC)) \
+ : (GRAN_USEC(TIME_CC) / 2))
+
+static uint64_t time_now_usec(void)
+{
+ struct timespec tp;
+ if (osmo_clock_gettime(CLOCK_MONOTONIC, &tp))
+ return 0;
+ return (uint64_t)tp.tv_sec * 1000000 + tp.tv_nsec / 1000;
+}
+
+static void osmo_time_cc_forget_sum(struct osmo_time_cc *tc, uint64_t now);
+
+static void osmo_time_cc_update_from_tdef(struct osmo_time_cc *tc, uint64_t now)
+{
+ bool do_forget_sum = false;
+ if (!tc->cfg.T_defs)
+ return;
+ if (tc->cfg.T_gran) {
+ uint64_t was = GRAN_USEC(tc);
+ tc->cfg.gran_usec = osmo_tdef_get(tc->cfg.T_defs, tc->cfg.T_gran, OSMO_TDEF_US, -1);
+ if (was != GRAN_USEC(tc))
+ do_forget_sum = true;
+ }
+ if (tc->cfg.T_round_threshold)
+ tc->cfg.round_threshold_usec = osmo_tdef_get(tc->cfg.T_defs, tc->cfg.T_round_threshold,
+ OSMO_TDEF_US, -1);
+ if (tc->cfg.T_forget_sum) {
+ uint64_t was = tc->cfg.forget_sum_usec;
+ tc->cfg.forget_sum_usec = osmo_tdef_get(tc->cfg.T_defs, tc->cfg.T_forget_sum, OSMO_TDEF_US, -1);
+ if (tc->cfg.forget_sum_usec && was != tc->cfg.forget_sum_usec)
+ do_forget_sum = true;
+ }
+
+ if (do_forget_sum && tc->sum)
+ osmo_time_cc_forget_sum(tc, now);
+}
+
+static void osmo_time_cc_schedule_timer(struct osmo_time_cc *tc, uint64_t now);
+
+/*! Clear out osmo_timer and internal counting state of struct osmo_time_cc. The .cfg remains unaffected. After calling,
+ * the osmo_time_cc instance can be used again to accumulate state as if it had just been initialized. */
+void osmo_time_cc_cleanup(struct osmo_time_cc *tc)
+{
+ osmo_timer_del(&tc->timer);
+ *tc = (struct osmo_time_cc){
+ .cfg = tc->cfg,
+ };
+}
+
+static void osmo_time_cc_start(struct osmo_time_cc *tc, uint64_t now)
+{
+ osmo_time_cc_cleanup(tc);
+ tc->start_time = now;
+ tc->last_counted_time = now;
+ osmo_time_cc_update_from_tdef(tc, now);
+ osmo_time_cc_schedule_timer(tc, now);
+}
+
+static void osmo_time_cc_count_time(struct osmo_time_cc *tc, uint64_t now)
+{
+ uint64_t time_delta = now - tc->last_counted_time;
+ tc->last_counted_time = now;
+ if (!tc->flag_state)
+ return;
+ /* Flag is currently true, cumulate the elapsed time */
+ tc->total_sum += time_delta;
+ tc->sum += time_delta;
+}
+
+static void osmo_time_cc_report(struct osmo_time_cc *tc, uint64_t now)
+{
+ uint64_t delta;
+ uint64_t n;
+ /* We report a sum "rounded up", ahead of time. If the granularity period has not yet elapsed after the last
+ * reporting, do not report again yet. */
+ if (tc->reported_sum > tc->sum)
+ return;
+ delta = tc->sum - tc->reported_sum;
+ /* elapsed full periods */
+ n = delta / GRAN_USEC(tc);
+ /* If the delta has passed round_threshold (normally half of gran_usec), increment. */
+ delta -= n * GRAN_USEC(tc);
+ if (delta >= ROUND_THRESHOLD_USEC(tc))
+ n++;
+ if (!n)
+ return;
+
+ /* integer sanity, since rate_ctr_add() takes an int argument. */
+ if (n > INT_MAX)
+ n = INT_MAX;
+ if (tc->cfg.rate_ctr)
+ rate_ctr_add(tc->cfg.rate_ctr, n);
+ /* Store the increments of gran_usec that were counted. */
+ tc->reported_sum += n * GRAN_USEC(tc);
+}
+
+static void osmo_time_cc_forget_sum(struct osmo_time_cc *tc, uint64_t now)
+{
+ tc->reported_sum = 0;
+ tc->sum = 0;
+
+ if (tc->last_counted_time < now)
+ tc->last_counted_time = now;
+}
+
+/*! Initialize struct osmo_time_cc. Call this once before use, and before setting up the .cfg items. */
+void osmo_time_cc_init(struct osmo_time_cc *tc)
+{
+ *tc = (struct osmo_time_cc){0};
+}
+
+/*! Report state to be recorded by osmo_time_cc instance. Setting an unchanged state repeatedly has no effect. */
+void osmo_time_cc_set_flag(struct osmo_time_cc *tc, bool flag)
+{
+ uint64_t now = time_now_usec();
+ if (!tc->start_time)
+ osmo_time_cc_start(tc, now);
+ /* No flag change == no effect */
+ if (flag == tc->flag_state)
+ return;
+ /* Sum up elapsed time, report increments for that. */
+ osmo_time_cc_count_time(tc, now);
+ osmo_time_cc_report(tc, now);
+ tc->flag_state = flag;
+ osmo_time_cc_schedule_timer(tc, now);
+}
+
+static void osmo_time_cc_timer_cb(void *data)
+{
+ struct osmo_time_cc *tc = data;
+ uint64_t now = time_now_usec();
+
+ osmo_time_cc_update_from_tdef(tc, now);
+
+ if (tc->flag_state) {
+ osmo_time_cc_count_time(tc, now);
+ osmo_time_cc_report(tc, now);
+ } else if (tc->cfg.forget_sum_usec && tc->sum
+ && (now >= tc->last_counted_time + tc->cfg.forget_sum_usec)) {
+ osmo_time_cc_forget_sum(tc, now);
+ }
+ osmo_time_cc_schedule_timer(tc, now);
+}
+
+/*! Figure out the next time we should do anything, if the flag state remains unchanged. */
+static void osmo_time_cc_schedule_timer(struct osmo_time_cc *tc, uint64_t now)
+{
+ uint64_t next_event = UINT64_MAX;
+
+ osmo_time_cc_update_from_tdef(tc, now);
+
+ /* If it is required, when will the next forget_sum happen? */
+ if (tc->cfg.forget_sum_usec && !tc->flag_state && tc->sum > 0) {
+ uint64_t next_forget_time = tc->last_counted_time + tc->cfg.forget_sum_usec;
+ next_event = OSMO_MIN(next_event, next_forget_time);
+ }
+ /* Next rate_ctr increment? */
+ if (tc->flag_state) {
+ uint64_t next_inc = now + (tc->reported_sum - tc->sum) + ROUND_THRESHOLD_USEC(tc);
+ next_event = OSMO_MIN(next_event, next_inc);
+ }
+
+ /* No event coming up? */
+ if (next_event == UINT64_MAX)
+ return;
+
+ if (next_event <= now)
+ next_event = 0;
+ else
+ next_event -= now;
+
+ osmo_timer_setup(&tc->timer, osmo_time_cc_timer_cb, tc);
+ osmo_timer_del(&tc->timer);
+ osmo_timer_schedule(&tc->timer, next_event / 1000000, next_event % 1000000);
+}
+
+#endif /* HAVE_CLOCK_GETTIME */
+
+/*! @} */
diff --git a/src/timer.c b/src/core/timer.c
index ed5a6103..067bd875 100644
--- a/src/timer.c
+++ b/src/core/timer.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -195,7 +191,16 @@ int osmo_timers_nearest_ms(void)
return -1;
nearest_ms = nearest_p->tv_sec * 1000;
+#ifndef EMBEDDED
+ /* By adding 999 milliseconds, we ensure rounding up to the nearest
+ * whole millisecond. This approach prevents the return of 0 when the
+ * timer is still active, and it guarantees that the calling process
+ * does not wait for a duration shorter than the time remaining on the
+ * timer. */
+ nearest_ms += (nearest_p->tv_usec + 999) / 1000;
+#else
nearest_ms += nearest_p->tv_usec / 1000;
+#endif
return nearest_ms;
}
diff --git a/src/timer_clockgettime.c b/src/core/timer_clockgettime.c
index 7b17fd11..6112b8a5 100644
--- a/src/timer_clockgettime.c
+++ b/src/core/timer_clockgettime.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup timer
diff --git a/src/timer_gettimeofday.c b/src/core/timer_gettimeofday.c
index 34949465..e0212b5a 100644
--- a/src/timer_gettimeofday.c
+++ b/src/core/timer_gettimeofday.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup timer
diff --git a/src/core/tun.c b/src/core/tun.c
new file mode 100644
index 00000000..86128190
--- /dev/null
+++ b/src/core/tun.c
@@ -0,0 +1,577 @@
+
+/* TUN interface functions.
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "config.h"
+
+/*! \addtogroup tun
+ * @{
+ * tun network device (interface) convenience functions
+ *
+ * \file tundev.c
+ *
+ * Example lifecycle use of the API:
+ *
+ * struct osmo_sockaddr_str osa_str = {};
+ * struct osmo_sockaddr osa = {};
+ *
+ * // Allocate object:
+ * struct osmo_tundev *tundev = osmo_tundev_alloc(parent_talloc_ctx, name);
+ * OSMO_ASSERT(tundev);
+ *
+ * // Configure object (before opening):
+ * osmo_tundev_set_data_ind_cb(tun, tun_data_ind_cb);
+ * rc = osmo_tundev_set_dev_name(tun, "mytunnel0");
+ * rc = osmo_tundev_set_netns_name(tun, "some_netns_name_or_null");
+ *
+ * // Open the tundev object:
+ * rc = osmo_tundev_open(tundev);
+ * // The tunnel device is now created and an associatd netdev object
+ * // is available to operate the device:
+ * struct osmo_netdev *netdev = osmo_tundev_get_netdev(tundev);
+ * OSMO_ASSERT(netdev);
+ *
+ * // Add a local IPv4 address:
+ * rc = osmo_sockaddr_str_from_str2(&osa_str, "192.168.200.1");
+ * rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas);
+ * rc = osmo_netdev_add_addr(netdev, &osa, 24);
+ *
+ * // Bring network interface up:
+ * rc = osmo_netdev_ifupdown(netdev, true);
+ *
+ * // Add default route (0.0.0.0/0):
+ * rc = osmo_sockaddr_str_from_str2(&osa_str, "0.0.0.0");
+ * rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas);
+ * rc = osmo_netdev_add_route(netdev, &osa, 0, NULL);
+ *
+ * // Close the tunnel (asssociated netdev object becomes unavailable)
+ * rc = osmo_tundev_close(tundev);
+ * // Free the object:
+ * osmo_tundev_free(tundev);
+ */
+
+#if (!EMBEDDED)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <net/if.h>
+
+#if defined(__linux__)
+#include <linux/if_tun.h>
+#else
+#error "Unknown platform!"
+#endif
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/netns.h>
+#include <osmocom/core/netdev.h>
+#include <osmocom/core/tun.h>
+
+#define TUN_DEV_PATH "/dev/net/tun"
+#define TUN_PACKET_MAX 8196
+
+#define LOGTUN(tun, lvl, fmt, args ...) \
+ LOGP(DLGLOBAL, lvl, "TUN(%s,if=%s/%u,ns=%s): " fmt, \
+ (tun)->name, (tun)->dev_name ? : "", \
+ (tun)->ifindex, (tun)->netns_name ? : "", ## args)
+
+struct osmo_tundev {
+ /* Name used to identify the osmo_tundev */
+ char *name;
+
+ /* netdev managing the tun interface: */
+ struct osmo_netdev *netdev;
+
+ /* ifindiex of the currently opened tunnel interface */
+ unsigned int ifindex;
+
+ /* Network interface name to use when setting up the tun device.
+ * NULL = let the system pick one. */
+ char *dev_name;
+ /* Whether dev_name is set by user or dynamically allocated by system */
+ bool dev_name_dynamic;
+
+ /* Write queue used since tun fd is set non-blocking */
+ struct osmo_wqueue wqueue;
+
+ /* netns name where the tun interface is created (NULL = default netns) */
+ char *netns_name;
+
+ /* API user private data */
+ void *priv_data;
+
+ /* Called by tundev each time a new packet is received on the tun interface. Can be NULL. */
+ osmo_tundev_data_ind_cb_t data_ind_cb;
+
+ /* Whether the tundev is in opened state (managing the tun interface) */
+ bool opened;
+};
+
+/* A new pkt arrived from the tun device, dispatch it to the API user */
+static int tundev_decaps(struct osmo_tundev *tundev)
+{
+ struct msgb *msg;
+ int rc;
+
+ msg = msgb_alloc(TUN_PACKET_MAX, "tundev_rx");
+
+ if ((rc = read(tundev->wqueue.bfd.fd, msgb_data(msg), TUN_PACKET_MAX)) <= 0) {
+ LOGTUN(tundev, LOGL_ERROR, "read() failed: %s (%d)\n", strerror(errno), errno);
+ msgb_free(msg);
+ return -1;
+ }
+ msgb_put(msg, rc);
+
+ if (tundev->data_ind_cb)
+ return tundev->data_ind_cb(tundev, msg);
+
+ msgb_free(msg);
+ return 0;
+}
+
+/* callback for tun device osmocom select loop integration */
+static int tundev_read_cb(struct osmo_fd *fd)
+{
+ struct osmo_tundev *tundev = fd->data;
+ return tundev_decaps(tundev);
+}
+
+/* callback for tun device osmocom select loop integration */
+static int tundev_write_cb(struct osmo_fd *fd, struct msgb *msg)
+{
+ struct osmo_tundev *tundev = fd->data;
+ size_t pkt_len = msgb_length(msg);
+
+ int rc;
+ rc = write(tundev->wqueue.bfd.fd, msgb_data(msg), pkt_len);
+ if (rc < 0)
+ LOGTUN(tundev, LOGL_ERROR, "write() failed: %s (%d)\n", strerror(errno), errno);
+ else if (rc < pkt_len)
+ LOGTUN(tundev, LOGL_ERROR, "short write() %d < %zu\n", rc, pkt_len);
+ return rc;
+}
+
+static int tundev_ifupdown_ind_cb(struct osmo_netdev *netdev, bool ifupdown)
+{
+ struct osmo_tundev *tundev = osmo_netdev_get_priv_data(netdev);
+ LOGTUN(tundev, LOGL_NOTICE, "Physical link state changed: %s\n",
+ ifupdown ? "UP" : "DOWN");
+
+ /* free any backlog, both on IFUP and IFDOWN. Keep the LMI, as it makes
+ * sense to get one out of the door ASAP. */
+ osmo_wqueue_clear(&tundev->wqueue);
+ return 0;
+}
+
+static int tundev_dev_name_chg_cb(struct osmo_netdev *netdev, const char *new_dev_name)
+{
+ struct osmo_tundev *tundev = osmo_netdev_get_priv_data(netdev);
+ LOGTUN(tundev, LOGL_NOTICE, "netdev changed name: %s -> %s\n",
+ osmo_netdev_get_dev_name(netdev), new_dev_name);
+
+ if (tundev->dev_name_dynamic) {
+ osmo_talloc_replace_string(tundev, &tundev->dev_name, new_dev_name);
+ } else {
+ /* TODO: in here we probably want to force the iface name back
+ * to tundev->dev_name one we have a osmo_netdev_set_ifname() API */
+ osmo_talloc_replace_string(tundev, &tundev->dev_name, new_dev_name);
+ }
+
+ return 0;
+}
+
+static int tundev_mtu_chg_cb(struct osmo_netdev *netdev, uint32_t new_mtu)
+{
+ struct osmo_tundev *tundev = osmo_netdev_get_priv_data(netdev);
+ LOGTUN(tundev, LOGL_NOTICE, "netdev changed MTU: %u\n", new_mtu);
+
+ return 0;
+}
+
+/*! Allocate a new tundev object.
+ * \param[in] ctx talloc context to use as a parent when allocating the tundev object
+ * \param[in] name A name providen to identify the tundev object
+ * \returns newly allocated tundev object on success; NULL on error
+ */
+struct osmo_tundev *osmo_tundev_alloc(void *ctx, const char *name)
+{
+ struct osmo_tundev *tundev;
+
+ tundev = talloc_zero(ctx, struct osmo_tundev);
+ if (!tundev)
+ return NULL;
+
+ tundev->netdev = osmo_netdev_alloc(tundev, name);
+ if (!tundev->netdev) {
+ talloc_free(tundev);
+ return NULL;
+ }
+ osmo_netdev_set_priv_data(tundev->netdev, tundev);
+ osmo_netdev_set_ifupdown_ind_cb(tundev->netdev, tundev_ifupdown_ind_cb);
+ osmo_netdev_set_dev_name_chg_cb(tundev->netdev, tundev_dev_name_chg_cb);
+ osmo_netdev_set_mtu_chg_cb(tundev->netdev, tundev_mtu_chg_cb);
+
+ tundev->name = talloc_strdup(tundev, name);
+ osmo_wqueue_init(&tundev->wqueue, 1000);
+ osmo_fd_setup(&tundev->wqueue.bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, tundev, 0);
+ tundev->wqueue.read_cb = tundev_read_cb;
+ tundev->wqueue.write_cb = tundev_write_cb;
+
+ return tundev;
+}
+
+/*! Free an allocated tundev object.
+ * \param[in] tundev The tundev object to free
+ */
+void osmo_tundev_free(struct osmo_tundev *tundev)
+{
+ if (!tundev)
+ return;
+ osmo_tundev_close(tundev);
+ osmo_netdev_free(tundev->netdev);
+ talloc_free(tundev);
+}
+
+/*! Open and configure fd of the tunnel device.
+ * \param[in] tundev The tundev object whose tunnel interface to open
+ * \param[in] flags internal linux flags to pass when creating the device (not used yet)
+ * \returns 0 on success; negative on error
+ */
+static int tundev_open_fd(struct osmo_tundev *tundev, int flags)
+{
+ struct ifreq ifr;
+ int fd, rc;
+
+ fd = open(TUN_DEV_PATH, O_RDWR);
+ if (fd < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "Cannot open " TUN_DEV_PATH ": %s\n", strerror(errno));
+ return fd;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TUN | IFF_NO_PI | flags;
+ if (tundev->dev_name) {
+ /* if a TUN interface name was specified, put it in the structure; otherwise,
+ the kernel will try to allocate the "next" device of the specified type */
+ osmo_strlcpy(ifr.ifr_name, tundev->dev_name, IFNAMSIZ);
+ }
+
+ /* try to create the device */
+ rc = ioctl(fd, TUNSETIFF, (void *) &ifr);
+ if (rc < 0)
+ goto close_ret;
+
+ /* Read name back from device */
+ if (!tundev->dev_name) {
+ ifr.ifr_name[IFNAMSIZ - 1] = '\0';
+ tundev->dev_name = talloc_strdup(tundev, ifr.ifr_name);
+ tundev->dev_name_dynamic = true;
+ }
+
+ /* Store interface index:
+ * (Note: there's a potential race condition here between creating the
+ * iface with a given name above and attempting to retrieve its ifindex based
+ * on that name. Someone (ie udev) could have the iface renamed in
+ * between here. It's a pity that TUNSETIFF doesn't copy back to us the
+ * newly allocated ifinidex as it does with ifname)
+ */
+ tundev->ifindex = if_nametoindex(tundev->dev_name);
+ if (tundev->ifindex == 0) {
+ LOGTUN(tundev, LOGL_ERROR, "Unable to find ifinidex for dev %s\n",
+ tundev->dev_name);
+ rc = -ENODEV;
+ goto close_ret;
+ }
+
+ LOGTUN(tundev, LOGL_INFO, "TUN device created\n");
+
+ /* set non-blocking: */
+ rc = fcntl(fd, F_GETFL);
+ if (rc < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "fcntl(F_GETFL) failed: %s (%d)\n",
+ strerror(errno), errno);
+ goto close_ret;
+ }
+ rc = fcntl(fd, F_SETFL, rc | O_NONBLOCK);
+ if (rc < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "fcntl(F_SETFL, O_NONBLOCK) failed: %s (%d)\n",
+ strerror(errno), errno);
+ goto close_ret;
+ }
+ return fd;
+
+close_ret:
+ close(fd);
+ return rc;
+}
+
+/*! Open the tunnel device owned by the tundev object.
+ * \param[in] tundev The tundev object to open
+ * \returns 0 on success; negative on error
+ */
+int osmo_tundev_open(struct osmo_tundev *tundev)
+{
+ struct osmo_netns_switch_state switch_state;
+ int rc;
+ int netns_fd = -1;
+
+ if (tundev->opened)
+ return -EALREADY;
+
+ /* temporarily switch to specified namespace to create tun device */
+ if (tundev->netns_name) {
+ LOGTUN(tundev, LOGL_INFO, "Open tun: Switch to netns '%s'\n",
+ tundev->netns_name);
+ netns_fd = osmo_netns_open_fd(tundev->netns_name);
+ if (netns_fd < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Open tun: Cannot switch to netns '%s': %s (%d)\n",
+ tundev->netns_name, strerror(errno), errno);
+ return netns_fd;
+ }
+ rc = osmo_netns_switch_enter(netns_fd, &switch_state);
+ if (rc < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "Open tun: Cannot switch to netns '%s': %s (%d)\n",
+ tundev->netns_name, strerror(errno), errno);
+ goto err_close_netns_fd;
+ }
+ }
+
+ tundev->wqueue.bfd.fd = tundev_open_fd(tundev, 0);
+ if (tundev->wqueue.bfd.fd < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "Cannot open TUN device: %s\n", strerror(errno));
+ rc = -ENODEV;
+ goto err_restore_ns;
+ }
+
+ /* switch back to default namespace */
+ if (tundev->netns_name) {
+ rc = osmo_netns_switch_exit(&switch_state);
+ if (rc < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "Open tun: Cannot switch back from netns '%s': %s\n",
+ tundev->netns_name, strerror(errno));
+ goto err_close_tun;
+ }
+ LOGTUN(tundev, LOGL_INFO, "Open tun: Back from netns '%s'\n",
+ tundev->netns_name);
+ }
+
+ rc = osmo_netdev_set_netns_name(tundev->netdev, tundev->netns_name);
+ if (rc < 0)
+ goto err_close_tun;
+ rc = osmo_netdev_set_ifindex(tundev->netdev, tundev->ifindex);
+ if (rc < 0)
+ goto err_close_tun;
+
+ rc = osmo_netdev_register(tundev->netdev);
+ if (rc < 0)
+ goto err_close_tun;
+
+ rc = osmo_fd_register(&tundev->wqueue.bfd);
+ if (rc < 0)
+ goto err_unregister_netdev;
+
+ tundev->opened = true;
+ return 0;
+
+err_unregister_netdev:
+ osmo_netdev_unregister(tundev->netdev);
+err_close_tun:
+ close(tundev->wqueue.bfd.fd);
+ tundev->wqueue.bfd.fd = -1;
+err_restore_ns:
+ if (tundev->netns_name)
+ osmo_netns_switch_exit(&switch_state);
+err_close_netns_fd:
+ if (netns_fd >= 0)
+ close(netns_fd);
+ return rc;
+}
+
+/*! Close the tunnel device owned by the tundev object.
+ * \param[in] tundev The tundev object to close
+ * \returns 0 on success; negative on error
+ */
+int osmo_tundev_close(struct osmo_tundev *tundev)
+{
+ if (!tundev->opened)
+ return -EALREADY;
+
+ osmo_wqueue_clear(&tundev->wqueue);
+ if (tundev->wqueue.bfd.fd != -1) {
+ osmo_fd_unregister(&tundev->wqueue.bfd);
+ close(tundev->wqueue.bfd.fd);
+ tundev->wqueue.bfd.fd = -1;
+ }
+
+ osmo_netdev_unregister(tundev->netdev);
+ if (tundev->dev_name_dynamic) {
+ TALLOC_FREE(tundev->dev_name);
+ tundev->dev_name_dynamic = false;
+ }
+ tundev->opened = false;
+ return 0;
+}
+
+/*! Retrieve whether the tundev object is in "opened" state.
+ * \param[in] tundev The tundev object to check
+ * \returns true if in state "opened"; false otherwise
+ */
+bool osmo_tundev_is_open(struct osmo_tundev *tundev)
+{
+ return tundev->opened;
+}
+
+/*! Set private user data pointer on the tundev object.
+ * \param[in] tundev The tundev object where the field is set
+ */
+void osmo_tundev_set_priv_data(struct osmo_tundev *tundev, void *priv_data)
+{
+ tundev->priv_data = priv_data;
+}
+
+/*! Get private user data pointer from the tundev object.
+ * \param[in] tundev The tundev object from where to retrieve the field
+ * \returns The current value of the priv_data field.
+ */
+void *osmo_tundev_get_priv_data(struct osmo_tundev *tundev)
+{
+ return tundev->priv_data;
+}
+
+/*! Set data_ind_cb callback, called when a new packet is received on the tun interface.
+ * \param[in] tundev The tundev object where the field is set
+ * \param[in] data_ind_cb the user provided function to be called when a new packet is received
+ */
+void osmo_tundev_set_data_ind_cb(struct osmo_tundev *tundev, osmo_tundev_data_ind_cb_t data_ind_cb)
+{
+ tundev->data_ind_cb = data_ind_cb;
+}
+
+/*! Get name used to identify the tundev object.
+ * \param[in] tundev The tundev object from where to retrieve the field
+ * \returns The current value of the name used to identify the tundev object
+ */
+const char *osmo_tundev_get_name(const struct osmo_tundev *tundev)
+{
+ return tundev->name;
+}
+
+/*! Set name used to name the tunnel interface created by the tundev object.
+ * \param[in] tundev The tundev object where the field is set
+ * \param[in] dev_name The tunnel interface name to use
+ * \returns 0 on success; negative on error
+ *
+ * This is used during osmo_tundev_open() time, and hence shouldn't be changed
+ * when the tundev object is in "opened" state.
+ * If left as NULL (default), the system will pick a suitable name during
+ * osmo_tundev_open(), and the field will be updated to the system-selected
+ * name, which can be retrieved later with osmo_tundev_get_dev_name().
+ */
+int osmo_tundev_set_dev_name(struct osmo_tundev *tundev, const char *dev_name)
+{
+ if (tundev->opened)
+ return -EALREADY;
+ osmo_talloc_replace_string(tundev, &tundev->dev_name, dev_name);
+ tundev->dev_name_dynamic = false;
+ return 0;
+}
+
+/*! Get name used to name the tunnel interface created by the tundev object
+ * \param[in] tundev The tundev object from where to retrieve the field
+ * \returns The current value of the configured tunnel interface name to use
+ */
+const char *osmo_tundev_get_dev_name(const struct osmo_tundev *tundev)
+{
+ return tundev->dev_name;
+}
+
+/*! Set name of the network namespace to use when opening the tunnel interface
+ * \param[in] tundev The tundev object where the field is set
+ * \param[in] netns_name The network namespace to use during tunnel interface creation
+ * \returns 0 on success; negative on error
+ *
+ * This is used during osmo_tundev_open() time, and hence shouldn't be changed
+ * when the tundev object is in "opened" state.
+ * If left as NULL (default), the system will stay in the current network namespace.
+ */
+int osmo_tundev_set_netns_name(struct osmo_tundev *tundev, const char *netns_name)
+{
+ if (tundev->opened)
+ return -EALREADY;
+ osmo_talloc_replace_string(tundev, &tundev->netns_name, netns_name);
+ return 0;
+}
+
+/*! Get name of network namespace used when opening the tunnel interface
+ * \param[in] tundev The tundev object from where to retrieve the field
+ * \returns The current value of the configured network namespace
+ */
+const char *osmo_tundev_get_netns_name(const struct osmo_tundev *tundev)
+{
+ return tundev->netns_name;
+}
+
+/*! Get netdev managing the tunnel interface of tundev
+ * \param[in] tundev The tundev object from where to retrieve the field
+ * \returns The netdev objet managing the tun interface
+ */
+struct osmo_netdev *osmo_tundev_get_netdev(struct osmo_tundev *tundev)
+{
+ return tundev->netdev;
+}
+
+/*! Submit a packet to the tunnel device managed by the tundev object
+ * \param[in] tundev The tundev object owning the tunnel device where to inject the packet
+ * \param[in] msg The msgb containg the packet to transfer
+ * \returns The current value of the configured network namespace
+ *
+ * This function takes the ownership of msg in all cases.
+ */
+int osmo_tundev_send(struct osmo_tundev *tundev, struct msgb *msg)
+{
+ int rc = osmo_wqueue_enqueue(&tundev->wqueue, msg);
+ if (rc < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "Failed to enqueue the packet\n");
+ msgb_free(msg);
+ return rc;
+ }
+ return rc;
+}
+
+
+#endif /* (!EMBEDDED) */
+
+/*! @} */
diff --git a/src/use_count.c b/src/core/use_count.c
index d07e47fc..9714403d 100644
--- a/src/use_count.c
+++ b/src/core/use_count.c
@@ -19,10 +19,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <errno.h>
@@ -239,6 +235,8 @@ int _osmo_use_count_get_put(struct osmo_use_count *uc, const char *use, int32_t
{
struct osmo_use_count_entry *e;
int32_t old_use_count;
+ if (!uc)
+ return -EINVAL;
if (!change)
return 0;
diff --git a/src/utils.c b/src/core/utils.c
index f5896c41..1ec940d0 100644
--- a/src/utils.c
+++ b/src/core/utils.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -151,13 +147,15 @@ uint8_t osmo_char2bcd(char c)
*/
int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibble, int end_nibble, bool allow_hex)
{
- char *dst_end = dst + dst_size - 1;
+ char *dst_end;
int nibble_i;
int rc = 0;
if (!dst || dst_size < 1 || start_nibble < 0)
return -ENOMEM;
+ dst_end = dst + dst_size - 1;
+
for (nibble_i = start_nibble; nibble_i < end_nibble && dst < dst_end; nibble_i++, dst++) {
uint8_t nibble = bcd[nibble_i >> 1];
if ((nibble_i & 1))
@@ -206,7 +204,7 @@ int osmo_str2bcd(uint8_t *dst, size_t dst_size, const char *digits, int start_ni
if (end_nibble & 1)
end_nibble++;
}
- if ((end_nibble / 2) > dst_size)
+ if ((unsigned int) (end_nibble / 2) > dst_size)
return -ENOMEM;
for (nibble_i = start_nibble; nibble_i < end_nibble; nibble_i++) {
@@ -241,7 +239,7 @@ int osmo_str2bcd(uint8_t *dst, size_t dst_size, const char *digits, int start_ni
* \param[in] max_len maximum space in output buffer
* \returns number of parsed octets, or -1 on error
*/
-int osmo_hexparse(const char *str, uint8_t *b, int max_len)
+int osmo_hexparse(const char *str, uint8_t *b, unsigned int max_len)
{
char c;
@@ -316,7 +314,7 @@ const char *osmo_hexdump_buf(char *out_buf, size_t out_buf_size, const unsigned
for (i = 0; i < len; i++) {
const char *delimp = delim;
int len_remain = out_buf_size - (cur - out_buf) - 1;
- if (len_remain < (2 + delim_len)
+ if (len_remain < (int) (2 + delim_len)
&& !(!delim_after_last && i == (len - 1) && len_remain >= 2))
break;
@@ -344,7 +342,7 @@ const char *osmo_hexdump_buf(char *out_buf, size_t out_buf_size, const unsigned
*/
char *osmo_ubit_dump_buf(char *buf, size_t buf_len, const uint8_t *bits, unsigned int len)
{
- int i;
+ unsigned int i;
if (len > buf_len-1)
len = buf_len-1;
@@ -407,9 +405,6 @@ char *osmo_hexdump(const unsigned char *buf, int len)
*
* This function will print a sequence of bytes as hexadecimal numbers,
* adding one space character between each byte (e.g. "1a ef d9")
- *
- * The maximum size of the output buffer is 4096 bytes, i.e. the maximum
- * number of input bytes that can be printed in one call is 1365!
*/
char *osmo_hexdump_c(const void *ctx, const unsigned char *buf, int len)
{
@@ -446,9 +441,6 @@ char *osmo_hexdump_nospc(const unsigned char *buf, int len)
*
* This function will print a sequence of bytes as hexadecimal numbers,
* without any space character between each byte (e.g. "1aefd9")
- *
- * The maximum size of the output buffer is 4096 bytes, i.e. the maximum
- * number of input bytes that can be printed in one call is 2048!
*/
char *osmo_hexdump_nospc_c(const void *ctx, const unsigned char *buf, int len)
{
@@ -469,7 +461,7 @@ char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len)
__attribute__((weak, alias("osmo_hexdump_nospc")));
#endif
-#include "../config.h"
+#include "config.h"
#ifdef HAVE_CTYPE_H
#include <ctype.h>
/*! Convert an entire string to lower case
@@ -687,7 +679,7 @@ bool osmo_identifier_valid(const char *str)
* To guarantee passing osmo_separated_identifiers_valid(), replace_with must not itself be an illegal character. If in
* doubt, use '-'.
* \param[inout] str Identifier to sanitize, must be nul terminated and in a writable buffer.
- * \param[in] sep_chars Additional characters that are allowed besides osmo_identifier_illegal_chars.
+ * \param[in] sep_chars Additional characters that are to be replaced besides osmo_identifier_illegal_chars.
* \param[in] replace_with Replace any illegal characters with this character.
*/
void osmo_identifier_sanitize_buf(char *str, const char *sep_chars, char replace_with)
@@ -764,7 +756,7 @@ int osmo_print_n(char *buf, size_t bufsize, const char *str, size_t n)
* the non-legacy format also escapes those as "\xNN" sequences.
* \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()).
*/
-static size_t _osmo_escape_str_buf(char *buf, size_t bufsize, const char *str, int in_len, bool legacy_format)
+static int _osmo_escape_str_buf(char *buf, size_t bufsize, const char *str, int in_len, bool legacy_format)
{
struct osmo_strbuf sb = { .buf = buf, .len = bufsize };
int in_pos = 0;
@@ -836,6 +828,18 @@ done:
* \param[in] bufsize sizeof(buf).
* \param[in] str A string that may contain any characters.
* \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars).
+ * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()).
+ */
+int osmo_escape_str_buf3(char *buf, size_t bufsize, const char *str, int in_len)
+{
+ return _osmo_escape_str_buf(buf, bufsize, str, in_len, false);
+}
+
+/*! Return the string with all non-printable characters escaped.
+ * \param[out] buf string buffer to write escaped characters to.
+ * \param[in] bufsize sizeof(buf).
+ * \param[in] str A string that may contain any characters.
+ * \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars).
* \return The output buffer (buf).
*/
char *osmo_escape_str_buf2(char *buf, size_t bufsize, const char *str, int in_len)
@@ -893,6 +897,20 @@ static size_t _osmo_quote_str_buf(char *buf, size_t bufsize, const char *str, in
return sb.chars_needed;
}
+/*! Like osmo_escape_str_buf3(), but returns double-quotes around a string, or "NULL" for a NULL string.
+ * This allows passing any char* value and get its C representation as string.
+ * The function signature is suitable for OSMO_STRBUF_APPEND_NOLEN().
+ * \param[out] buf string buffer to write escaped characters to.
+ * \param[in] bufsize sizeof(buf).
+ * \param[in] str A string that may contain any characters.
+ * \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length.
+ * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()).
+ */
+int osmo_quote_str_buf3(char *buf, size_t bufsize, const char *str, int in_len)
+{
+ return _osmo_quote_str_buf(buf, bufsize, str, in_len, false);
+}
+
/*! Like osmo_escape_str_buf2(), but returns double-quotes around a string, or "NULL" for a NULL string.
* This allows passing any char* value and get its C representation as string.
* The function signature is suitable for OSMO_STRBUF_APPEND_NOLEN().
@@ -1195,6 +1213,51 @@ char osmo_luhn(const char* in, int in_len)
return (sum * 9) % 10 + '0';
}
+/*! Remove up to N chars from the end of an osmo_strbuf.
+ * |--char-count---| - - chars_needed - - |
+ * |<---------drop----------|
+ */
+void osmo_strbuf_drop_tail(struct osmo_strbuf *sb, size_t n_chars)
+{
+ size_t drop_n;
+ if (sb->pos <= sb->buf)
+ return;
+ drop_n = OSMO_MIN(sb->chars_needed, n_chars);
+ sb->chars_needed -= drop_n;
+ /* chars_needed was reduced by n_chars, which may have been entirely behind the end of a full buffer, within the
+ * hypothetical chars_needed. Modify the buffer tail pos only if the buffer is not or longer full now. */
+ if (sb->chars_needed >= OSMO_STRBUF_CHAR_COUNT(*sb))
+ return;
+ sb->pos = sb->buf + sb->chars_needed;
+ *sb->pos = '\0';
+}
+
+/*! Let osmo_strbuf know that n_chars characters (excluding nul) were written to the end of the buffer.
+ * If sb is nonempty, the n_chars are assumed to have been written to sb->pos. If sb is still empty and pos == NULL, the
+ * n_chars are assumed to have been written to the start of the buffer.
+ * Advance sb->pos and sb->chars_needed by at most n_chars, or up to sb->len - 1.
+ * Ensure nul termination. */
+void osmo_strbuf_added_tail(struct osmo_strbuf *sb, size_t n_chars)
+{
+ /* On init of an osmo_strbuf, sb->pos == NULL, which is defined as semantically identical to pointing at the
+ * start of the buffer. A caller may just write to the buffer and call osmo_strbuf_added_tail(), in which case
+ * still pos == NULL. pos != NULL happens as soon as the first OSMO_STRBUF_*() API has acted on the strbuf. */
+ if (!sb->pos)
+ sb->pos = sb->buf;
+ sb->chars_needed += n_chars;
+ /* first get remaining space, not counting trailing nul; but safeguard against empty buffer */
+ size_t n_added = OSMO_STRBUF_REMAIN(*sb);
+ if (n_added)
+ n_added--;
+ /* do not add more than fit in sb->len, still ensuring nul termination */
+ n_added = OSMO_MIN(n_added, n_chars);
+ if (n_added)
+ sb->pos += n_added;
+ /* when a strbuf is full, sb->pos may point after the final nul, so nul terminate only when pos is valid. */
+ if (sb->pos < sb->buf + sb->len)
+ *sb->pos = '\0';
+}
+
/*! Compare start of a string.
* This is an optimisation of 'strstr(str, startswith_str) == str' because it doesn't search through the entire string.
* \param str (Longer) string to compare.
@@ -1232,7 +1295,7 @@ int osmo_float_str_to_int(int64_t *val, const char *str, unsigned int precision)
int64_t precision_factor;
int64_t integer_max;
int64_t decimal_max;
- int i;
+ unsigned int i;
OSMO_ASSERT(val);
*val = 0;
@@ -1256,7 +1319,7 @@ int osmo_float_str_to_int(int64_t *val, const char *str, unsigned int precision)
if (point)
return -EINVAL;
point = p;
- } else if (!isdigit(*p))
+ } else if (!isdigit((unsigned char)*p))
return -EINVAL;
}
@@ -1405,4 +1468,119 @@ char *osmo_int_to_float_str_c(void *ctx, int64_t val, unsigned int precision)
OSMO_NAME_C_IMPL(ctx, 16, "ERROR", osmo_int_to_float_str_buf, val, precision)
}
+/*! Convert a string of a number to int64_t, including all common strtoll() validity checks.
+ * It's not so trivial to call strtoll() and properly verify that the input string was indeed a valid number string.
+ * \param[out] result Buffer for the resulting integer number, or NULL if the caller is only interested in the
+ * validation result (returned rc).
+ * \param[in] str The string to convert.
+ * \param[in] base The integer base, i.e. 10 for decimal numbers or 16 for hexadecimal, as in strtoll().
+ * \param[in] min_val The smallest valid number expected in the string.
+ * \param[in] max_val The largest valid number expected in the string.
+ * \return 0 on success, -EOVERFLOW if the number in the string exceeds int64_t, -ENOTSUPP if the base is not supported,
+ * -ERANGE if the converted number exceeds the range [min_val..max_val] but is still within int64_t range, -E2BIG if
+ * surplus characters follow after the number, -EINVAL if the string does not contain a number. In case of -ERANGE and
+ * -E2BIG, the converted number is still accurately returned in result. In case of -EOVERFLOW, the returned value is
+ * clamped to INT64_MIN..INT64_MAX.
+ */
+int osmo_str_to_int64(int64_t *result, const char *str, int base, int64_t min_val, int64_t max_val)
+{
+ long long int val;
+ char *endptr;
+ if (result)
+ *result = 0;
+ if (!str || !*str)
+ return -EINVAL;
+ errno = 0;
+ val = strtoll(str, &endptr, base);
+ /* In case the number string exceeds long long int range, strtoll() clamps the returned value to LLONG_MIN or
+ * LLONG_MAX. Make sure of the same here with respect to int64_t. */
+ if (val < INT64_MIN) {
+ if (result)
+ *result = INT64_MIN;
+ return -ERANGE;
+ }
+ if (val > INT64_MAX) {
+ if (result)
+ *result = INT64_MAX;
+ return -ERANGE;
+ }
+ if (result)
+ *result = (int64_t)val;
+ switch (errno) {
+ case 0:
+ break;
+ case ERANGE:
+ return -EOVERFLOW;
+ default:
+ case EINVAL:
+ return -ENOTSUP;
+ }
+ if (!endptr || *endptr) {
+ /* No chars were converted */
+ if (endptr == str)
+ return -EINVAL;
+ /* Or there are surplus chars after the converted number */
+ return -E2BIG;
+ }
+ if (val < min_val || val > max_val)
+ return -ERANGE;
+ return 0;
+}
+
+/*! Convert a string of a number to int, including all common strtoll() validity checks.
+ * Same as osmo_str_to_int64() but using the plain int data type.
+ * \param[out] result Buffer for the resulting integer number, or NULL if the caller is only interested in the
+ * validation result (returned rc).
+ * \param[in] str The string to convert.
+ * \param[in] base The integer base, i.e. 10 for decimal numbers or 16 for hexadecimal, as in strtoll().
+ * \param[in] min_val The smallest valid number expected in the string.
+ * \param[in] max_val The largest valid number expected in the string.
+ * \return 0 on success, -EOVERFLOW if the number in the string exceeds int range, -ENOTSUPP if the base is not supported,
+ * -ERANGE if the converted number exceeds the range [min_val..max_val] but is still within int range, -E2BIG if
+ * surplus characters follow after the number, -EINVAL if the string does not contain a number. In case of -ERANGE and
+ * -E2BIG, the converted number is still accurately returned in result. In case of -EOVERFLOW, the returned value is
+ * clamped to INT_MIN..INT_MAX.
+ */
+int osmo_str_to_int(int *result, const char *str, int base, int min_val, int max_val)
+{
+ int64_t val;
+ int rc = osmo_str_to_int64(&val, str, base, min_val, max_val);
+ /* In case the number string exceeds long long int range, strtoll() clamps the returned value to LLONG_MIN or
+ * LLONG_MAX. Make sure of the same here with respect to int. */
+ if (val < INT_MIN) {
+ if (result)
+ *result = INT_MIN;
+ return -EOVERFLOW;
+ }
+ if (val > INT_MAX) {
+ if (result)
+ *result = INT_MAX;
+ return -EOVERFLOW;
+ }
+ if (result)
+ *result = (int)val;
+ return rc;
+}
+
+/*! Replace a string using talloc and release its prior content (if any).
+ * This is a format string capable equivalent of osmo_talloc_replace_string().
+ * \param[in] ctx Talloc context to use for allocation.
+ * \param[out] dst Pointer to string, will be updated with ptr to new string.
+ * \param[in] fmt Format string that will be copied to newly allocated string. */
+void osmo_talloc_replace_string_fmt(void *ctx, char **dst, const char *fmt, ...)
+{
+ char *name = NULL;
+
+ if (fmt != NULL) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ name = talloc_vasprintf(ctx, fmt, ap);
+ va_end(ap);
+ }
+
+ talloc_free(*dst);
+ *dst = name;
+}
+
/*! @} */
diff --git a/src/write_queue.c b/src/core/write_queue.c
index b208b25e..8fb73a67 100644
--- a/src/write_queue.c
+++ b/src/core/write_queue.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
@@ -106,7 +102,7 @@ void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length)
/*! Enqueue a new \ref msgb into a write queue (without logging full queue events)
* \param[in] queue Write queue to be used
* \param[in] data to-be-enqueued message buffer
- * \returns 0 on success; negative on error
+ * \returns 0 on success; negative on error (MESSAGE NOT FREED IN CASE OF ERROR).
*/
int osmo_wqueue_enqueue_quiet(struct osmo_wqueue *queue, struct msgb *data)
{
@@ -122,7 +118,7 @@ int osmo_wqueue_enqueue_quiet(struct osmo_wqueue *queue, struct msgb *data)
/*! Enqueue a new \ref msgb into a write queue
* \param[in] queue Write queue to be used
* \param[in] data to-be-enqueued message buffer
- * \returns 0 on success; negative on error
+ * \returns 0 on success; negative on error (MESSAGE NOT FREED IN CASE OF ERROR).
*/
int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data)
{
@@ -151,4 +147,24 @@ void osmo_wqueue_clear(struct osmo_wqueue *queue)
queue->bfd.when &= ~OSMO_FD_WRITE;
}
+/*! Update write queue length & drop excess messages.
+ * \param[in] queue linked list header of message queue
+ * \param[in] len new max. wqueue length
+ * \returns Number of messages dropped.
+ *
+ * Messages beyond the new maximum message queue size will be dropped.
+ */
+size_t osmo_wqueue_set_maxlen(struct osmo_wqueue *queue, unsigned int len)
+{
+ size_t dropped_msgs = 0;
+ struct msgb *msg;
+ queue->max_length = len;
+ while (queue->current_length > queue->max_length) {
+ msg = msgb_dequeue_count(&queue->msg_queue, &queue->current_length);
+ msgb_free(msg);
+ dropped_msgs++;
+ }
+ return dropped_msgs;
+}
+
/*! @} */
diff --git a/src/ctrl/Makefile.am b/src/ctrl/Makefile.am
index a5f191d7..f9e34333 100644
--- a/src/ctrl/Makefile.am
+++ b/src/ctrl/Makefile.am
@@ -1,9 +1,10 @@
# 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=5:0:5
+LIBVERSION=8:1:8
-AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
if ENABLE_CTRL
lib_LTLIBRARIES = libosmoctrl.la
@@ -12,7 +13,7 @@ libosmoctrl_la_SOURCES = control_cmd.c control_if.c fsm_ctrl_commands.c
libosmoctrl_la_LDFLAGS = $(LTLDFLAGS_OSMOCTRL) -version-info $(LIBVERSION) -no-undefined
libosmoctrl_la_LIBADD = $(TALLOC_LIBS) \
- $(top_builddir)/src/libosmocore.la \
+ $(top_builddir)/src/core/libosmocore.la \
$(top_builddir)/src/gsm/libosmogsm.la \
$(top_builddir)/src/vty/libosmovty.la
@@ -21,5 +22,6 @@ libosmoctrl_la_SOURCES += control_vty.c
endif
EXTRA_DIST = libosmoctrl.map
+EXTRA_libosmoctrl_la_DEPENDENCIES = libosmoctrl.map
endif
diff --git a/src/ctrl/control_cmd.c b/src/ctrl/control_cmd.c
index 33496bd8..db205510 100644
--- a/src/ctrl/control_cmd.c
+++ b/src/ctrl/control_cmd.c
@@ -20,10 +20,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <ctype.h>
@@ -35,6 +31,7 @@
#include <unistd.h>
#include <osmocom/ctrl/control_cmd.h>
+#include <osmocom/ctrl/control_if.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
@@ -207,13 +204,23 @@ failure:
}
/*! Install a given command definition at a given CTRL node.
- * \param[in] node CTRL node at whihc \a cmd is to be installed
+ * \param[in] node CTRL node at which \a cmd is to be installed
* \param[in] cmd command definition to be installed
* \returns 0 on success; negative on error */
int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd)
{
vector cmds_vec;
+ /* If this assert triggers, it means the program forgot to initialize
+ * the CTRL interface first by calling ctrl_handle_alloc(2)() directly
+ * or indirectly through ctrl_interface_setup_dynip(2)()
+ */
+ if (!ctrl_node_vec) {
+ LOGP(DLCTRL, LOGL_ERROR,
+ "ctrl_handle must be initialized prior to installing cmds.\n");
+ return -ENODEV;
+ }
+
cmds_vec = vector_lookup_ensure(ctrl_node_vec, node);
if (!cmds_vec) {
@@ -516,92 +523,61 @@ err:
* \returns callee-allocated message buffer containing the encoded \a cmd; NULL on error */
struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd)
{
- struct msgb *msg;
+ struct msgb *msg = NULL;
+ char *strbuf;
+ size_t len;
const char *type;
- char *tmp;
if (!cmd->id)
return NULL;
- msg = msgb_alloc_headroom(4096, 128, "ctrl command make");
- if (!msg)
- return NULL;
-
type = get_value_string(ctrl_type_vals, cmd->type);
switch (cmd->type) {
case CTRL_TYPE_GET:
if (!cmd->variable)
- goto err;
-
- tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable);
- if (!tmp) {
- LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
- goto err;
- }
-
- msg->l2h = msgb_put(msg, strlen(tmp));
- memcpy(msg->l2h, tmp, strlen(tmp));
- talloc_free(tmp);
+ return NULL;
+ strbuf = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable);
break;
case CTRL_TYPE_SET:
if (!cmd->variable || !cmd->value)
- goto err;
-
- tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
- cmd->value);
- if (!tmp) {
- LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
- goto err;
- }
-
- msg->l2h = msgb_put(msg, strlen(tmp));
- memcpy(msg->l2h, tmp, strlen(tmp));
- talloc_free(tmp);
+ return NULL;
+ strbuf = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id,
+ cmd->variable, cmd->value);
break;
case CTRL_TYPE_GET_REPLY:
case CTRL_TYPE_SET_REPLY:
case CTRL_TYPE_TRAP:
if (!cmd->variable || !cmd->reply)
- goto err;
-
- tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
- cmd->reply);
- if (!tmp) {
- LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
- goto err;
- }
-
- msg->l2h = msgb_put(msg, strlen(tmp));
- memcpy(msg->l2h, tmp, strlen(tmp));
- talloc_free(tmp);
+ return NULL;
+ strbuf = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id,
+ cmd->variable, cmd->reply);
break;
case CTRL_TYPE_ERROR:
if (!cmd->reply)
- goto err;
-
- tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id,
- cmd->reply);
- if (!tmp) {
- LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
- goto err;
- }
-
- msg->l2h = msgb_put(msg, strlen(tmp));
- memcpy(msg->l2h, tmp, strlen(tmp));
- talloc_free(tmp);
+ return NULL;
+ strbuf = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->reply);
break;
default:
LOGP(DLCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type);
- goto err;
- break;
+ return NULL;
}
- return msg;
+ if (!strbuf) {
+ LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
+ goto ret;
+ }
+ len = strlen(strbuf);
-err:
- msgb_free(msg);
- return NULL;
+ msg = msgb_alloc_headroom(len + 128, 128, "ctrl ERROR command make");
+ if (!msg)
+ goto ret;
+ msg->l2h = msgb_put(msg, len);
+ memcpy(msg->l2h, strbuf, len);
+
+ret:
+ talloc_free(strbuf);
+ return msg;
}
/*! Build a deferred control command state and keep it the per-connection list of deferred commands.
@@ -672,7 +648,7 @@ int ctrl_cmd_def_send(struct ctrl_cmd_def *cd)
cmd->type = CTRL_TYPE_ERROR;
}
- rc = ctrl_cmd_send(&cmd->ccon->write_queue, cmd);
+ rc = ctrl_cmd_send2(cmd->ccon, cmd);
talloc_free(cmd);
llist_del(&cd->list);
diff --git a/src/ctrl/control_if.c b/src/ctrl/control_if.c
index 5eb81c7b..c265c3a9 100644
--- a/src/ctrl/control_if.c
+++ b/src/ctrl/control_if.c
@@ -20,10 +20,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include "config.h"
@@ -35,6 +31,7 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <limits.h>
#include <arpa/inet.h>
@@ -50,9 +47,11 @@
#include <osmocom/ctrl/control_cmd.h>
#include <osmocom/ctrl/control_if.h>
+#include <osmocom/ctrl/control_vty.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/stat_item.h>
#include <osmocom/core/select.h>
#include <osmocom/core/counter.h>
#include <osmocom/core/talloc.h>
@@ -82,26 +81,22 @@ static LLIST_HEAD(ctrl_lookup_helpers);
* \returns 1 on success; 0 in case of error */
int ctrl_parse_get_num(vector vline, int i, long *num)
{
- char *token, *tmp;
+ char *token;
+ int64_t val;
if (i >= vector_active(vline))
return 0;
token = vector_slot(vline, i);
- errno = 0;
- if (token[0] == '\0')
- return 0;
-
- *num = strtol(token, &tmp, 10);
- if (tmp[0] != '\0' || errno != 0)
+ if (osmo_str_to_int64(&val, token, 10, LONG_MIN, LONG_MAX))
return 0;
-
+ *num = (long)val;
return 1;
}
/*! Send a CTRL command to all connections.
* \param[in] ctrl global control handle
- * \param[in] cmd command to send to all connections in \ctrl
+ * \param[in] cmd command to send to all connections in ctrl
* \returns number of times the command has been sent */
int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd)
{
@@ -111,18 +106,28 @@ int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd)
llist_for_each_entry(ccon, &ctrl->ccon_list, list_entry) {
if (ccon == cmd->ccon)
continue;
- if (ctrl_cmd_send(&ccon->write_queue, cmd))
+ if (ctrl_cmd_send2(ccon, cmd))
ret++;
}
return ret;
}
-/*! Encode a CTRL command and append it to the given write queue
+/*! Encode a CTRL command and append it to the given ctrl_connection
* \param[inout] queue write queue to which encoded \a cmd shall be appended
* \param[in] cmd decoded command representation
* \returns 0 in case of success; negative on error */
int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd)
{
+ struct ctrl_connection *ccon = container_of(queue, struct ctrl_connection, write_queue);
+ return ctrl_cmd_send2(ccon, cmd);
+}
+
+/*! Encode a CTRL command and append it to the given ctrl_connection
+ * \param[inout] queue write queue to which encoded \a cmd shall be appended
+ * \param[in] cmd decoded command representation
+ * \returns 0 in case of success; negative on error */
+int ctrl_cmd_send2(struct ctrl_connection *ccon, struct ctrl_cmd *cmd)
+{
int ret;
struct msgb *msg;
@@ -135,7 +140,7 @@ int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd)
ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
ipa_prepend_header(msg, IPAC_PROTO_OSMO);
- ret = osmo_wqueue_enqueue(queue, msg);
+ ret = osmo_wqueue_enqueue(&ccon->write_queue, msg);
if (ret != 0) {
LOGP(DLCTRL, LOGL_ERROR, "Failed to enqueue the command.\n");
msgb_free(msg);
@@ -469,7 +474,7 @@ int ctrl_handle_msg(struct ctrl_handle *ctrl, struct ctrl_connection *ccon, stru
send_reply:
/* There is a reply or error that should be reported back to the sender. */
- ctrl_cmd_send(&ccon->write_queue, cmd);
+ ctrl_cmd_send2(ccon, cmd);
just_free:
talloc_free(cmd);
return 0;
@@ -489,8 +494,14 @@ static int control_write_cb(struct osmo_fd *bfd, struct msgb *msg)
control_close_conn(ccon);
return -EBADF;
}
- if (rc != msg->len)
+ if (rc < 0) {
LOGP(DLCTRL, LOGL_ERROR, "Failed to write message to the CTRL connection.\n");
+ return 0;
+ }
+ if (rc < msg->len) {
+ msgb_pull(msg, rc);
+ return -EAGAIN;
+ }
return 0;
}
@@ -589,12 +600,12 @@ static uint64_t get_rate_ctr_value(const struct rate_ctr *ctr, int intv, const c
}
}
-static int get_rate_ctr_group_idx(const struct rate_ctr_group *ctrg, int intv, struct ctrl_cmd *cmd)
+static int get_rate_ctr_group_idx(struct rate_ctr_group *ctrg, int intv, struct ctrl_cmd *cmd)
{
unsigned int i;
for (i = 0; i < ctrg->desc->num_ctr; i++) {
ctrl_cmd_reply_printf(cmd, "%s %"PRIu64";", ctrg->desc->ctr_desc[i].name,
- get_rate_ctr_value(&ctrg->ctr[i], intv, ctrg->desc->group_name_prefix));
+ get_rate_ctr_value(rate_ctr_group_get_ctr(ctrg, i), intv, ctrg->desc->group_name_prefix));
if (!cmd->reply) {
cmd->reply = "OOM";
return CTRL_CMD_ERROR;
@@ -723,6 +734,100 @@ static int verify_rate_ctr(struct ctrl_cmd *cmd, const char *value, void *data)
return 0;
}
+/* Expose all stat_item groups on CTRL, as read-only variables of the form:
+ * stat_item.(last|...).group_name.N.item_name
+ * stat_item.(last|...).group_name.by_name.group_idx_name.item_name
+ */
+CTRL_CMD_DEFINE_RO(stat_item, "stat_item *");
+static int get_stat_item(struct ctrl_cmd *cmd, void *data)
+{
+ char *dup;
+ char *saveptr;
+ char *value_type;
+ char *group_name;
+ char *group_idx_str;
+ char *item_name;
+ char *tmp;
+ int32_t val;
+ struct osmo_stat_item_group *statg;
+ const struct osmo_stat_item *stat_item;
+
+ /* cmd will be freed in control_if.c after handling here, so no need to free the dup string. */
+ dup = talloc_strdup(cmd, cmd->variable);
+ if (!dup) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ /* Split off the first "stat_item." part */
+ tmp = strtok_r(dup, ".", &saveptr);
+ if (!tmp || strcmp(tmp, "stat_item") != 0)
+ goto format_error;
+
+ /* Split off the "last." part (validated further below) */
+ value_type = strtok_r(NULL, ".", &saveptr);
+ if (!value_type)
+ goto format_error;
+
+ /* Split off the "group_name." part */
+ group_name = strtok_r(NULL, ".", &saveptr);
+ if (!group_name)
+ goto format_error;
+
+ /* Split off the "N." part */
+ group_idx_str = strtok_r(NULL, ".", &saveptr);
+ if (!group_idx_str)
+ goto format_error;
+ if (strcmp(group_idx_str, "by_name") == 0) {
+ /* The index is not given by "N" but by "by_name.foo". Get the "foo" idx-name */
+ group_idx_str = strtok_r(NULL, ".", &saveptr);
+ statg = osmo_stat_item_get_group_by_name_idxname(group_name, group_idx_str);
+ } else {
+ int idx;
+ if (osmo_str_to_int(&idx, group_idx_str, 10, 0, INT_MAX))
+ goto format_error;
+ statg = osmo_stat_item_get_group_by_name_idx(group_name, idx);
+ }
+ if (!statg) {
+ cmd->reply = "Stat group with given name and index not found";
+ return CTRL_CMD_ERROR;
+ }
+
+ /* Split off the "item_name" part */
+ item_name = strtok_r(NULL, ".", &saveptr);
+ if (!item_name)
+ goto format_error;
+ stat_item = osmo_stat_item_get_by_name(statg, item_name);
+ if (!stat_item) {
+ cmd->reply = "No such stat item found";
+ return CTRL_CMD_ERROR;
+ }
+
+ tmp = strtok_r(NULL, "", &saveptr);
+ if (tmp) {
+ cmd->reply = "Garbage after stat item name";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (!strcmp(value_type, "last"))
+ val = osmo_stat_item_get_last(stat_item);
+ else
+ goto format_error;
+
+ cmd->reply = talloc_asprintf(cmd, "%"PRIu32, val);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+
+format_error:
+ cmd->reply = "Stat item must be of form stat_item.type.group_name.N.item_name,"
+ " e.g. 'stat_item.last.bsc.0.msc_num:connected'";
+ return CTRL_CMD_ERROR;
+}
+
/* counter */
CTRL_CMD_DEFINE(counter, "counter *");
static int get_counter(struct ctrl_cmd *cmd, void *data)
@@ -784,7 +889,7 @@ static int verify_counter(struct ctrl_cmd *cmd, const char *value, void *data)
struct ctrl_handle *ctrl_interface_setup(void *data, uint16_t port,
ctrl_cmd_lookup lookup)
{
- return ctrl_interface_setup_dynip(data, "127.0.0.1", port, lookup);
+ return ctrl_interface_setup2(data, port, lookup, 0);
}
static int ctrl_initialized = 0;
@@ -810,6 +915,9 @@ static int ctrl_init(unsigned int node_count)
ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rate_ctr);
if (ret)
goto err_vec;
+ ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_stat_item);
+ if (ret)
+ goto err_vec;
ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_counter);
if (ret)
goto err_vec;
@@ -933,12 +1041,24 @@ struct ctrl_handle *ctrl_interface_setup_dynip(void *data,
return ctrl_interface_setup_dynip2(data, bind_addr, port, lookup, 0);
}
+/*! Initializes CTRL interface using the configured bind addr/port.
+ * \param[in] data Pointer which will be made available to each set_..() get_..() verify_..() control command function
+ * \param[in] default_port TCP port number to bind to if not explicitly configured
+ * \param[in] lookup Lookup function pointer, can be NULL
+ * \param[in] node_count Number of CTRL nodes to allocate, 0 for default.
+ */
+struct ctrl_handle *ctrl_interface_setup2(void *data, uint16_t default_port, ctrl_cmd_lookup lookup,
+ unsigned int node_count)
+{
+ return ctrl_interface_setup_dynip2(data, ctrl_vty_get_bind_addr(), ctrl_vty_get_bind_port(default_port), lookup,
+ node_count);
+}
/*! Install a lookup helper function for control nodes
* This function is used by e.g. library code to install lookup helpers
* for additional nodes in the control interface.
* \param[in] lookup The lookup helper function
- * \retuns - on success; negative on error.
+ * \returns - on success; negative on error.
*/
int ctrl_lookup_register(ctrl_cmd_lookup lookup)
{
diff --git a/src/ctrl/control_vty.c b/src/ctrl/control_vty.c
index e141a4c8..a7ebddc2 100644
--- a/src/ctrl/control_vty.c
+++ b/src/ctrl/control_vty.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdlib.h>
@@ -30,16 +26,20 @@
static void *ctrl_vty_ctx = NULL;
static const char *ctrl_vty_bind_addr = NULL;
+/* Port the CTRL should bind to: -1 means not configured */
+static int ctrl_bind_port = -1;
DEFUN(cfg_ctrl_bind_addr,
cfg_ctrl_bind_addr_cmd,
- "bind A.B.C.D",
+ "bind A.B.C.D [<0-65535>]",
"Set bind address to listen for Control connections\n"
- "Local IP address (default 127.0.0.1)\n")
+ "Local IP address (default 127.0.0.1)\n"
+ "Local TCP port number\n")
{
talloc_free((char*)ctrl_vty_bind_addr);
ctrl_vty_bind_addr = NULL;
ctrl_vty_bind_addr = talloc_strdup(ctrl_vty_ctx, argv[0]);
+ ctrl_bind_port = argc > 1 ? atoi(argv[1]) : -1;
return CMD_SUCCESS;
}
@@ -50,6 +50,11 @@ const char *ctrl_vty_get_bind_addr(void)
return ctrl_vty_bind_addr;
}
+uint16_t ctrl_vty_get_bind_port(uint16_t default_port)
+{
+ return ctrl_bind_port >= 0 ? ctrl_bind_port : default_port;
+}
+
static struct cmd_node ctrl_node = {
L_CTRL_NODE,
"%s(config-ctrl)# ",
diff --git a/src/ctrl/libosmoctrl.map b/src/ctrl/libosmoctrl.map
index f995467b..3418e620 100644
--- a/src/ctrl/libosmoctrl.map
+++ b/src/ctrl/libosmoctrl.map
@@ -15,6 +15,7 @@ ctrl_cmd_parse;
ctrl_cmd_parse2;
ctrl_cmd_parse3;
ctrl_cmd_send;
+ctrl_cmd_send2;
ctrl_cmd_send_to_all;
ctrl_cmd_send_trap;
ctrl_cmd_trap;
@@ -22,12 +23,14 @@ ctrl_handle_alloc; /* could be removed? */
ctrl_handle_alloc2; /* could be removed? */
ctrl_handle_msg; /* only used in unit test */
ctrl_interface_setup;
+ctrl_interface_setup2;
ctrl_interface_setup_dynip;
ctrl_interface_setup_dynip2;
ctrl_lookup_register;
ctrl_parse_get_num;
ctrl_type_vals;
ctrl_vty_get_bind_addr;
+ctrl_vty_get_bind_port;
ctrl_vty_init;
osmo_ctrl_conn_alloc;
diff --git a/src/gb/Makefile.am b/src/gb/Makefile.am
index 2ccb7576..2f7ad5eb 100644
--- a/src/gb/Makefile.am
+++ b/src/gb/Makefile.am
@@ -1,12 +1,11 @@
# 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=12:0:0
+LIBVERSION=16:0:2
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
-AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN} -fno-strict-aliasing \
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -Wall -fno-strict-aliasing \
$(TALLOC_CFLAGS) \
- $(LIBMNL_CFLAGS) \
$(NULL)
# FIXME: this should eventually go into a milenage/Makefile.am
@@ -15,9 +14,13 @@ noinst_HEADERS = common_vty.h gb_internal.h gprs_bssgp_internal.h gprs_ns2_inter
if ENABLE_GB
lib_LTLIBRARIES = libosmogb.la
-libosmogb_la_LDFLAGS = $(LTLDFLAGS_OSMOGB) -version-info $(LIBVERSION)
+libosmogb_la_LDFLAGS = \
+ $(LTLDFLAGS_OSMOGB) \
+ -version-info $(LIBVERSION) \
+ -no-undefined \
+ $(NULL)
libosmogb_la_LIBADD = $(TALLOC_LIBS) \
- $(top_builddir)/src/libosmocore.la \
+ $(top_builddir)/src/core/libosmocore.la \
$(top_builddir)/src/vty/libosmovty.la \
$(top_builddir)/src/gsm/libosmogsm.la
@@ -37,3 +40,4 @@ libosmogb_test_la_SOURCES= $(libosmogb_la_SOURCES)
endif
EXTRA_DIST = libosmogb.map
+EXTRA_libosmogb_la_DEPENDENCIES = libosmogb.map
diff --git a/src/gb/bssgp_bvc_fsm.c b/src/gb/bssgp_bvc_fsm.c
index ce9079d5..3a36c7dc 100644
--- a/src/gb/bssgp_bvc_fsm.c
+++ b/src/gb/bssgp_bvc_fsm.c
@@ -375,6 +375,7 @@ static void bssgp_bvc_fsm_wait_reset_ack(struct osmo_fsm_inst *fi, uint32_t even
struct bvc_fsm_priv *bfp = fi->priv;
const struct tlv_parsed *tp = NULL;
struct msgb *rx = NULL, *tx;
+ uint8_t cause;
switch (event) {
case BSSGP_BVCFSM_E_RX_RESET:
@@ -385,6 +386,7 @@ static void bssgp_bvc_fsm_wait_reset_ack(struct osmo_fsm_inst *fi, uint32_t even
/* fall-through */
case BSSGP_BVCFSM_E_RX_RESET_ACK:
rx = data;
+ cause = bfp->last_reset_cause;
tp = (const struct tlv_parsed *) msgb_bcid(rx);
if (bfp->bvci == 0)
update_negotiated_features(fi, tp);
@@ -398,6 +400,8 @@ static void bssgp_bvc_fsm_wait_reset_ack(struct osmo_fsm_inst *fi, uint32_t even
osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, T1_SECS, T1);
} else
osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, 0, 0);
+ if (bfp->ops && bfp->ops->reset_ack_notification)
+ bfp->ops->reset_ack_notification(bfp->nsei, bfp->bvci, &bfp->ra_id, bfp->cell_id, cause, bfp->ops_priv);
break;
}
}
@@ -415,8 +419,8 @@ static void bssgp_bvc_fsm_unblocked(struct osmo_fsm_inst *fi, uint32_t event, vo
rx = data;
tp = (const struct tlv_parsed *) msgb_bcid(rx);
/* If BVC-UNBLOCK-ACK PDU is received by an BSS for the signalling BVC, the PDU is ignored. */
- LOGPFSML(fi, LOGL_ERROR, "Rx BVC-UNBLOCK-ACK on BVCI=0 is illegal\n");
if (bfp->bvci == 0) {
+ LOGPFSML(fi, LOGL_ERROR, "Rx BVC-UNBLOCK-ACK on BVCI=0 is illegal\n");
if (!bfp->role_sgsn)
break;
_tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
diff --git a/src/gb/frame_relay.c b/src/gb/frame_relay.c
index 4d1df672..e973b915 100644
--- a/src/gb/frame_relay.c
+++ b/src/gb/frame_relay.c
@@ -138,7 +138,7 @@ struct q933_a_pvc_sts {
ext2:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t ext0:1, spare:1, dlci_msb:6;
uint8_t ext1:1, dlci_lsb:4, space1:3;
uint8_t ext2:1, spare2:3, new:1, delete:1, active:1, reserved:1;
@@ -652,7 +652,7 @@ static int rx_lmi_q933_status(struct msgb *msg, struct tlv_parsed *tp)
/* check for mandatory IEs */
if (!TLVP_PRES_LEN(tp, Q933_IEI_REPORT_TYPE, 1)) {
- LOGPFRL(link, LOGL_NOTICE, "Rx STATUSL: Missing TLV Q933 Report Type\n");
+ LOGPFRL(link, LOGL_NOTICE, "Rx STATUS: Missing TLV Q933 Report Type\n");
return -1;
}
diff --git a/src/gb/gprs_bssgp.c b/src/gb/gprs_bssgp.c
index 207c9a81..7abef804 100644
--- a/src/gb/gprs_bssgp.c
+++ b/src/gb/gprs_bssgp.c
@@ -77,6 +77,7 @@ static int _bssgp_tx_dl_ud(struct bssgp_flow_control *fc, struct msgb *msg,
/* callback to be backward compatible with old users which do not set the bssgp_ns_send function */
static int _gprs_ns_sendmsg(void *ctx, struct msgb *msg)
{
+ OSMO_ASSERT(bssgp_nsi);
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
@@ -441,7 +442,7 @@ static int bssgp_rx_bvc_block(struct msgb *msg, struct tlv_parsed *tp)
return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
ptp_ctx->state |= BVC_S_BLOCKED;
- rate_ctr_inc(&ptp_ctx->ctrg->ctr[BSSGP_CTR_BLOCKED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ptp_ctx->ctrg, BSSGP_CTR_BLOCKED));
/* Send NM_BVC_BLOCK.ind to NM */
memset(&nmp, 0, sizeof(nmp));
@@ -634,7 +635,7 @@ static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp,
DEBUGP(DLBSSGP, "BSSGP BVCI=%u TLLI=%08x Rx LLC DISCARDED\n",
ctx->bvci, tlli);
- rate_ctr_inc(&ctx->ctrg->ctr[BSSGP_CTR_DISCARDED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctx->ctrg, BSSGP_CTR_DISCARDED));
/* send NM_LLC_DISCARDED to NM */
memset(&nmp, 0, sizeof(nmp));
@@ -675,7 +676,7 @@ int bssgp_rx_status(struct msgb *msg, struct tlv_parsed *tp,
}
if (bctx)
- rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_STATUS]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_STATUS));
/* send NM_STATUS to NM */
memset(&nmp, 0, sizeof(nmp));
@@ -1194,8 +1195,8 @@ int bssgp_rcvmsg(struct msgb *msg)
if (bctx) {
log_set_context(LOG_CTX_GB_BVC, bctx);
- rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
- rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN],
+ rate_ctr_inc(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_PKTS_IN));
+ rate_ctr_add(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_BYTES_IN),
msgb_bssgp_len(msg));
}
@@ -1319,8 +1320,8 @@ int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime,
budh->tlli = osmo_htonl(msgb_tlli(msg));
budh->pdu_type = BSSGP_PDUT_DL_UNITDATA;
- rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]);
- rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_PKTS_OUT));
+ rate_ctr_add(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_BYTES_OUT), msg->len);
/* Identifiers down: BVCI, NSEI (in msgb->cb) */
@@ -1427,7 +1428,7 @@ void bssgp_fc_flush_queue(struct bssgp_flow_control *fc)
/*!
* \brief Flush the queues of all BSSGP contexts.
*/
-void bssgp_flush_all_queues()
+void bssgp_flush_all_queues(void)
{
struct bssgp_bvc_ctx *bctx;
diff --git a/src/gb/gprs_bssgp2.c b/src/gb/gprs_bssgp2.c
index 8e2ba66c..104fe08e 100644
--- a/src/gb/gprs_bssgp2.c
+++ b/src/gb/gprs_bssgp2.c
@@ -349,6 +349,35 @@ struct msgb *bssgp2_enc_fc_bvc(const struct bssgp2_flow_ctrl *fc, enum bssgp_fc_
return msg;
}
+/*! Encode BSSGP FLUSH-LL PDU as per TS 48.018 Section 10.4.1.
+ * \param[in] tlli - the TLLI of the MS
+ * \param[in] old_bvci BVCI
+ * \param[in] new_bvci2 optional BVCI - only encoded if non-NULL
+ * \param[in] nsei optional - only encoded if non-NULL
+ * \returns encoded PDU or NULL in case of error */
+struct msgb *bssgp2_enc_flush_ll(uint32_t tlli, uint16_t old_bvci,
+ const uint16_t *new_bvci, const uint16_t *nsei)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+
+ if (!msg)
+ return NULL;
+
+ bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ bgph->pdu_type = BSSGP_PDUT_FLUSH_LL;
+
+ msgb_tvlv_put_32be(msg, BSSGP_IE_TLLI, tlli);
+ msgb_tvlv_put_16be(msg, BSSGP_IE_BVCI, old_bvci);
+ if (new_bvci)
+ msgb_tvlv_put_16be(msg, BSSGP_IE_BVCI, *new_bvci);
+
+ if (nsei)
+ msgb_tvlv_put_16be(msg, BSSGP_IE_BVCI, *nsei);
+
+ return msg;
+}
+
/*! Encode a FLOW-CONTROL-BVC-ACK PDU as per TS 48.018 Section 10.4.4.
* \param[in] tag the tag IE value to encode
* \returns encoded PDU or NULL in case of error */
diff --git a/src/gb/gprs_bssgp_bss.c b/src/gb/gprs_bssgp_bss.c
index a066e91c..8230d871 100644
--- a/src/gb/gprs_bssgp_bss.c
+++ b/src/gb/gprs_bssgp_bss.c
@@ -466,8 +466,8 @@ int bssgp_tx_ul_ud(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
msgb_nsei(msg) = bctx->nsei;
msgb_bvci(msg) = bctx->bvci;
- rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]);
- rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_PKTS_OUT));
+ rate_ctr_add(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_BYTES_OUT), msg->len);
return bssgp_ns_send(bssgp_ns_send_data, msg);
}
diff --git a/src/gb/gprs_bssgp_rim.c b/src/gb/gprs_bssgp_rim.c
index 63b303e5..9c09e1ef 100644
--- a/src/gb/gprs_bssgp_rim.c
+++ b/src/gb/gprs_bssgp_rim.c
@@ -49,12 +49,13 @@ const struct value_string bssgp_rim_routing_info_discr_strs[] = {
{ 0, NULL }
};
-/*! Parse a RIM Routing information IE (3GPP TS 48.018, chapter 11.3.70).
+/*! Parse a RIM Routing address IE (3GPP TS 29.060, chapter 7.7.57 and 7.7.77).
* \param[out] ri user provided memory to store the parsed results.
- * \param[in] buf input buffer of the value part of the IE.
+ * \param[in] buf input buffer of the value part of the RIM Routing address IE.
+ * \discr[in] discr value part (one byte) of the RIM Routing Address Discriminator IE.
* \returns length of parsed octets, -EINVAL on error. */
-int bssgp_parse_rim_ri(struct bssgp_rim_routing_info *ri, const uint8_t *buf,
- unsigned int len)
+int bssgp_parse_rim_ra(struct bssgp_rim_routing_info *ri, const uint8_t *buf,
+ unsigned int len, uint8_t discr)
{
struct gprs_ra_id raid_temp;
@@ -62,23 +63,22 @@ int bssgp_parse_rim_ri(struct bssgp_rim_routing_info *ri, const uint8_t *buf,
if (len < 2)
return -EINVAL;
- ri->discr = buf[0] & 0x0f;
- buf++;
+ ri->discr = discr;
switch (ri->discr) {
case BSSGP_RIM_ROUTING_INFO_GERAN:
- if (len < 9)
+ if (len < 8)
return -EINVAL;
ri->geran.cid = bssgp_parse_cell_id(&ri->geran.raid, buf);
- return 9;
+ return 8;
case BSSGP_RIM_ROUTING_INFO_UTRAN:
- if (len < 9)
+ if (len < 8)
return -EINVAL;
gsm48_parse_ra(&ri->utran.raid, buf);
ri->utran.rncid = osmo_load16be(buf + 6);
- return 9;
+ return 8;
case BSSGP_RIM_ROUTING_INFO_EUTRAN:
- if (len < 7 || len > 14)
+ if (len < 6 || len > 13)
return -EINVAL;
/* Note: 3GPP TS 24.301 Figure 9.9.3.32.1 and 3GPP TS 24.008
* Figure 10.5.130 specify MCC/MNC encoding in the same way,
@@ -88,14 +88,35 @@ int bssgp_parse_rim_ri(struct bssgp_rim_routing_info *ri, const uint8_t *buf,
ri->eutran.tai.mnc = raid_temp.mnc;
ri->eutran.tai.mnc_3_digits = raid_temp.mnc_3_digits;
ri->eutran.tai.tac = osmo_load16be(buf + 3);
- memcpy(ri->eutran.global_enb_id, buf + 5, len - 6);
- ri->eutran.global_enb_id_len = len - 6;
+ memcpy(ri->eutran.global_enb_id, buf + 5, len - 5);
+ ri->eutran.global_enb_id_len = len - 5;
return len;
default:
return -EINVAL;
}
}
+/*! Parse a RIM Routing information IE (3GPP TS 48.018, chapter 11.3.70).
+ * \param[out] ri user provided memory to store the parsed results.
+ * \param[in] buf input buffer of the value part of the IE.
+ * \returns length of parsed octets, -EINVAL on error. */
+int bssgp_parse_rim_ri(struct bssgp_rim_routing_info *ri, const uint8_t *buf,
+ unsigned int len)
+{
+ uint8_t discr;
+ int rc;
+
+ if (len < 1)
+ return -EINVAL;
+
+ discr = buf[0] & 0x0f;
+
+ rc = bssgp_parse_rim_ra(ri, buf + 1, len - 1, discr);
+ if (rc < 0)
+ return rc;
+ return rc + 1;
+}
+
/*! Encode a RIM Routing information IE (3GPP TS 48.018, chapter 11.3.70).
* \param[out] buf user provided memory (at least 14 byte) for the generated value part of the IE.
* \param[in] ri user provided input data struct.
@@ -1064,7 +1085,6 @@ struct msgb *bssgp_encode_rim_pdu(const struct bssgp_ran_information_pdu *pdu)
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph;
uint8_t rim_ri_buf[BSSGP_RIM_ROUTING_INFO_MAXLEN];
- uint8_t *rim_cont_buf;
int rc;
if (!msg)
@@ -1105,7 +1125,7 @@ struct msgb *bssgp_encode_rim_pdu(const struct bssgp_ran_information_pdu *pdu)
/* Put RIM container */
if (pdu->decoded_present) {
- rim_cont_buf = talloc_zero_size(msg, msg->data_len);
+ uint8_t *rim_cont_buf = talloc_zero_size(msg, msg->data_len);
if (!rim_cont_buf)
goto error;
@@ -1130,8 +1150,10 @@ struct msgb *bssgp_encode_rim_pdu(const struct bssgp_ran_information_pdu *pdu)
/* The API user must set the iei properly! */
OSMO_ASSERT(false);
}
- if (rc < 0)
+ if (rc < 0) {
+ talloc_free(rim_cont_buf);
goto error;
+ }
msgb_tvlv_put(msg, pdu->rim_cont_iei, rc, rim_cont_buf);
talloc_free(rim_cont_buf);
@@ -1143,7 +1165,6 @@ struct msgb *bssgp_encode_rim_pdu(const struct bssgp_ran_information_pdu *pdu)
return msg;
error:
- talloc_free(rim_cont_buf);
msgb_free(msg);
return 0;
}
@@ -1171,14 +1192,33 @@ int bssgp_tx_rim(const struct bssgp_ran_information_pdu *pdu, uint16_t nsei)
msgb_bvci(msg) = 0; /* Signalling */
bgph = (struct bssgp_normal_hdr *)msgb_bssgph(msg);
- DEBUGP(DLBSSGP, "BSSGP BVCI=0 Tx RIM-PDU:%s, src=%s, dest=%s\n",
- bssgp_pdu_str(bgph->pdu_type),
+ DEBUGP(DLBSSGP, "BSSGP BVCI=0 NSEI=%u Tx RIM-PDU:%s, src=%s, dest=%s\n",
+ nsei, bssgp_pdu_str(bgph->pdu_type),
bssgp_rim_ri_name_buf(ri_src_str, sizeof(ri_src_str), &pdu->routing_info_src),
bssgp_rim_ri_name_buf(ri_dest_str, sizeof(ri_dest_str), &pdu->routing_info_dest));
return bssgp_ns_send(bssgp_ns_send_data, msg);
}
+/*! Send encoded RAN TRANSPARENT CONTAINER via BSSGP (3GPP TS 29.060, section 7.7.43).
+ * \param[in] msg user provided memory for the encoded RAN TRANSPARENT CONTAINER to be sent.
+ * (this function will take ownership of msg).
+ * \param[in] nsei BSSGP network service entity identifier (NSEI).
+ * \returns 0 on sccess, -EINVAL on error. */
+int bssgp_tx_rim_encoded(struct msgb *msg, uint16_t nsei)
+{
+ struct bssgp_normal_hdr *bgph;
+
+ msgb_nsei(msg) = nsei;
+ msgb_bvci(msg) = 0; /* Signalling */
+
+ bgph = (struct bssgp_normal_hdr *)msgb_bssgph(msg);
+ DEBUGP(DLBSSGP, "BSSGP BVCI=0 NSEI=%u Tx RIM-PDU:%s\n",
+ nsei, bssgp_pdu_str(bgph->pdu_type));
+
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
+}
+
/* For internal use only (called from gprs_bssgp.c) */
int bssgp_rx_rim(struct msgb *msg, struct tlv_parsed *tp, uint16_t bvci)
{
diff --git a/src/gb/gprs_ns.c b/src/gb/gprs_ns.c
index f486333a..304fe7c1 100644
--- a/src/gb/gprs_ns.c
+++ b/src/gb/gprs_ns.c
@@ -29,7 +29,7 @@
* @{
*
* GPRS Networks Service (NS) messages on the Gb interface
- * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
*
* Some introduction into NS: NS is used typically on top of frame relay,
* but in the ip.access world it is encapsulated in UDP packets. It serves
@@ -352,8 +352,7 @@ struct gprs_nsvc *gprs_nsvc_create2(struct gprs_ns_inst *nsi, uint16_t nsvci,
*/
void gprs_nsvc_delete(struct gprs_nsvc *nsvc)
{
- if (osmo_timer_pending(&nsvc->timer))
- osmo_timer_del(&nsvc->timer);
+ osmo_timer_del(&nsvc->timer);
llist_del(&nsvc->list);
rate_ctr_group_free(nsvc->ctrg);
osmo_stat_item_group_free(nsvc->statg);
@@ -486,8 +485,8 @@ static int gprs_ns_tx(struct gprs_nsvc *nsvc, struct msgb *msg)
}
/* Increment number of Uplink bytes */
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_OUT]);
- rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_OUT], msgb_l2len(msg));
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_PKTS_OUT));
+ rate_ctr_add(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_BYTES_OUT), msgb_l2len(msg));
switch (nsvc->ll) {
case GPRS_NS_LL_UDP:
@@ -533,7 +532,7 @@ static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, uint8_t pdu_type)
/*! Transmit a NS-RESET on a given NSVC
* \param[in] nsvc NS-VC used for transmission
- * \paam[in] cause Numeric NS cause value
+ * \param[in] cause Numeric NS cause value
*/
int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause)
{
@@ -643,7 +642,7 @@ int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause)
/* be conservative and mark it as blocked even now! */
ns_mark_blocked(nsvc);
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_BLOCKED));
msg->l2h = msgb_put(msg, sizeof(*nsh));
nsh = (struct gprs_ns_hdr *) msg->l2h;
@@ -750,8 +749,7 @@ static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode)
nsvc->nsei, get_value_string(timer_mode_strs, mode),
seconds);
- if (osmo_timer_pending(&nsvc->timer))
- osmo_timer_del(&nsvc->timer);
+ osmo_timer_del(&nsvc->timer);
osmo_gettimeofday(&nsvc->timer_started, NULL);
nsvc->timer_mode = mode;
@@ -781,15 +779,15 @@ static void gprs_ns_timer_cb(void *data)
switch (nsvc->timer_mode) {
case NSVC_TIMER_TNS_ALIVE:
/* Tns-alive case: we expired without response ! */
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_LOST_ALIVE]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_LOST_ALIVE));
nsvc->alive_retries++;
if (nsvc->alive_retries >
nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) {
/* mark as dead (and blocked unless IP-SNS) */
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_DEAD]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_DEAD));
if (!nsvc->nsi->bss_sns_fi && nsvc->nsi->nsip.use_reset_block_unblock) {
ns_set_state(nsvc, NSE_S_BLOCKED);
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_BLOCKED));
} else
ns_set_state(nsvc, 0);
LOGP(DNS, LOGL_NOTICE,
@@ -816,7 +814,7 @@ static void gprs_ns_timer_cb(void *data)
nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE);
break;
case NSVC_TIMER_TNS_RESET:
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_LOST_RESET]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_LOST_RESET));
if (!(nsvc->state & NSE_S_RESET))
LOGP(DNS, LOGL_NOTICE,
"NSEI=%u Reset timed out but RESET flag is not set\n",
@@ -1076,7 +1074,7 @@ int gprs_ns_tx_sns_size_ack(struct gprs_nsvc *nsvc, uint8_t *cause)
* if the NS-VC is ALIVE and not BLOCKED. After that, it adds a NS
* header for the NS-UNITDATA message type and sends it off.
*
- * Section 9.2.10: transmit side / NS-UNITDATA-REQUEST primitive
+ * Section 9.2.10: transmit side / NS-UNITDATA-REQUEST primitive
*/
int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg)
{
@@ -1251,7 +1249,7 @@ static int gprs_ns_rx_reset(struct gprs_nsvc **nsvc, struct msgb *msg)
ns_osmo_signal_dispatch_mismatch(*nsvc, msg,
NS_PDUT_RESET,
NS_IE_VCI);
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_VCI]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_INV_VCI));
gprs_ns_tx_reset_ack(*nsvc);
return 0;
}
@@ -1277,14 +1275,14 @@ static int gprs_ns_rx_reset(struct gprs_nsvc **nsvc, struct msgb *msg)
ns_osmo_signal_dispatch_mismatch(*nsvc, msg,
NS_PDUT_RESET,
NS_IE_NSEI);
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_NSEI]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_INV_NSEI));
rc = gprs_ns_tx_reset_ack(*nsvc);
CHECK_TX_RC(rc, *nsvc);
return 0;
}
/* NSEI has changed */
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_NSEI_CHG]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_NSEI_CHG));
(*nsvc)->nsei = nsei;
}
@@ -1292,7 +1290,7 @@ static int gprs_ns_rx_reset(struct gprs_nsvc **nsvc, struct msgb *msg)
ns_set_state(*nsvc, NSE_S_BLOCKED | NSE_S_ALIVE);
if (orig_nsvc) {
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_REPLACED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_REPLACED));
ns_osmo_signal_dispatch_replaced(*nsvc, orig_nsvc);
/* Update the ll info fields */
@@ -1390,7 +1388,7 @@ static int gprs_ns_rx_reset_ack(struct gprs_nsvc **nsvc, struct msgb *msg)
ns_osmo_signal_dispatch_mismatch(*nsvc, msg,
NS_PDUT_RESET_ACK,
NS_IE_VCI);
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_VCI]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_INV_VCI));
LOGP(DNS, LOGL_ERROR,
"NS RESET ACK Unknown NS-VCI %d (%s NSEI=%d) "
"from %s\n",
@@ -1401,7 +1399,7 @@ static int gprs_ns_rx_reset_ack(struct gprs_nsvc **nsvc, struct msgb *msg)
}
/* Notify others */
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_REPLACED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_REPLACED));
ns_osmo_signal_dispatch_replaced(*nsvc, orig_nsvc);
/* Update the ll info fields */
@@ -1415,7 +1413,7 @@ static int gprs_ns_rx_reset_ack(struct gprs_nsvc **nsvc, struct msgb *msg)
ns_osmo_signal_dispatch_mismatch(*nsvc, msg,
NS_PDUT_RESET_ACK,
NS_IE_NSEI);
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_NSEI]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_INV_NSEI));
LOGP(DNS, LOGL_ERROR,
"NS RESET ACK Unknown NSEI %d (NS-VCI=%u) from %s\n",
nsei, nsvci, gprs_ns_ll_str(*nsvc));
@@ -1423,14 +1421,14 @@ static int gprs_ns_rx_reset_ack(struct gprs_nsvc **nsvc, struct msgb *msg)
}
/* NSEI has changed */
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_NSEI_CHG]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_NSEI_CHG));
(*nsvc)->nsei = nsei;
}
/* Mark NS-VC as blocked and alive */
ns_set_state(*nsvc, NSE_S_BLOCKED | NSE_S_ALIVE);
ns_set_remote_state(*nsvc, NSE_S_BLOCKED | NSE_S_ALIVE);
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_BLOCKED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_BLOCKED));
if ((*nsvc)->persistent || (*nsvc)->remote_end_is_sgsn) {
/* stop RESET timer */
osmo_timer_del(&(*nsvc)->timer);
@@ -1471,7 +1469,7 @@ static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg)
//nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI);
ns_osmo_signal_dispatch(nsvc, S_NS_BLOCK, *cause);
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_BLOCKED));
return gprs_ns_tx_block_ack(nsvc);
}
@@ -1705,7 +1703,7 @@ int gprs_ns_vc_create(struct gprs_ns_inst *nsi, struct msgb *msg,
existing_nsvc->nsei = nsei;
/* Do statistics */
- rate_ctr_inc(&existing_nsvc->ctrg->ctr[NS_CTR_NSEI_CHG]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(existing_nsvc->ctrg, NS_CTR_NSEI_CHG));
}
*new_nsvc = existing_nsvc;
@@ -1734,8 +1732,8 @@ int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg,
log_set_context(LOG_CTX_GB_NSVC, *nsvc);
/* Increment number of Incoming bytes */
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_PKTS_IN]);
- rate_ctr_add(&(*nsvc)->ctrg->ctr[NS_CTR_BYTES_IN], msgb_l2len(msg));
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_PKTS_IN));
+ rate_ctr_add(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_BYTES_IN), msgb_l2len(msg));
if (nsvc_is_not_used(*nsvc) && !ns_is_sns(nsh->pdu_type) && nsh->pdu_type != NS_PDUT_STATUS) {
LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx %s on unused/pre-configured endpoint, discarding\n",
@@ -1762,7 +1760,7 @@ int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg,
case NS_PDUT_ALIVE_ACK:
ns_mark_alive(*nsvc);
if ((*nsvc)->timer_mode == NSVC_TIMER_TNS_ALIVE)
- osmo_stat_item_set((*nsvc)->statg->items[NS_STAT_ALIVE_DELAY],
+ osmo_stat_item_set(osmo_stat_item_group_get_item((*nsvc)->statg, NS_STAT_ALIVE_DELAY),
nsvc_timer_elapsed_ms(*nsvc));
/* stop Tns-alive and start Tns-test */
nsvc_start_timer(*nsvc, NSVC_TIMER_TNS_TEST);
diff --git a/src/gb/gprs_ns2.c b/src/gb/gprs_ns2.c
index 1148d6f5..4e496c1f 100644
--- a/src/gb/gprs_ns2.c
+++ b/src/gb/gprs_ns2.c
@@ -159,7 +159,7 @@ const struct value_string gprs_ns2_cause_strs[] = {
{ 0, NULL }
};
-static const struct rate_ctr_desc nsvc_ctr_description[] = {
+static const struct rate_ctr_desc ns_ctr_description[] = {
[NS_CTR_PKTS_IN] = { "packets:in", "Packets at NS Level ( In)" },
[NS_CTR_PKTS_OUT] = { "packets:out", "Packets at NS Level (Out)" },
[NS_CTR_PKTS_OUT_DROP] = { "packets:out:drop", "Dropped Packets (Out)" },
@@ -177,11 +177,19 @@ static const struct rate_ctr_desc nsvc_ctr_description[] = {
[NS_CTR_LOST_RESET] = { "lost:reset", "RESET ACK missing count " },
};
+static const struct rate_ctr_group_desc nse_ctrg_desc = {
+ .group_name_prefix = "ns:nse",
+ .group_description = "NSE Peer Statistics",
+ .num_ctr = ARRAY_SIZE(ns_ctr_description),
+ .ctr_desc = ns_ctr_description,
+ .class_id = OSMO_STATS_CLASS_PEER,
+};
+
static const struct rate_ctr_group_desc nsvc_ctrg_desc = {
.group_name_prefix = "ns:nsvc",
.group_description = "NSVC Peer Statistics",
- .num_ctr = ARRAY_SIZE(nsvc_ctr_description),
- .ctr_desc = nsvc_ctr_description,
+ .num_ctr = ARRAY_SIZE(ns_ctr_description),
+ .ctr_desc = ns_ctr_description,
.class_id = OSMO_STATS_CLASS_PEER,
};
@@ -414,6 +422,8 @@ static struct gprs_ns2_vc *ns2_load_sharing_weight_modulo(
mod = (bvci + load_selector) % nse->sum_data_weight;
llist_for_each_entry(tmp, &nse->nsvc, list) {
+ if (tmp->data_weight == 0)
+ continue;
if (!ns2_vc_is_unblocked(tmp))
continue;
if (i == mod || mod < i + tmp->data_weight)
@@ -560,11 +570,11 @@ void ns2_prim_status_ind(struct gprs_ns2_nse *nse,
nsp.u.status.nsvc = gprs_ns2_ll_str_buf(nsvc_str, sizeof(nsvc_str), nsvc);
LOGNSVC(nsvc, LOGL_NOTICE, "NS-STATUS.ind(bvci=%05u): cause=%s, transfer=%d, first=%d, mtu=%d\n",
nsp.bvci, gprs_ns2_aff_cause_prim_str(nsp.u.status.cause),
- nsp.u.status.transfer, nsp.u.status.first, nse->mtu);
+ nsp.u.status.transfer, nsp.u.status.first, nsp.u.status.mtu);
} else {
LOGNSE(nse, LOGL_NOTICE, "NS-STATUS.ind(bvci=%05u): cause=%s, transfer=%d, first=%d, mtu=%d\n",
nsp.bvci, gprs_ns2_aff_cause_prim_str(nsp.u.status.cause),
- nsp.u.status.transfer, nsp.u.status.first, nse->mtu);
+ nsp.u.status.transfer, nsp.u.status.first, nsp.u.status.mtu);
}
osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_STATUS, PRIM_OP_INDICATION, NULL);
@@ -606,8 +616,12 @@ struct gprs_ns2_vc *ns2_vc_alloc(struct gprs_ns2_vc_bind *bind, struct gprs_ns2_
bind->nsi->nsvc_rate_ctr_idx++;
- llist_add(&nsvc->list, &nse->nsvc);
- llist_add(&nsvc->blist, &bind->nsvc);
+ rate_ctr_group_set_name(nsvc->ctrg, id);
+ osmo_stat_item_group_set_name(nsvc->statg, id);
+
+ llist_add_tail(&nsvc->list, &nse->nsvc);
+ llist_add_tail(&nsvc->blist, &bind->nsvc);
+ osmo_clock_gettime(CLOCK_MONOTONIC, &nsvc->ts_alive_change);
ns2_nse_update_mtu(nse);
return nsvc;
@@ -626,9 +640,9 @@ err:
* \param[in] nsvc NS-VC to destroy */
void gprs_ns2_free_nsvc(struct gprs_ns2_vc *nsvc)
{
- if (!nsvc)
+ if (!nsvc || nsvc->freed)
return;
-
+ nsvc->freed = true;
ns2_prim_status_ind(nsvc->nse, nsvc, 0, GPRS_NS2_AFF_CAUSE_VC_FAILURE);
llist_del(&nsvc->list);
@@ -651,18 +665,29 @@ void gprs_ns2_free_nsvc(struct gprs_ns2_vc *nsvc)
talloc_free(nsvc);
}
+void ns2_free_nsvcs(struct gprs_ns2_nse *nse)
+{
+ struct gprs_ns2_vc *nsvc;
+
+ /* prevent recursive free() when the user reacts on a down event and free() a second time */
+ while (!llist_empty(&nse->nsvc)) {
+ nsvc = llist_first_entry(&nse->nsvc, struct gprs_ns2_vc, list);
+ gprs_ns2_free_nsvc(nsvc);
+ }
+}
+
/*! Destroy/release all NS-VC of given NSE
* \param[in] nse NSE
*/
void gprs_ns2_free_nsvcs(struct gprs_ns2_nse *nse)
{
- struct gprs_ns2_vc *nsvc, *tmp;
-
- if (!nse)
+ if (!nse || nse->freed)
return;
- llist_for_each_entry_safe(nsvc, tmp, &nse->nsvc, list) {
- gprs_ns2_free_nsvc(nsvc);
+ if (nse->bss_sns_fi) {
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_FREE_NSVCS, NULL);
+ } else {
+ ns2_free_nsvcs(nse);
}
}
@@ -801,13 +826,20 @@ struct gprs_ns2_nse *gprs_ns2_create_nse2(struct gprs_ns2_inst *nsi, uint16_t ns
return NULL;
}
+ nse->ctrg = rate_ctr_group_alloc(nse, &nse_ctrg_desc, nsei);
+ if (!nse->ctrg) {
+ talloc_free(nse);
+ return NULL;
+ }
+
nse->ll = linklayer;
nse->nsei = nsei;
nse->nsi = nsi;
nse->first = true;
nse->mtu = 0;
- llist_add(&nse->list, &nsi->nse);
+ llist_add_tail(&nse->list, &nsi->nse);
INIT_LLIST_HEAD(&nse->nsvc);
+ osmo_clock_gettime(CLOCK_MONOTONIC, &nse->ts_alive_change);
return nse;
}
@@ -870,9 +902,10 @@ uint16_t gprs_ns2_nse_nsei(struct gprs_ns2_nse *nse)
* \param[in] nse NS Entity to destroy */
void gprs_ns2_free_nse(struct gprs_ns2_nse *nse)
{
- if (!nse)
+ if (!nse || nse->freed)
return;
+ nse->freed = true;
nse->alive = false;
if (nse->bss_sns_fi) {
osmo_fsm_inst_term(nse->bss_sns_fi, OSMO_FSM_TERM_REQUEST, NULL);
@@ -881,6 +914,8 @@ void gprs_ns2_free_nse(struct gprs_ns2_nse *nse)
gprs_ns2_free_nsvcs(nse);
ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_FAILURE);
+ rate_ctr_group_free(nse->ctrg);
+ ns2_free_nsvcs(nse);
llist_del(&nse->list);
talloc_free(nse);
@@ -888,9 +923,11 @@ void gprs_ns2_free_nse(struct gprs_ns2_nse *nse)
void gprs_ns2_free_nses(struct gprs_ns2_inst *nsi)
{
- struct gprs_ns2_nse *nse, *ntmp;
+ struct gprs_ns2_nse *nse;
- llist_for_each_entry_safe(nse, ntmp, &nsi->nse, list) {
+ /* prevent recursive free() when the user reacts on a down event and free() a second time */
+ while (!llist_empty(&nsi->nse)) {
+ nse = llist_first_entry(&nsi->nse, struct gprs_ns2_nse, list);
gprs_ns2_free_nse(nse);
}
}
@@ -980,7 +1017,8 @@ enum ns2_cs ns2_create_vc(struct gprs_ns2_vc_bind *bind,
enum gprs_ns2_vc_mode vc_mode;
uint16_t nsvci;
uint16_t nsei;
- char idbuf[32];
+ const struct osmo_sockaddr *local;
+ char idbuf[256], tmp[INET6_ADDRSTRLEN + 8];
int rc, tlv;
@@ -1113,8 +1151,12 @@ enum ns2_cs ns2_create_vc(struct gprs_ns2_vc_bind *bind,
nsvci = tlvp_val16be(&tp, NS_IE_VCI);
vc_mode = ns2_dialect_to_vc_mode(dialect);
- snprintf(idbuf, sizeof(idbuf), "%s-NSE%05u-NSVC%05u", gprs_ns2_lltype_str(nse->ll),
- nse->nsei, nsvci);
+
+ local = gprs_ns2_ip_bind_sockaddr(bind);
+ osmo_sockaddr_to_str_buf(tmp, sizeof(tmp), local);
+ snprintf(idbuf, sizeof(idbuf), "%s-NSE%05u-NSVC%05u-%s-%s", gprs_ns2_lltype_str(nse->ll),
+ nse->nsei, nsvci, tmp, osmo_sockaddr_to_str(remote));
+ osmo_identifier_sanitize_buf(idbuf, NULL, '_');
nsvc = ns2_vc_alloc(bind, nse, false, vc_mode, idbuf);
if (!nsvc)
return NS2_CS_SKIPPED;
@@ -1255,8 +1297,8 @@ int ns2_recv_vc(struct gprs_ns2_vc *nsvc,
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_IN]);
- rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_IN], msg->len);
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_IN);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_IN, msg->len);
if (msg->len < sizeof(struct gprs_ns_hdr)) {
rc = -EINVAL;
@@ -1314,7 +1356,7 @@ int ns2_recv_vc(struct gprs_ns2_vc *nsvc,
if (rc < 0) {
LOGP(DLNS, LOGL_NOTICE, "Error during TLV Parse\n");
if (nsh->pdu_type != NS_PDUT_STATUS)
- ns2_tx_status(nsvc, NS_CAUSE_PROTO_ERR_UNSPEC, 0, msg);
+ ns2_tx_status(nsvc, NS_CAUSE_PROTO_ERR_UNSPEC, 0, msg, NULL);
return rc;
}
return ns2_vc_rx(nsvc, msg, &tp);
@@ -1350,16 +1392,22 @@ void ns2_nse_data_sum(struct gprs_ns2_nse *nse)
void ns2_nse_notify_unblocked(struct gprs_ns2_vc *nsvc, bool unblocked)
{
struct gprs_ns2_nse *nse = nsvc->nse;
+ struct gprs_ns2_inst *nsi = nse->nsi;
+ uint16_t nsei = nse->nsei;
- ns2_sns_notify_alive(nse, nsvc, unblocked);
ns2_nse_data_sum(nse);
+ ns2_sns_notify_alive(nse, nsvc, unblocked);
+
+ /* NSE could have been freed, try to get it again */
+ nse = gprs_ns2_nse_by_nsei(nsi, nsei);
- if (unblocked == nse->alive)
+ if (!nse || unblocked == nse->alive)
return;
/* wait until both data_weight and sig_weight are != 0 before declaring NSE as alive */
if (unblocked && nse->sum_data_weight && nse->sum_sig_weight) {
nse->alive = true;
+ osmo_clock_gettime(CLOCK_MONOTONIC, &nse->ts_alive_change);
ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_RECOVERY);
nse->first = false;
return;
@@ -1368,6 +1416,7 @@ void ns2_nse_notify_unblocked(struct gprs_ns2_vc *nsvc, bool unblocked)
if (nse->alive && (nse->sum_data_weight == 0 || nse->sum_sig_weight == 0)) {
/* nse became unavailable */
nse->alive = false;
+ osmo_clock_gettime(CLOCK_MONOTONIC, &nse->ts_alive_change);
ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_FAILURE);
}
}
@@ -1400,6 +1449,9 @@ struct gprs_ns2_inst *gprs_ns2_instantiate(void *ctx, osmo_prim_cb cb, void *cb_
nsi->timeout[NS_TOUT_TSNS_PROV] = 3; /* 1..10 */
nsi->timeout[NS_TOUT_TSNS_SIZE_RETRIES] = 3;
nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES] = 3;
+ nsi->timeout[NS_TOUT_TSNS_PROCEDURES_RETRIES] = 3;
+
+ nsi->txqueue_max_length = NS_DEFAULT_TXQUEUE_MAX_LENGTH;
return nsi;
}
@@ -1439,14 +1491,11 @@ void gprs_ns2_start_alive_all_nsvcs(struct gprs_ns2_nse *nse)
* \param[in] bind the bind we want to destroy */
void gprs_ns2_free_bind(struct gprs_ns2_vc_bind *bind)
{
- struct gprs_ns2_vc *nsvc, *tmp;
+ struct gprs_ns2_vc *nsvc;
struct gprs_ns2_nse *nse;
- if (!bind)
+ if (!bind || bind->freed)
return;
-
- llist_for_each_entry_safe(nsvc, tmp, &bind->nsvc, blist) {
- gprs_ns2_free_nsvc(nsvc);
- }
+ bind->freed = true;
if (gprs_ns2_is_ip_bind(bind)) {
llist_for_each_entry(nse, &bind->nsi->nse, list) {
@@ -1454,6 +1503,12 @@ void gprs_ns2_free_bind(struct gprs_ns2_vc_bind *bind)
}
}
+ /* prevent recursive free() when the user reacts on a down event and free() a second time */
+ while (!llist_empty(&bind->nsvc)) {
+ nsvc = llist_first_entry(&bind->nsvc, struct gprs_ns2_vc, blist);
+ gprs_ns2_free_nsvc(nsvc);
+ }
+
if (bind->driver->free_bind)
bind->driver->free_bind(bind);
@@ -1465,9 +1520,11 @@ void gprs_ns2_free_bind(struct gprs_ns2_vc_bind *bind)
void gprs_ns2_free_binds(struct gprs_ns2_inst *nsi)
{
- struct gprs_ns2_vc_bind *bind, *tbind;
+ struct gprs_ns2_vc_bind *bind;
- llist_for_each_entry_safe(bind, tbind, &nsi->binding, list) {
+ /* prevent recursive free() when the user reacts on a down event and free() a second time */
+ while (!llist_empty(&nsi->binding)) {
+ bind = llist_first_entry(&nsi->binding, struct gprs_ns2_vc_bind, list);
gprs_ns2_free_bind(bind);
}
}
@@ -1542,7 +1599,7 @@ void ns2_nse_update_mtu(struct gprs_ns2_nse *nse)
nse->mtu = mtu;
if (nse->alive)
- ns2_prim_status_ind(nsvc->nse, NULL, 0, GPRS_NS2_AFF_CAUSE_MTU_CHANGE);
+ ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_MTU_CHANGE);
}
/*! calculate the transfer capabilities for a nse
@@ -1625,7 +1682,7 @@ int ns2_bind_alloc(struct gprs_ns2_inst *nsi, const char *name,
bind->sns_data_weight = 1;
bind->nsi = nsi;
INIT_LLIST_HEAD(&bind->nsvc);
- llist_add(&bind->list, &nsi->binding);
+ llist_add_tail(&bind->list, &nsi->binding);
nsi->bind_rate_ctr_idx++;
diff --git a/src/gb/gprs_ns2_fr.c b/src/gb/gprs_ns2_fr.c
index 84f37846..f6bee39c 100644
--- a/src/gb/gprs_ns2_fr.c
+++ b/src/gb/gprs_ns2_fr.c
@@ -55,13 +55,10 @@
#include <osmocom/core/timer.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gprs/gprs_ns2.h>
+#include <osmocom/core/netdev.h>
#include <osmocom/gprs/protocol/gsm_08_16.h>
#include <osmocom/gprs/protocol/gsm_08_18.h>
-#ifdef ENABLE_LIBMNL
-#include <osmocom/core/mnl.h>
-#endif
-
#include "config.h"
#include "common_vty.h"
#include "gprs_ns2_internal.h"
@@ -83,11 +80,6 @@
/* nanoseconds per bit (504) */
#define BIT_DURATION_NS (1000000000 / SUPERCHANNEL_LINERATE)
-struct gre_hdr {
- uint16_t flags;
- uint16_t ptype;
-} __attribute__ ((packed));
-
static void free_bind(struct gprs_ns2_vc_bind *bind);
static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg);
@@ -97,11 +89,12 @@ struct gprs_ns2_vc_driver vc_driver_fr = {
};
struct priv_bind {
+ struct osmo_netdev *netdev;
char netif[IFNAMSIZ];
struct osmo_fr_link *link;
int ifindex;
bool if_running;
- /* backlog queue for AF_PACKET / ENOBUFS handling (see OS#4993) */
+ /* backlog queue for AF_PACKET / ENOBUFS handling (see OS#4995) */
struct {
/* file-descriptor for AF_PACKET socket */
struct osmo_fd ofd;
@@ -177,6 +170,7 @@ static void free_bind(struct gprs_ns2_vc_bind *bind)
}
msgb_free(priv->backlog.lmi_msg);
+ osmo_netdev_free(priv->netdev);
osmo_fr_link_free(priv->link);
osmo_fd_close(&priv->backlog.ofd);
talloc_free(priv);
@@ -256,13 +250,13 @@ static int fr_netif_ofd_cb(struct osmo_fd *bfd, uint32_t what)
if (!(what & OSMO_FD_READ))
return 0;
- msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
+ msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR Rx");
if (!msg)
return -ENOMEM;
rc = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, (struct sockaddr *)&sll, &sll_len);
if (rc < 0) {
- LOGBIND(bind, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n", strerror(errno));
+ LOGBIND(bind, LOGL_ERROR, "recv error %s during NS-FR recv\n", strerror(errno));
goto out_err;
} else if (rc == 0) {
goto out_err;
@@ -338,16 +332,26 @@ int gprs_ns2_is_fr_bind(struct gprs_ns2_vc_bind *bind)
static int fr_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
{
struct priv_vc *vcpriv = nsvc->priv;
+ unsigned int vc_len = msgb_length(msg);
+ int rc;
msg->dst = vcpriv->dlc;
- return osmo_fr_tx_dlc(msg);
+ rc = osmo_fr_tx_dlc(msg);
+ if (OSMO_LIKELY(rc >= 0)) {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT, vc_len);
+ } else {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT_DROP);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT_DROP, vc_len);
+ }
+ return rc;
}
static void enqueue_at_head(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
{
struct priv_bind *priv = bind->priv;
llist_add(&msg->list, &priv->backlog.list);
- osmo_stat_item_inc(bind->statg->items[NS2_BIND_STAT_BACKLOG_LEN], 1);
+ osmo_stat_item_inc(osmo_stat_item_group_get_item(bind->statg, NS2_BIND_STAT_BACKLOG_LEN), 1);
osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
}
@@ -355,7 +359,7 @@ static void enqueue_at_tail(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
{
struct priv_bind *priv = bind->priv;
llist_add_tail(&msg->list, &priv->backlog.list);
- osmo_stat_item_inc(bind->statg->items[NS2_BIND_STAT_BACKLOG_LEN], 1);
+ osmo_stat_item_inc(osmo_stat_item_group_get_item(bind->statg, NS2_BIND_STAT_BACKLOG_LEN), 1);
osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
}
@@ -439,7 +443,7 @@ static void fr_backlog_timer_cb(void *data)
llist_add(&msg->list, &priv->backlog.list);
break;
}
- osmo_stat_item_dec(bind->statg->items[NS2_BIND_STAT_BACKLOG_LEN], 1);
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(bind->statg, NS2_BIND_STAT_BACKLOG_LEN), 1);
}
restart_timer:
@@ -520,60 +524,14 @@ static int open_socket(int ifindex, const struct gprs_ns2_vc_bind *nsbind)
return fd;
}
-#ifdef ENABLE_LIBMNL
-
-#include <osmocom/core/mnl.h>
-#include <linux/if_link.h>
-#include <linux/rtnetlink.h>
-
-#ifndef ARPHRD_FRAD
-#define ARPHRD_FRAD 770
-#endif
-
-/* validate the netlink attributes */
-static int data_attr_cb(const struct nlattr *attr, void *data)
-{
- const struct nlattr **tb = data;
- int type = mnl_attr_get_type(attr);
-
- if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
- return MNL_CB_OK;
-
- switch (type) {
- case IFLA_MTU:
- if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
- return MNL_CB_ERROR;
- break;
- case IFLA_IFNAME:
- if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
- return MNL_CB_ERROR;
- break;
- }
- tb[type] = attr;
- return MNL_CB_OK;
-}
-
-/* find the bind for the netdev (if any) */
-static struct gprs_ns2_vc_bind *bind4netdev(struct gprs_ns2_inst *nsi, const char *ifname)
-{
- struct gprs_ns2_vc_bind *bind;
-
- llist_for_each_entry(bind, &nsi->binding, list) {
- struct priv_bind *bpriv = bind->priv;
- if (!strcmp(bpriv->netif, ifname))
- return bind;
- }
-
- return NULL;
-}
-
-static void link_state_change(struct gprs_ns2_vc_bind *bind, bool if_running)
+static int gprs_n2_fr_ifupdown_ind_cb(struct osmo_netdev *netdev, bool if_running)
{
+ struct gprs_ns2_vc_bind *bind = osmo_netdev_get_priv_data(netdev);
struct priv_bind *bpriv = bind->priv;
struct msgb *msg, *msg2;
if (bpriv->if_running == if_running)
- return;
+ return 0;
LOGBIND(bind, LOGL_NOTICE, "FR net-device '%s': Physical link state changed: %s\n",
bpriv->netif, if_running ? "UP" : "DOWN");
@@ -594,89 +552,36 @@ static void link_state_change(struct gprs_ns2_vc_bind *bind, bool if_running)
}
bpriv->if_running = if_running;
+ return 0;
}
-static void mtu_change(struct gprs_ns2_vc_bind *bind, uint32_t mtu)
+static int gprs_n2_fr_mtu_chg_cb(struct osmo_netdev *netdev, uint32_t new_mtu)
{
+ struct gprs_ns2_vc_bind *bind = osmo_netdev_get_priv_data(netdev);
struct priv_bind *bpriv = bind->priv;
struct gprs_ns2_nse *nse;
- if (mtu == bind->mtu)
- return;
+ /* 2 byte DLCI header */
+ if (new_mtu <= 2)
+ return 0;
+ new_mtu -= 2;
+
+ if (new_mtu == bind->mtu)
+ return 0;
LOGBIND(bind, LOGL_INFO, "MTU changed from %d to %d.\n",
- bind->mtu, mtu);
+ bind->mtu + 2, new_mtu + 2);
- /* 2 byte DLCI header */
- bind->mtu = mtu - 2;
+ bind->mtu = new_mtu;
if (!bpriv->if_running)
- return;
+ return 0;
llist_for_each_entry(nse, &bind->nsi->nse, list) {
ns2_nse_update_mtu(nse);
}
+ return 0;
}
-/* handle a single netlink message received via libmnl */
-static int linkmon_mnl_cb(const struct nlmsghdr *nlh, void *data)
-{
- struct osmo_mnl *omnl = data;
- struct gprs_ns2_vc_bind *bind;
- struct nlattr *tb[IFLA_MAX+1] = {};
- struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
- struct gprs_ns2_inst *nsi;
- const char *ifname;
- bool if_running;
-
- OSMO_ASSERT(omnl);
- OSMO_ASSERT(ifm);
-
- nsi = omnl->priv;
-
- if (ifm->ifi_type != ARPHRD_FRAD)
- return MNL_CB_OK;
-
- mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
-
- if (!tb[IFLA_IFNAME])
- return MNL_CB_OK;
- ifname = mnl_attr_get_str(tb[IFLA_IFNAME]);
- if_running = !!(ifm->ifi_flags & IFF_RUNNING);
-
- bind = bind4netdev(nsi, ifname);
- if (!bind)
- return MNL_CB_OK;
-
- if (tb[IFLA_MTU]) {
- mtu_change(bind, mnl_attr_get_u32(tb[IFLA_MTU]));
- }
-
- link_state_change(bind, if_running);
-
- return MNL_CB_OK;
-}
-
-/* trigger one initial dump of all link information */
-static void linkmon_initial_dump(struct osmo_mnl *omnl)
-{
- char buf[MNL_SOCKET_BUFFER_SIZE];
- struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
- struct rtgenmsg *rt;
-
- nlh->nlmsg_type = RTM_GETLINK;
- nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
- nlh->nlmsg_seq = time(NULL);
- rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
- rt->rtgen_family = AF_PACKET;
-
- if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
- LOGP(DLNS, LOGL_ERROR, "linkmon: Cannot send rtnetlink message: %s\n", strerror(errno));
- }
-
- /* the response[s] will be handled just like the events */
-}
-#endif /* LIBMNL */
-
static int set_ifupdown(const char *netif, bool up)
{
int sock, rc;
@@ -855,16 +760,31 @@ int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
goto err_fr;
}
+ priv->netdev = osmo_netdev_alloc(bind, name);
+ if (!priv->netdev) {
+ rc = -ENOENT;
+ goto err_fr;
+ }
+ osmo_netdev_set_priv_data(priv->netdev, bind);
+ osmo_netdev_set_ifupdown_ind_cb(priv->netdev, gprs_n2_fr_ifupdown_ind_cb);
+ osmo_netdev_set_mtu_chg_cb(priv->netdev, gprs_n2_fr_mtu_chg_cb);
+ rc = osmo_netdev_set_ifindex(priv->netdev, priv->ifindex);
+ if (rc < 0)
+ goto err_free_netdev;
+ rc = osmo_netdev_register(priv->netdev);
+ if (rc < 0)
+ goto err_free_netdev;
+
/* set protocol frame relay and lmi */
rc = setup_device(priv->netif, bind);
if(rc < 0) {
LOGBIND(bind, LOGL_ERROR, "Failed to setup the interface %s for frame relay and lmi\n", netif);
- goto err_fr;
+ goto err_free_netdev;
}
rc = open_socket(priv->ifindex, bind);
if (rc < 0)
- goto err_fr;
+ goto err_free_netdev;
priv->backlog.retry_us = 2500; /* start with some non-zero value; this corrsponds to 496 bytes */
osmo_timer_setup(&priv->backlog.timer, fr_backlog_timer_cb, bind);
osmo_fd_setup(&priv->backlog.ofd, rc, OSMO_FD_READ, fr_netif_ofd_cb, bind, 0);
@@ -872,16 +792,6 @@ int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
if (rc < 0)
goto err_fd;
-#ifdef ENABLE_LIBMNL
- if (!nsi->linkmon_mnl)
- nsi->linkmon_mnl = osmo_mnl_init(nsi, NETLINK_ROUTE, RTMGRP_LINK, linkmon_mnl_cb, nsi);
-
- /* we get a new full dump after every bind. which is a bit excessive. But that's just once
- * at start-up, so we can get away with it */
- if (nsi->linkmon_mnl)
- linkmon_initial_dump(nsi->linkmon_mnl);
-#endif
-
if (result)
*result = bind;
@@ -889,6 +799,9 @@ int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
err_fd:
close(priv->backlog.ofd.fd);
+err_free_netdev:
+ osmo_netdev_free(priv->netdev);
+ priv->netdev = NULL;
err_fr:
osmo_fr_link_free(fr_link);
priv->link = NULL;
@@ -976,8 +889,9 @@ struct gprs_ns2_vc *gprs_ns2_fr_connect(struct gprs_ns2_vc_bind *bind,
goto err;
}
- snprintf(idbuf, sizeof(idbuf), "%s-%s-DLCI%u-NSE%05u-NSVC%05u", gprs_ns2_lltype_str(nse->ll),
- bpriv->netif, dlci, nse->nsei, nsvci);
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-NSVC%05u-%s-%s-DLCI%u", nse->nsei, nsvci,
+ gprs_ns2_lltype_str(nse->ll), bpriv->netif, dlci);
+ osmo_identifier_sanitize_buf(idbuf, NULL, '_');
nsvc = ns2_vc_alloc(bind, nse, true, GPRS_NS2_VC_MODE_BLOCKRESET, idbuf);
if (!nsvc)
goto err;
diff --git a/src/gb/gprs_ns2_frgre.c b/src/gb/gprs_ns2_frgre.c
index 93913431..b99761eb 100644
--- a/src/gb/gprs_ns2_frgre.c
+++ b/src/gb/gprs_ns2_frgre.c
@@ -503,6 +503,8 @@ static int frgre_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
uint16_t dlci = osmo_htons(bindpriv->dlci);
uint8_t *frh;
struct gre_hdr *greh;
+ unsigned int vc_len = msgb_length(msg);
+ int rc;
/* Prepend the FR header */
frh = msgb_push(msg, 2);
@@ -514,7 +516,15 @@ static int frgre_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
greh->flags = 0;
greh->ptype = osmo_htons(GRE_PTYPE_FR);
- return frgre_sendmsg(bind, msg, &vcpriv->remote);
+ rc = frgre_sendmsg(bind, msg, &vcpriv->remote);
+ if (OSMO_LIKELY(rc >= 0)) {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT, rc);
+ } else {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT_DROP);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT_DROP, vc_len);
+ }
+ return rc;
}
static int frgre_fd_cb(struct osmo_fd *bfd, unsigned int what)
diff --git a/src/gb/gprs_ns2_internal.h b/src/gb/gprs_ns2_internal.h
index 70e212a0..2e7dac3f 100644
--- a/src/gb/gprs_ns2_internal.h
+++ b/src/gb/gprs_ns2_internal.h
@@ -6,6 +6,7 @@
#include <stdint.h>
#include <osmocom/core/logging.h>
+#include <osmocom/core/rate_ctr.h>
#include <osmocom/gprs/protocol/gsm_08_16.h>
#include <osmocom/gprs/gprs_ns2.h>
@@ -38,6 +39,20 @@
#define LOG_NS_RX_SIGNAL(nsvc, pdu_type) LOG_NS_SIGNAL(nsvc, "Rx", pdu_type, LOGL_INFO, "\n")
#define LOG_NS_TX_SIGNAL(nsvc, pdu_type) LOG_NS_SIGNAL(nsvc, "Tx", pdu_type, LOGL_INFO, "\n")
+#define RATE_CTR_INC_NS(nsvc, ctr) \
+ do { \
+ struct gprs_ns2_vc *_nsvc = (nsvc); \
+ rate_ctr_inc(rate_ctr_group_get_ctr(_nsvc->ctrg, ctr)); \
+ rate_ctr_inc(rate_ctr_group_get_ctr(_nsvc->nse->ctrg, ctr)); \
+ } while (0)
+
+#define RATE_CTR_ADD_NS(nsvc, ctr, val) \
+ do { \
+ struct gprs_ns2_vc *_nsvc = (nsvc); \
+ rate_ctr_add(rate_ctr_group_get_ctr(_nsvc->ctrg, ctr), val); \
+ rate_ctr_add(rate_ctr_group_get_ctr(_nsvc->nse->ctrg, ctr), val); \
+ } while (0)
+
struct osmo_fsm_inst;
struct tlv_parsed;
@@ -46,8 +61,23 @@ struct vty;
struct gprs_ns2_vc_driver;
struct gprs_ns2_vc_bind;
-#define NS_TIMERS_COUNT 10
-#define NS_TIMERS "(tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries)"
+#define NS_TIMERS_COUNT 11
+
+#define TNS_BLOCK_STR "tns-block"
+#define TNS_BLOCK_RETRIES_STR "tns-block-retries"
+#define TNS_RESET_STR "tns-reset"
+#define TNS_RESET_RETRIES_STR "tns-reset-retries"
+#define TNS_TEST_STR "tns-test"
+#define TNS_ALIVE_STR "tns-alive"
+#define TNS_ALIVE_RETRIES_STR "tns-alive-retries"
+#define TSNS_PROV_STR "tsns-prov"
+#define TSNS_SIZE_RETRIES_STR "tsns-size-retries"
+#define TSNS_CONFIG_RETRIES_STR "tsns-config-retries"
+#define TSNS_PROCEDURES_RETRIES_STR "tsns-procedures-retries"
+#define NS_TIMERS "(" TNS_BLOCK_STR "|" TNS_BLOCK_RETRIES_STR "|" TNS_RESET_STR "|" TNS_RESET_RETRIES_STR "|" TNS_TEST_STR "|"\
+ TNS_ALIVE_STR "|" TNS_ALIVE_RETRIES_STR "|" TSNS_PROV_STR "|" TSNS_SIZE_RETRIES_STR "|" TSNS_CONFIG_RETRIES_STR "|"\
+ TSNS_PROCEDURES_RETRIES_STR ")"
+
#define NS_TIMERS_HELP \
"(un)blocking Timer (Tns-block) timeout\n" \
"(un)blocking Timer (Tns-block) number of retries\n" \
@@ -59,11 +89,14 @@ struct gprs_ns2_vc_bind;
"SNS Provision Timer (Tsns-prov) timeout\n" \
"SNS Size number of retries\n" \
"SNS Config number of retries\n" \
+ "SNS Procedures number of retries\n" \
/* Educated guess - LLC user payload is 1500 bytes plus possible headers */
#define NS_ALLOC_SIZE 3072
#define NS_ALLOC_HEADROOM 20
+#define NS_DEFAULT_TXQUEUE_MAX_LENGTH 128
+
enum ns2_timeout {
NS_TOUT_TNS_BLOCK,
NS_TOUT_TNS_BLOCK_RETRIES,
@@ -75,6 +108,7 @@ enum ns2_timeout {
NS_TOUT_TSNS_PROV,
NS_TOUT_TSNS_SIZE_RETRIES,
NS_TOUT_TSNS_CONFIG_RETRIES,
+ NS_TOUT_TSNS_PROCEDURES_RETRIES,
};
enum nsvc_timer_mode {
@@ -148,8 +182,7 @@ struct gprs_ns2_inst {
uint32_t nsvc_rate_ctr_idx;
uint32_t bind_rate_ctr_idx;
- /*! libmnl netlink socket for link state monitoring */
- struct osmo_mnl *linkmon_mnl;
+ uint32_t txqueue_max_length;
};
@@ -198,6 +231,15 @@ struct gprs_ns2_nse {
/*! are we implementing the SGSN role? */
bool ip_sns_role_sgsn;
+
+ /*! NSE-wide statistics */
+ struct rate_ctr_group *ctrg;
+
+ /*! recursive anchor */
+ bool freed;
+
+ /*! when the NSE became alive or dead */
+ struct timespec ts_alive_change;
};
/*! Structure representing a single NS-VC */
@@ -242,6 +284,15 @@ struct gprs_ns2_vc {
enum gprs_ns2_vc_mode mode;
struct osmo_fsm_inst *fi;
+
+ /*! recursive anchor */
+ bool freed;
+
+ /*! if blocked by O&M/vty */
+ bool om_blocked;
+
+ /*! when the NSVC became alive or dead */
+ struct timespec ts_alive_change;
};
/*! Structure repesenting a bind instance. E.g. IPv4 listen port. */
@@ -286,6 +337,9 @@ struct gprs_ns2_vc_bind {
uint8_t sns_data_weight;
struct osmo_stat_item_group *statg;
+
+ /*! recursive anchor */
+ bool freed;
};
struct gprs_ns2_vc_driver {
@@ -294,6 +348,25 @@ struct gprs_ns2_vc_driver {
void (*free_bind)(struct gprs_ns2_vc_bind *driver);
};
+enum ns2_sns_event {
+ NS2_SNS_EV_REQ_SELECT_ENDPOINT, /*!< Select a SNS endpoint from the list */
+ NS2_SNS_EV_RX_SIZE,
+ NS2_SNS_EV_RX_SIZE_ACK,
+ NS2_SNS_EV_RX_CONFIG,
+ NS2_SNS_EV_RX_CONFIG_END, /*!< SNS-CONFIG with end flag received */
+ NS2_SNS_EV_RX_CONFIG_ACK,
+ NS2_SNS_EV_RX_ADD,
+ NS2_SNS_EV_RX_DELETE,
+ NS2_SNS_EV_RX_CHANGE_WEIGHT,
+ NS2_SNS_EV_RX_ACK, /*!< Rx of SNS-ACK (response to ADD/DELETE/CHG_WEIGHT */
+ NS2_SNS_EV_REQ_NO_NSVC, /*!< no more NS-VC remaining (all dead) */
+ NS2_SNS_EV_REQ_FREE_NSVCS, /*!< free all NS-VCs */
+ NS2_SNS_EV_REQ_NSVC_ALIVE, /*!< a NS-VC became alive */
+ NS2_SNS_EV_REQ_ADD_BIND, /*!< add a new local bind to this NSE */
+ NS2_SNS_EV_REQ_DELETE_BIND, /*!< remove a local bind from this NSE */
+ NS2_SNS_EV_REQ_CHANGE_WEIGHT, /*!< a bind changed its weight */
+};
+
enum ns2_cs ns2_create_vc(struct gprs_ns2_vc_bind *bind,
struct msgb *msg,
const struct osmo_sockaddr *remote,
@@ -310,6 +383,7 @@ struct gprs_ns2_vc *ns2_vc_alloc(struct gprs_ns2_vc_bind *bind,
enum gprs_ns2_vc_mode vc_mode,
const char *id);
+void ns2_free_nsvcs(struct gprs_ns2_nse *nse);
int ns2_bind_alloc(struct gprs_ns2_inst *nsi, const char *name,
struct gprs_ns2_vc_bind **result);
@@ -368,8 +442,8 @@ int ns2_tx_sns_del(struct gprs_ns2_vc *nsvc,
unsigned int num_ip6_elems);
/* transmit message over a VC */
-int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause);
-int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc);
+int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause, uint16_t *nsvci);
+int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc, uint16_t *nsvci);
int ns2_tx_reset(struct gprs_ns2_vc *nsvc, uint8_t cause);
int ns2_tx_reset_ack(struct gprs_ns2_vc *nsvc);
@@ -385,7 +459,7 @@ int ns2_tx_unit_data(struct gprs_ns2_vc *nsvc,
struct msgb *msg);
int ns2_tx_status(struct gprs_ns2_vc *nsvc, uint8_t cause,
- uint16_t bvci, struct msgb *orig_msg);
+ uint16_t bvci, struct msgb *orig_msg, uint16_t *nsvci);
/* driver */
struct gprs_ns2_vc *ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
@@ -395,6 +469,7 @@ int ns2_ip_count_bind(struct gprs_ns2_inst *nsi, struct osmo_sockaddr *remote);
struct gprs_ns2_vc_bind *ns2_ip_get_bind_by_index(struct gprs_ns2_inst *nsi,
struct osmo_sockaddr *remote,
int index);
+void ns2_ip_set_txqueue_max_length(struct gprs_ns2_vc_bind *bind, unsigned int max_length);
/* sns */
int ns2_sns_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp);
diff --git a/src/gb/gprs_ns2_message.c b/src/gb/gprs_ns2_message.c
index 93341bbd..de63b7aa 100644
--- a/src/gb/gprs_ns2_message.c
+++ b/src/gb/gprs_ns2_message.c
@@ -95,22 +95,26 @@ static int ns2_validate_status(struct gprs_ns2_vc *nsvc, struct msgb *msg, struc
return -1;
}
- uint8_t _cause = tlvp_val8(tp, NS_IE_VCI, 0);
-
+ uint8_t _cause = tlvp_val8(tp, NS_IE_CAUSE, 0);
switch (_cause) {
case NS_CAUSE_NSVC_BLOCKED:
case NS_CAUSE_NSVC_UNKNOWN:
- if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
+ if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 1)) {
*cause = NS_CAUSE_MISSING_ESSENT_IE;
return -1;
}
+
+ if (nsvc->mode != GPRS_NS2_VC_MODE_BLOCKRESET) {
+ *cause = NS_CAUSE_PDU_INCOMP_PSTATE;
+ return -1;
+ }
break;
case NS_CAUSE_SEM_INCORR_PDU:
case NS_CAUSE_PDU_INCOMP_PSTATE:
case NS_CAUSE_PROTO_ERR_UNSPEC:
case NS_CAUSE_INVAL_ESSENT_IE:
case NS_CAUSE_MISSING_ESSENT_IE:
- if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
+ if (!TLVP_PRES_LEN(tp, NS_IE_PDU, 1)) {
*cause = NS_CAUSE_MISSING_ESSENT_IE;
return -1;
}
@@ -165,23 +169,9 @@ int ns2_validate(struct gprs_ns2_vc *nsvc,
return 0;
}
-
static int ns_vc_tx(struct gprs_ns2_vc *nsvc, struct msgb *msg)
{
- unsigned int bytes = msgb_length(msg);
- int rc;
-
-
- rc = nsvc->bind->send_vc(nsvc, msg);
- if (rc < 0) {
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_OUT_DROP]);
- rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_OUT_DROP], bytes);
- } else {
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_OUT]);
- rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_OUT], bytes);
- }
-
- return rc;
+ return nsvc->bind->send_vc(nsvc, msg);
}
/* transmit functions */
@@ -207,12 +197,18 @@ static int ns2_tx_simple(struct gprs_ns2_vc *nsvc, uint8_t pdu_type)
/*! Transmit a NS-BLOCK on a given NS-VC.
* \param[in] vc NS-VC on which the NS-BLOCK is to be transmitted
* \param[in] cause Numeric NS Cause value
+ * \param[in] nsvci if given this NSVCI will be encoded. If NULL the nsvc->nsvci will be used.
* \returns 0 in case of success */
-int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause)
+int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause, uint16_t *nsvci)
{
struct msgb *msg;
struct gprs_ns_hdr *nsh;
- uint16_t nsvci = osmo_htons(nsvc->nsvci);
+ uint16_t encoded_nsvci;
+
+ if (nsvci)
+ encoded_nsvci = osmo_htons(*nsvci);
+ else
+ encoded_nsvci = osmo_htons(nsvc->nsvci);
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
@@ -223,14 +219,14 @@ int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause)
if (!msg)
return -ENOMEM;
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_BLOCKED));
msg->l2h = msgb_put(msg, sizeof(*nsh));
nsh = (struct gprs_ns_hdr *) msg->l2h;
nsh->pdu_type = NS_PDUT_BLOCK;
msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
- msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &encoded_nsvci);
LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO, " cause=%s\n", gprs_ns2_cause_str(cause));
return ns_vc_tx(nsvc, msg);
@@ -238,12 +234,18 @@ int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause)
/*! Transmit a NS-BLOCK-ACK on a given NS-VC.
* \param[in] nsvc NS-VC on which the NS-BLOCK is to be transmitted
+ * \param[in] nsvci if given this NSVCI will be encoded. If NULL the nsvc->nsvci will be used.
* \returns 0 in case of success */
-int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc)
+int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc, uint16_t *nsvci)
{
struct msgb *msg;
struct gprs_ns_hdr *nsh;
- uint16_t nsvci = osmo_htons(nsvc->nsvci);
+ uint16_t encoded_nsvci;
+
+ if (nsvci)
+ encoded_nsvci = osmo_htons(*nsvci);
+ else
+ encoded_nsvci = osmo_htons(nsvc->nsvci);
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
@@ -258,7 +260,7 @@ int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc)
nsh = (struct gprs_ns_hdr *) msg->l2h;
nsh->pdu_type = NS_PDUT_BLOCK_ACK;
- msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &encoded_nsvci);
LOG_NS_TX_SIGNAL(nsvc, nsh->pdu_type);
return ns_vc_tx(nsvc, msg);
@@ -266,7 +268,7 @@ int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc)
/*! Transmit a NS-RESET on a given NS-VC.
* \param[in] nsvc NS-VC used for transmission
- * \paam[in] cause Numeric NS cause value
+ * \param[in] cause Numeric NS cause value
* \returns 0 in case of success */
int ns2_tx_reset(struct gprs_ns2_vc *nsvc, uint8_t cause)
{
@@ -416,13 +418,14 @@ int ns2_tx_unit_data(struct gprs_ns2_vc *nsvc,
* \param[in] cause Numeric NS cause value
* \param[in] bvci BVCI to be reset within NSVC
* \param[in] orig_msg message causing the STATUS
+ * \param[in] nsvci if given this NSVCI will be encoded. If NULL the nsvc->nsvci will be used.
* \returns 0 in case of success */
int ns2_tx_status(struct gprs_ns2_vc *nsvc, uint8_t cause,
- uint16_t bvci, struct msgb *orig_msg)
+ uint16_t bvci, struct msgb *orig_msg, uint16_t *nsvci)
{
struct msgb *msg = ns2_msgb_alloc();
struct gprs_ns_hdr *nsh;
- uint16_t nsvci = osmo_htons(nsvc->nsvci);
+ uint16_t encoded_nsvci;
unsigned int orig_len, max_orig_len;
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
@@ -443,7 +446,11 @@ int ns2_tx_status(struct gprs_ns2_vc *nsvc, uint8_t cause,
case NS_CAUSE_NSVC_BLOCKED:
case NS_CAUSE_NSVC_UNKNOWN:
/* Section 9.2.7.1: Static conditions for NS-VCI */
- msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
+ if (nsvci)
+ encoded_nsvci = osmo_htons(*nsvci);
+ else
+ encoded_nsvci = osmo_htons(nsvc->nsvci);
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&encoded_nsvci);
break;
case NS_CAUSE_SEM_INCORR_PDU:
case NS_CAUSE_PDU_INCOMP_PSTATE:
@@ -798,7 +805,7 @@ int ns2_tx_sns_size(struct gprs_ns2_vc *nsvc, bool reset_flag, uint16_t max_nr_n
msgb_tv16_put(msg, NS_IE_IPv6_EP_NR, ip6_ep_nr);
LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO,
- " (reset=%u, max_nr_nsvc=%u, num_ip4=%u, num_ip6=%u)\n",
+ " (reset=%u, max_nr_nsvc=%u, num_ip4=%d, num_ip6=%d)\n",
reset_flag, max_nr_nsvc, ip4_ep_nr, ip6_ep_nr);
return ns_vc_tx(nsvc, msg);
}
diff --git a/src/gb/gprs_ns2_sns.c b/src/gb/gprs_ns2_sns.c
index c6e80afd..0afc06ed 100644
--- a/src/gb/gprs_ns2_sns.c
+++ b/src/gb/gprs_ns2_sns.c
@@ -56,11 +56,6 @@
#define S(x) (1 << (x))
-enum ns2_sns_type {
- IPv4,
- IPv6,
-};
-
enum ns2_sns_role {
GPRS_SNS_ROLE_BSS,
GPRS_SNS_ROLE_SGSN,
@@ -75,43 +70,38 @@ enum gprs_sns_bss_state {
GPRS_SNS_ST_CONFIGURED,
GPRS_SNS_ST_SGSN_WAIT_CONFIG, /* !< SGSN role: Wait for CONFIG from BSS */
GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK, /* !< SGSN role: Wait for CONFIG-ACK from BSS */
-};
-
-enum gprs_sns_event {
- GPRS_SNS_EV_REQ_SELECT_ENDPOINT, /*!< Select a SNS endpoint from the list */
- GPRS_SNS_EV_RX_SIZE,
- GPRS_SNS_EV_RX_SIZE_ACK,
- GPRS_SNS_EV_RX_CONFIG,
- GPRS_SNS_EV_RX_CONFIG_END, /*!< SNS-CONFIG with end flag received */
- GPRS_SNS_EV_RX_CONFIG_ACK,
- GPRS_SNS_EV_RX_ADD,
- GPRS_SNS_EV_RX_DELETE,
- GPRS_SNS_EV_RX_CHANGE_WEIGHT,
- GPRS_SNS_EV_RX_ACK, /*!< Rx of SNS-ACK (response to ADD/DELETE/CHG_WEIGHT */
- GPRS_SNS_EV_REQ_NO_NSVC, /*!< no more NS-VC remaining (all dead) */
- GPRS_SNS_EV_REQ_NSVC_ALIVE, /*!< a NS-VC became alive */
- GPRS_SNS_EV_REQ_ADD_BIND, /*!< add a new local bind to this NSE */
- GPRS_SNS_EV_REQ_DELETE_BIND, /*!< remove a local bind from this NSE */
+ GPRS_SNS_ST_LOCAL_PROCEDURE, /*!< in process of a ADD/DEL/CHANGE procedure towards SGSN (BSS->SGSN) */
};
static const struct value_string gprs_sns_event_names[] = {
- { GPRS_SNS_EV_REQ_SELECT_ENDPOINT, "REQ_SELECT_ENDPOINT" },
- { GPRS_SNS_EV_RX_SIZE, "RX_SIZE" },
- { GPRS_SNS_EV_RX_SIZE_ACK, "RX_SIZE_ACK" },
- { GPRS_SNS_EV_RX_CONFIG, "RX_CONFIG" },
- { GPRS_SNS_EV_RX_CONFIG_END, "RX_CONFIG_END" },
- { GPRS_SNS_EV_RX_CONFIG_ACK, "RX_CONFIG_ACK" },
- { GPRS_SNS_EV_RX_ADD, "RX_ADD" },
- { GPRS_SNS_EV_RX_DELETE, "RX_DELETE" },
- { GPRS_SNS_EV_RX_ACK, "RX_ACK" },
- { GPRS_SNS_EV_RX_CHANGE_WEIGHT, "RX_CHANGE_WEIGHT" },
- { GPRS_SNS_EV_REQ_NO_NSVC, "REQ_NO_NSVC" },
- { GPRS_SNS_EV_REQ_NSVC_ALIVE, "REQ_NSVC_ALIVE"},
- { GPRS_SNS_EV_REQ_ADD_BIND, "REQ_ADD_BIND"},
- { GPRS_SNS_EV_REQ_DELETE_BIND, "REQ_DELETE_BIND"},
+ { NS2_SNS_EV_REQ_SELECT_ENDPOINT, "REQ_SELECT_ENDPOINT" },
+ { NS2_SNS_EV_RX_SIZE, "RX_SIZE" },
+ { NS2_SNS_EV_RX_SIZE_ACK, "RX_SIZE_ACK" },
+ { NS2_SNS_EV_RX_CONFIG, "RX_CONFIG" },
+ { NS2_SNS_EV_RX_CONFIG_END, "RX_CONFIG_END" },
+ { NS2_SNS_EV_RX_CONFIG_ACK, "RX_CONFIG_ACK" },
+ { NS2_SNS_EV_RX_ADD, "RX_ADD" },
+ { NS2_SNS_EV_RX_DELETE, "RX_DELETE" },
+ { NS2_SNS_EV_RX_ACK, "RX_ACK" },
+ { NS2_SNS_EV_RX_CHANGE_WEIGHT, "RX_CHANGE_WEIGHT" },
+ { NS2_SNS_EV_REQ_NO_NSVC, "REQ_NO_NSVC" },
+ { NS2_SNS_EV_REQ_FREE_NSVCS, "REQ_FREE_NSVCS" },
+ { NS2_SNS_EV_REQ_NSVC_ALIVE, "REQ_NSVC_ALIVE"},
+ { NS2_SNS_EV_REQ_ADD_BIND, "REQ_ADD_BIND"},
+ { NS2_SNS_EV_REQ_DELETE_BIND, "REQ_DELETE_BIND"},
+ { NS2_SNS_EV_REQ_CHANGE_WEIGHT, "REQ_CHANGE_WEIGHT"},
{ 0, NULL }
};
+#define GPRS_SNS_FLAG_KEEP_SELECT_ENDPOINT_ORDER (void *) 1
+
+enum sns_procedure {
+ SNS_PROC_NONE, /*!< used as invalid/idle value */
+ SNS_PROC_ADD,
+ SNS_PROC_DEL,
+ SNS_PROC_CHANGE_WEIGHT,
+};
+
struct sns_endpoint {
struct llist_head list;
struct osmo_sockaddr saddr;
@@ -120,12 +110,35 @@ struct sns_endpoint {
struct ns2_sns_bind {
struct llist_head list;
struct gprs_ns2_vc_bind *bind;
+ uint8_t change_weight_state;
+};
+
+struct ns2_sns_procedure {
+ struct llist_head list;
+ struct ns2_sns_bind *sbind;
+ uint16_t sig_weight;
+ uint16_t data_weight;
+ /* copy entry to protect against changes of gss->local */
+ struct gprs_ns_ie_ip4_elem ip4;
+ struct gprs_ns_ie_ip6_elem ip6;
+ enum sns_procedure procedure;
+ uint8_t trans_id;
+ /* is the procedure in process */
+ bool running;
+};
+
+struct ns2_sns_elems {
+ struct gprs_ns_ie_ip4_elem *ip4;
+ unsigned int num_ip4;
+ struct gprs_ns_ie_ip6_elem *ip6;
+ unsigned int num_ip6;
};
struct ns2_sns_state {
struct gprs_ns2_nse *nse;
- enum ns2_sns_type ip;
+ /* containing the address family AF_* */
+ int family;
enum ns2_sns_role role; /* local role: BSS or SGSN */
/* holds the list of initial SNS endpoints */
@@ -137,6 +150,9 @@ struct ns2_sns_state {
/* prevent recursive reselection */
bool reselection_running;
+ /* protection against recursive free() */
+ bool block_no_nsvc_events;
+
/* The current initial SNS endpoints.
* The initial connection will be moved into the NSE
* if configured via SNS. Otherwise it will be removed
@@ -150,12 +166,13 @@ struct ns2_sns_state {
bool alive;
/* local configuration to send to the remote end */
- struct gprs_ns_ie_ip4_elem *ip4_local;
- size_t num_ip4_local;
+ struct ns2_sns_elems local;
- /* local configuration to send to the remote end */
- struct gprs_ns_ie_ip6_elem *ip6_local;
- size_t num_ip6_local;
+ /* local configuration after all local procedures applied */
+ struct ns2_sns_elems local_procedure;
+
+ /* remote configuration as received */
+ struct ns2_sns_elems remote;
/* local configuration about our capabilities in terms of connections to
* remote (SGSN) side */
@@ -163,13 +180,9 @@ struct ns2_sns_state {
size_t num_max_ip4_remote;
size_t num_max_ip6_remote;
- /* remote configuration as received */
- struct gprs_ns_ie_ip4_elem *ip4_remote;
- unsigned int num_ip4_remote;
-
- /* remote configuration as received */
- struct gprs_ns_ie_ip6_elem *ip6_remote;
- unsigned int num_ip6_remote;
+ struct llist_head procedures;
+ struct ns2_sns_procedure *current_procedure;
+ uint8_t trans_id;
};
static inline struct gprs_ns2_nse *nse_inst_from_fi(struct osmo_fsm_inst *fi)
@@ -178,49 +191,68 @@ static inline struct gprs_ns2_nse *nse_inst_from_fi(struct osmo_fsm_inst *fi)
return gss->nse;
}
+/* The SNS has failed. Etither restart the SNS (BSS) or remove the SNS (SGSN) */
+#define sns_failed(fi, reason) \
+ _sns_failed(fi, reason, __FILE__, __LINE__)
+static void _sns_failed(struct osmo_fsm_inst *fi, const char *reason, const char *file, int line)
+{
+ struct ns2_sns_state *gss = fi->priv;
+
+ if (reason)
+ LOGPFSMLSRC(fi, LOGL_ERROR, file, line, "NSE %d: SNS failed: %s\n", gss->nse->nsei, reason);
+
+ gss->alive = false;
+ if (gss->role == GPRS_SNS_ROLE_SGSN) {
+ if (!gss->nse->persistent)
+ gprs_ns2_free_nse(gss->nse);
+ else
+ _osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0, file, line);
+ } else {
+ _osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL, file, line);
+ }
+}
+
/* helper function to compute the sum of all (data or signaling) weights */
-static int ip4_weight_sum(const struct gprs_ns_ie_ip4_elem *ip4, unsigned int num,
- bool data_weight)
+static int ip4_weight_sum(const struct ns2_sns_elems *elems, bool data_weight)
{
unsigned int i;
int weight_sum = 0;
- for (i = 0; i < num; i++) {
+ for (i = 0; i < elems->num_ip4; i++) {
if (data_weight)
- weight_sum += ip4[i].data_weight;
+ weight_sum += elems->ip4[i].data_weight;
else
- weight_sum += ip4[i].sig_weight;
+ weight_sum += elems->ip4[i].sig_weight;
}
return weight_sum;
}
-#define ip4_weight_sum_data(x,y) ip4_weight_sum(x, y, true)
-#define ip4_weight_sum_sig(x,y) ip4_weight_sum(x, y, false)
+#define ip4_weight_sum_data(elems) ip4_weight_sum(elems, true)
+#define ip4_weight_sum_sig(elems) ip4_weight_sum(elems, false)
/* helper function to compute the sum of all (data or signaling) weights */
-static int ip6_weight_sum(const struct gprs_ns_ie_ip6_elem *ip6, unsigned int num,
- bool data_weight)
+static int ip6_weight_sum(const struct ns2_sns_elems *elems, bool data_weight)
{
unsigned int i;
int weight_sum = 0;
- for (i = 0; i < num; i++) {
+ for (i = 0; i < elems->num_ip6; i++) {
if (data_weight)
- weight_sum += ip6[i].data_weight;
+ weight_sum += elems->ip6[i].data_weight;
else
- weight_sum += ip6[i].sig_weight;
+ weight_sum += elems->ip6[i].sig_weight;
}
return weight_sum;
}
-#define ip6_weight_sum_data(x,y) ip6_weight_sum(x, y, true)
-#define ip6_weight_sum_sig(x,y) ip6_weight_sum(x, y, false)
+#define ip6_weight_sum_data(elems) ip6_weight_sum(elems, true)
+#define ip6_weight_sum_sig(elems) ip6_weight_sum(elems, false)
-static int nss_weight_sum(const struct ns2_sns_state *nss, bool data_weight)
+static int ip46_weight_sum(const struct ns2_sns_elems *elems, bool data_weight)
{
- return ip4_weight_sum(nss->ip4_remote, nss->num_ip4_remote, data_weight) +
- ip6_weight_sum(nss->ip6_remote, nss->num_ip6_remote, data_weight);
+ return ip4_weight_sum(elems, data_weight) +
+ ip6_weight_sum(elems, data_weight);
}
-#define nss_weight_sum_data(nss) nss_weight_sum(nss, true)
-#define nss_weight_sum_sig(nss) nss_weight_sum(nss, false)
+#define ip46_weight_sum_data(elems) ip46_weight_sum(elems, true)
+#define ip46_weight_sum_sig(elems) ip46_weight_sum(elems, false)
static struct gprs_ns2_vc *nsvc_by_ip4_elem(struct gprs_ns2_nse *nse,
const struct gprs_ns_ie_ip4_elem *ip4)
@@ -295,25 +327,27 @@ void ns2_sns_replace_nsvc(struct gprs_ns2_vc *nsvc)
}
}
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_NO_NSVC, NULL);
+ if (gss->block_no_nsvc_events)
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_NO_NSVC, NULL);
}
-static void ns2_clear_ipv46_entries_local(struct ns2_sns_state *gss)
+static void ns2_clear_elems(struct ns2_sns_elems *elems)
{
- TALLOC_FREE(gss->ip4_local);
- TALLOC_FREE(gss->ip6_local);
+ TALLOC_FREE(elems->ip4);
+ TALLOC_FREE(elems->ip6);
- gss->num_ip4_local = 0;
- gss->num_ip6_local = 0;
+ elems->num_ip4 = 0;
+ elems->num_ip6 = 0;
}
-static void ns2_clear_ipv46_entries_remote(struct ns2_sns_state *gss)
+static void ns2_clear_procedures(struct ns2_sns_state *gss)
{
- TALLOC_FREE(gss->ip4_remote);
- TALLOC_FREE(gss->ip6_remote);
-
- gss->num_ip4_remote = 0;
- gss->num_ip6_remote = 0;
+ struct ns2_sns_procedure *procedure, *tmp;
+ gss->current_procedure = NULL;
+ llist_for_each_entry_safe(procedure, tmp, &gss->procedures, list) {
+ llist_del(&procedure->list);
+ talloc_free(procedure);
+ }
}
static void ns2_vc_create_ip(struct osmo_fsm_inst *fi, struct gprs_ns2_nse *nse, const struct osmo_sockaddr *remote,
@@ -393,8 +427,8 @@ static int create_missing_nsvcs(struct osmo_fsm_inst *fi)
unsigned int i;
/* iterate over all remote IPv4 endpoints */
- for (i = 0; i < gss->num_ip4_remote; i++) {
- const struct gprs_ns_ie_ip4_elem *ip4 = &gss->ip4_remote[i];
+ for (i = 0; i < gss->remote.num_ip4; i++) {
+ const struct gprs_ns_ie_ip4_elem *ip4 = &gss->remote.ip4[i];
remote.u.sin.sin_family = AF_INET;
remote.u.sin.sin_addr.s_addr = ip4->ip_addr;
@@ -425,8 +459,8 @@ static int create_missing_nsvcs(struct osmo_fsm_inst *fi)
}
/* iterate over all remote IPv4 endpoints */
- for (i = 0; i < gss->num_ip6_remote; i++) {
- const struct gprs_ns_ie_ip6_elem *ip6 = &gss->ip6_remote[i];
+ for (i = 0; i < gss->remote.num_ip6; i++) {
+ const struct gprs_ns_ie_ip6_elem *ip6 = &gss->remote.ip6[i];
remote.u.sin6.sin6_family = AF_INET6;
remote.u.sin6.sin6_addr = ip6->ip_addr;
@@ -461,106 +495,137 @@ static int create_missing_nsvcs(struct osmo_fsm_inst *fi)
}
/* Add a given remote IPv4 element to gprs_sns_state */
-static int add_remote_ip4_elem(struct ns2_sns_state *gss, const struct gprs_ns_ie_ip4_elem *ip4)
+static int add_ip4_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
+ const struct gprs_ns_ie_ip4_elem *ip4)
{
- unsigned int i;
-
- if (gss->num_ip4_remote >= gss->num_max_ip4_remote)
- return -NS_CAUSE_INVAL_NR_NS_VC;
-
/* check for duplicates */
- for (i = 0; i < gss->num_ip4_remote; i++) {
- if (memcmp(&gss->ip4_remote[i], ip4, sizeof(*ip4)))
+ for (unsigned int i = 0; i < elems->num_ip4; i++) {
+ if (memcmp(&elems->ip4[i], ip4, sizeof(*ip4)))
continue;
- /* TODO: log message duplicate */
- return -NS_CAUSE_PROTO_ERR_UNSPEC;
+ return -1;
}
- gss->ip4_remote = talloc_realloc(gss, gss->ip4_remote, struct gprs_ns_ie_ip4_elem,
- gss->num_ip4_remote+1);
- gss->ip4_remote[gss->num_ip4_remote] = *ip4;
- gss->num_ip4_remote += 1;
+ elems->ip4 = talloc_realloc(gss, elems->ip4, struct gprs_ns_ie_ip4_elem,
+ elems->num_ip4+1);
+ elems->ip4[elems->num_ip4] = *ip4;
+ elems->num_ip4 += 1;
return 0;
}
/* Remove a given remote IPv4 element from gprs_sns_state */
-static int remove_remote_ip4_elem(struct ns2_sns_state *gss, const struct gprs_ns_ie_ip4_elem *ip4)
+static int remove_ip4_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
+ const struct gprs_ns_ie_ip4_elem *ip4)
{
unsigned int i;
- for (i = 0; i < gss->num_ip4_remote; i++) {
- if (memcmp(&gss->ip4_remote[i], ip4, sizeof(*ip4)))
+ for (i = 0; i < elems->num_ip4; i++) {
+ if (memcmp(&elems->ip4[i], ip4, sizeof(*ip4)))
continue;
/* all array elements < i remain as they are; all > i are shifted left by one */
- memmove(&gss->ip4_remote[i], &gss->ip4_remote[i+1], gss->num_ip4_remote-i-1);
- gss->num_ip4_remote -= 1;
+ memmove(&elems->ip4[i], &elems->ip4[i+1], elems->num_ip4-i-1);
+ elems->num_ip4 -= 1;
return 0;
}
return -1;
}
/* update the weights for specified remote IPv4 */
-static int update_remote_ip4_elem(struct ns2_sns_state *gss, const struct gprs_ns_ie_ip4_elem *ip4)
+static int update_ip4_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
+ const struct gprs_ns_ie_ip4_elem *ip4)
{
unsigned int i;
- for (i = 0; i < gss->num_ip4_remote; i++) {
- if (gss->ip4_remote[i].ip_addr != ip4->ip_addr ||
- gss->ip4_remote[i].udp_port != ip4->udp_port)
+ for (i = 0; i < elems->num_ip4; i++) {
+ if (elems->ip4[i].ip_addr != ip4->ip_addr ||
+ elems->ip4[i].udp_port != ip4->udp_port)
continue;
- gss->ip4_remote[i].sig_weight = ip4->sig_weight;
- gss->ip4_remote[i].data_weight = ip4->data_weight;
+ elems->ip4[i].sig_weight = ip4->sig_weight;
+ elems->ip4[i].data_weight = ip4->data_weight;
return 0;
}
return -1;
}
/* Add a given remote IPv6 element to gprs_sns_state */
-static int add_remote_ip6_elem(struct ns2_sns_state *gss, const struct gprs_ns_ie_ip6_elem *ip6)
+static int add_ip6_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
+ const struct gprs_ns_ie_ip6_elem *ip6)
{
- if (gss->num_ip6_remote >= gss->num_max_ip6_remote)
- return -NS_CAUSE_INVAL_NR_NS_VC;
+ /* check for duplicates */
+ for (unsigned int i = 0; i < elems->num_ip6; i++) {
+ if (memcmp(&elems->ip6[i].ip_addr, &ip6->ip_addr, sizeof(ip6->ip_addr)) ||
+ elems->ip6[i].udp_port != ip6->udp_port)
+ continue;
+ return -1;
+ }
- gss->ip6_remote = talloc_realloc(gss, gss->ip6_remote, struct gprs_ns_ie_ip6_elem,
- gss->num_ip6_remote+1);
- gss->ip6_remote[gss->num_ip6_remote] = *ip6;
- gss->num_ip6_remote += 1;
+ elems->ip6 = talloc_realloc(gss, elems->ip6, struct gprs_ns_ie_ip6_elem,
+ elems->num_ip6+1);
+ elems->ip6[elems->num_ip6] = *ip6;
+ elems->num_ip6 += 1;
return 0;
}
/* Remove a given remote IPv6 element from gprs_sns_state */
-static int remove_remote_ip6_elem(struct ns2_sns_state *gss, const struct gprs_ns_ie_ip6_elem *ip6)
+static int remove_ip6_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
+ const struct gprs_ns_ie_ip6_elem *ip6)
{
unsigned int i;
- for (i = 0; i < gss->num_ip6_remote; i++) {
- if (memcmp(&gss->ip6_remote[i], ip6, sizeof(*ip6)))
+ for (i = 0; i < elems->num_ip6; i++) {
+ if (memcmp(&elems->ip6[i], ip6, sizeof(*ip6)))
continue;
/* all array elements < i remain as they are; all > i are shifted left by one */
- memmove(&gss->ip6_remote[i], &gss->ip6_remote[i+1], gss->num_ip6_remote-i-1);
- gss->num_ip6_remote -= 1;
+ memmove(&elems->ip6[i], &elems->ip6[i+1], elems->num_ip6-i-1);
+ elems->num_ip6 -= 1;
return 0;
}
return -1;
}
/* update the weights for specified remote IPv6 */
-static int update_remote_ip6_elem(struct ns2_sns_state *gss, const struct gprs_ns_ie_ip6_elem *ip6)
+static int update_ip6_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
+ const struct gprs_ns_ie_ip6_elem *ip6)
{
unsigned int i;
- for (i = 0; i < gss->num_ip6_remote; i++) {
- if (memcmp(&gss->ip6_remote[i].ip_addr, &ip6->ip_addr, sizeof(ip6->ip_addr)) ||
- gss->ip6_remote[i].udp_port != ip6->udp_port)
+ for (i = 0; i < elems->num_ip6; i++) {
+ if (memcmp(&elems->ip6[i].ip_addr, &ip6->ip_addr, sizeof(ip6->ip_addr)) ||
+ elems->ip6[i].udp_port != ip6->udp_port)
continue;
- gss->ip6_remote[i].sig_weight = ip6->sig_weight;
- gss->ip6_remote[i].data_weight = ip6->data_weight;
+ elems->ip6[i].sig_weight = ip6->sig_weight;
+ elems->ip6[i].data_weight = ip6->data_weight;
return 0;
}
return -1;
}
+static int remove_bind_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems, struct ns2_sns_bind *sbind)
+{
+ struct gprs_ns_ie_ip4_elem ip4;
+ struct gprs_ns_ie_ip6_elem ip6;
+ const struct osmo_sockaddr *saddr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+
+ switch (saddr->u.sa.sa_family) {
+ case AF_INET:
+ ip4.ip_addr = saddr->u.sin.sin_addr.s_addr;
+ ip4.udp_port = saddr->u.sin.sin_port;
+ ip4.sig_weight = sbind->bind->sns_sig_weight;
+ ip4.data_weight = sbind->bind->sns_data_weight;
+ return remove_ip4_elem(gss, elems, &ip4);
+ case AF_INET6:
+ memcpy(&ip6.ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr));
+ ip6.udp_port = saddr->u.sin.sin_port;
+ ip6.sig_weight = sbind->bind->sns_sig_weight;
+ ip6.data_weight = sbind->bind->sns_data_weight;
+ return remove_ip6_elem(gss, elems, &ip6);
+ default:
+ return -1;
+ }
+
+ return -1;
+}
+
static int do_sns_change_weight(struct osmo_fsm_inst *fi, const struct gprs_ns_ie_ip4_elem *ip4, const struct gprs_ns_ie_ip6_elem *ip6)
{
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
@@ -578,7 +643,7 @@ static int do_sns_change_weight(struct osmo_fsm_inst *fi, const struct gprs_ns_i
* SNS-ACK PDU with a cause code of "Invalid weights". */
if (ip4) {
- if (update_remote_ip4_elem(gss, ip4))
+ if (update_ip4_elem(gss, &gss->remote, ip4))
return -NS_CAUSE_UNKN_IP_EP;
/* copy over. Both data structures use network byte order */
@@ -588,7 +653,7 @@ static int do_sns_change_weight(struct osmo_fsm_inst *fi, const struct gprs_ns_i
new_signal = ip4->sig_weight;
new_data = ip4->data_weight;
} else if (ip6) {
- if (update_remote_ip6_elem(gss, ip6))
+ if (update_ip6_elem(gss, &gss->remote, ip6))
return -NS_CAUSE_UNKN_IP_EP;
/* copy over. Both data structures use network byte order */
@@ -631,14 +696,14 @@ static int do_sns_delete(struct osmo_fsm_inst *fi,
struct osmo_sockaddr sa = {};
if (ip4) {
- if (remove_remote_ip4_elem(gss, ip4) < 0)
+ if (remove_ip4_elem(gss, &gss->remote, ip4) < 0)
return -NS_CAUSE_UNKN_IP_EP;
/* copy over. Both data structures use network byte order */
sa.u.sin.sin_addr.s_addr = ip4->ip_addr;
sa.u.sin.sin_port = ip4->udp_port;
sa.u.sin.sin_family = AF_INET;
} else if (ip6) {
- if (remove_remote_ip6_elem(gss, ip6))
+ if (remove_ip6_elem(gss, &gss->remote, ip6))
return -NS_CAUSE_UNKN_IP_EP;
/* copy over. Both data structures use network byte order */
@@ -675,12 +740,18 @@ static int do_sns_add(struct osmo_fsm_inst *fi,
/* Upon receiving an SNS-ADD PDU, if the consequent number of IPv4 endpoints
* exceeds the number of IPv4 endpoints supported by the NSE, the NSE shall send
* an SNS-ACK PDU with a cause code set to "Invalid number of IP4 Endpoints". */
- switch (gss->ip) {
- case IPv4:
- rc = add_remote_ip4_elem(gss, ip4);
+ switch (gss->family) {
+ case AF_INET:
+ if (gss->remote.num_ip4 >= gss->num_max_ip4_remote)
+ return -NS_CAUSE_INVAL_NR_NS_VC;
+ /* TODO: log message duplicate */
+ rc = add_ip4_elem(gss, &gss->remote, ip4);
break;
- case IPv6:
- rc = add_remote_ip6_elem(gss, ip6);
+ case AF_INET6:
+ if (gss->remote.num_ip6 >= gss->num_max_ip6_remote)
+ return -NS_CAUSE_INVAL_NR_NS_VC;
+ /* TODO: log message duplicate */
+ rc = add_ip6_elem(gss, &gss->remote, ip6);
break;
default:
/* the gss->ip is initialized with the bss */
@@ -688,13 +759,13 @@ static int do_sns_add(struct osmo_fsm_inst *fi,
}
if (rc)
- return rc;
+ return -NS_CAUSE_PROTO_ERR_UNSPEC;
/* Upon receiving an SNS-ADD PDU containing an already configured IP endpoint the
* NSE shall send an SNS-ACK PDU with the cause code "Protocol error -
* unspecified" */
- switch (gss->ip) {
- case IPv4:
+ switch (gss->family) {
+ case AF_INET:
nsvc = nsvc_by_ip4_elem(nse, ip4);
if (nsvc) {
/* the nsvc should be already in sync with the ip4 / ip6 elements */
@@ -704,7 +775,7 @@ static int do_sns_add(struct osmo_fsm_inst *fi,
/* TODO: failure case */
ns2_nsvc_create_ip4(fi, nse, ip4);
break;
- case IPv6:
+ case AF_INET6:
nsvc = nsvc_by_ip6_elem(nse, ip6);
if (nsvc) {
/* the nsvc should be already in sync with the ip4 / ip6 elements */
@@ -739,7 +810,7 @@ static void ns2_sns_st_bss_size(struct osmo_fsm_inst *fi, uint32_t event, void *
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
switch (event) {
- case GPRS_SNS_EV_RX_SIZE_ACK:
+ case NS2_SNS_EV_RX_SIZE_ACK:
tp = data;
if (TLVP_VAL_MINLEN(tp, NS_IE_CAUSE, 1)) {
LOGPFSML(fi, LOGL_ERROR, "SNS-SIZE-ACK with cause %s\n",
@@ -755,7 +826,7 @@ static void ns2_sns_st_bss_size(struct osmo_fsm_inst *fi, uint32_t event, void *
}
}
-static int ns2_sns_count_num_local_ep(struct osmo_fsm_inst *fi, enum ns2_sns_type stype)
+static int ns2_sns_count_num_local_ep(struct osmo_fsm_inst *fi, int ip_proto)
{
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
struct ns2_sns_bind *sbind;
@@ -766,12 +837,12 @@ static int ns2_sns_count_num_local_ep(struct osmo_fsm_inst *fi, enum ns2_sns_typ
if (!sa)
continue;
- switch (stype) {
- case IPv4:
+ switch (ip_proto) {
+ case AF_INET:
if (sa->u.sas.ss_family == AF_INET)
count++;
break;
- case IPv6:
+ case AF_INET6:
if (sa->u.sas.ss_family == AF_INET6)
count++;
break;
@@ -780,6 +851,36 @@ static int ns2_sns_count_num_local_ep(struct osmo_fsm_inst *fi, enum ns2_sns_typ
return count;
}
+static int ns2_sns_copy_local_endpoints(struct ns2_sns_state *gss)
+{
+ switch (gss->family) {
+ case AF_INET:
+ gss->local_procedure.ip4 = talloc_realloc(gss, gss->local_procedure.ip4, struct gprs_ns_ie_ip4_elem,
+ gss->local.num_ip4);
+ if (!gss->local_procedure.ip4)
+ return -ENOMEM;
+
+ gss->local_procedure.num_ip4 = gss->local.num_ip4;
+ memcpy(gss->local_procedure.ip4, gss->local.ip4,
+ sizeof(struct gprs_ns_ie_ip4_elem) * gss->local.num_ip4);
+ break;
+ case AF_INET6:
+ gss->local_procedure.ip6 = talloc_realloc(gss, gss->local_procedure.ip6, struct gprs_ns_ie_ip6_elem,
+ gss->local.num_ip6);
+ if (!gss->local_procedure.ip6)
+ return -ENOMEM;
+
+ gss->local_procedure.num_ip6 = gss->local.num_ip6;
+ memcpy(gss->local_procedure.ip6, gss->local.ip6,
+ sizeof(struct gprs_ns_ie_ip6_elem) * gss->local.num_ip6);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ return 0;
+}
+
static void ns2_sns_compute_local_ep_from_binds(struct osmo_fsm_inst *fi)
{
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
@@ -792,7 +893,7 @@ static void ns2_sns_compute_local_ep_from_binds(struct osmo_fsm_inst *fi)
struct osmo_sockaddr local;
int count;
- ns2_clear_ipv46_entries_local(gss);
+ ns2_clear_elems(&gss->local);
/* no initial available */
if (gss->role == GPRS_SNS_ROLE_BSS) {
@@ -809,13 +910,13 @@ static void ns2_sns_compute_local_ep_from_binds(struct osmo_fsm_inst *fi)
return;
}
- switch (gss->ip) {
- case IPv4:
- ip4_elems = talloc_realloc(fi, gss->ip4_local, struct gprs_ns_ie_ip4_elem, count);
+ switch (gss->family) {
+ case AF_INET:
+ ip4_elems = talloc_realloc(fi, gss->local.ip4, struct gprs_ns_ie_ip4_elem, count);
if (!ip4_elems)
return;
- gss->ip4_local = ip4_elems;
+ gss->local.ip4 = ip4_elems;
llist_for_each_entry(sbind, &gss->binds, list) {
bind = sbind->bind;
sa = gprs_ns2_ip_bind_sockaddr(bind);
@@ -841,16 +942,16 @@ static void ns2_sns_compute_local_ep_from_binds(struct osmo_fsm_inst *fi)
ip4_elems++;
}
- gss->num_ip4_local = count;
- gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip4_remote * gss->num_ip4_local, 8);
+ gss->local.num_ip4 = count;
+ gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip4_remote * gss->local.num_ip4, 8);
break;
- case IPv6:
+ case AF_INET6:
/* IPv6 */
- ip6_elems = talloc_realloc(fi, gss->ip6_local, struct gprs_ns_ie_ip6_elem, count);
+ ip6_elems = talloc_realloc(fi, gss->local.ip6, struct gprs_ns_ie_ip6_elem, count);
if (!ip6_elems)
return;
- gss->ip6_local = ip6_elems;
+ gss->local.ip6 = ip6_elems;
llist_for_each_entry(sbind, &gss->binds, list) {
bind = sbind->bind;
@@ -877,10 +978,21 @@ static void ns2_sns_compute_local_ep_from_binds(struct osmo_fsm_inst *fi)
ip6_elems++;
}
- gss->num_ip6_local = count;
- gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip6_remote * gss->num_ip6_local, 8);
+ gss->local.num_ip6 = count;
+ gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip6_remote * gss->local.num_ip6, 8);
break;
}
+
+ ns2_sns_copy_local_endpoints(gss);
+}
+
+static void ns2_sns_choose_next_bind(struct ns2_sns_state *gss)
+{
+ /* take the first bind or take the next bind */
+ if (!gss->initial_bind || gss->initial_bind->list.next == &gss->binds)
+ gss->initial_bind = llist_first_entry_or_null(&gss->binds, struct ns2_sns_bind, list);
+ else
+ gss->initial_bind = llist_entry(gss->initial_bind->list.next, struct ns2_sns_bind, list);
}
/* setup all dynamic SNS settings, create a new nsvc and send the SIZE */
@@ -896,21 +1008,11 @@ static void ns2_sns_st_bss_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_s
if (old_state != GPRS_SNS_ST_BSS_SIZE)
gss->N = 0;
+ ns2_clear_procedures(gss);
gss->alive = false;
ns2_sns_compute_local_ep_from_binds(fi);
-
- /* take the first bind or take the next bind */
- if (!gss->initial_bind) {
- gss->initial_bind = llist_first_entry(&gss->binds, struct ns2_sns_bind, list);
- } else {
- if (gss->initial_bind->list.next != &gss->binds) {
- gss->initial_bind = llist_entry(gss->initial_bind->list.next, struct ns2_sns_bind, list);
- } else {
- gss->initial_bind = llist_first_entry(&gss->binds, struct ns2_sns_bind, list);
- }
- }
-
+ ns2_sns_choose_next_bind(gss);
/* setup the NSVC */
if (!gss->sns_nsvc) {
@@ -926,9 +1028,9 @@ static void ns2_sns_st_bss_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_s
}
if (gss->num_max_ip4_remote > 0)
- ns2_tx_sns_size(gss->sns_nsvc, true, gss->num_max_nsvcs, gss->num_ip4_local, -1);
+ ns2_tx_sns_size(gss->sns_nsvc, true, gss->num_max_nsvcs, gss->local.num_ip4, -1);
else
- ns2_tx_sns_size(gss->sns_nsvc, true, gss->num_max_nsvcs, -1, gss->num_ip6_local);
+ ns2_tx_sns_size(gss->sns_nsvc, true, gss->num_max_nsvcs, -1, gss->local.num_ip6);
}
static void ns2_sns_st_bss_config_bss(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -940,7 +1042,7 @@ static void ns2_sns_st_bss_config_bss(struct osmo_fsm_inst *fi, uint32_t event,
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
switch (event) {
- case GPRS_SNS_EV_RX_CONFIG_ACK:
+ case NS2_SNS_EV_RX_CONFIG_ACK:
tp = (struct tlv_parsed *) data;
if (TLVP_VAL_MINLEN(tp, NS_IE_CAUSE, 1)) {
LOGPFSML(fi, LOGL_ERROR, "SNS-CONFIG-ACK with cause %s\n",
@@ -965,16 +1067,16 @@ static void ns2_sns_st_bss_config_bss_onenter(struct osmo_fsm_inst *fi, uint32_t
gss->N = 0;
/* Transmit SNS-CONFIG */
- switch (gss->ip) {
- case IPv4:
+ switch (gss->family) {
+ case AF_INET:
ns2_tx_sns_config(gss->sns_nsvc, true,
- gss->ip4_local, gss->num_ip4_local,
+ gss->local.ip4, gss->local.num_ip4,
NULL, 0);
break;
- case IPv6:
+ case AF_INET6:
ns2_tx_sns_config(gss->sns_nsvc, true,
NULL, 0,
- gss->ip6_local, gss->num_ip6_local);
+ gss->local.ip6, gss->local.num_ip6);
break;
}
}
@@ -1003,19 +1105,19 @@ static int ns_sns_append_remote_eps(struct osmo_fsm_inst *fi, const struct tlv_p
v4_list = (const struct gprs_ns_ie_ip4_elem *) TLVP_VAL(tp, NS_IE_IPv4_LIST);
num_v4 = TLVP_LEN(tp, NS_IE_IPv4_LIST) / sizeof(*v4_list);
- if (num_v4 && gss->ip6_remote)
+ if (num_v4 && gss->remote.ip6)
return -NS_CAUSE_INVAL_NR_IPv4_EP;
/* realloc to the new size */
- gss->ip4_remote = talloc_realloc(gss, gss->ip4_remote,
+ gss->remote.ip4 = talloc_realloc(gss, gss->remote.ip4,
struct gprs_ns_ie_ip4_elem,
- gss->num_ip4_remote + num_v4);
+ gss->remote.num_ip4 + num_v4);
/* append the new entries to the end of the list */
- memcpy(&gss->ip4_remote[gss->num_ip4_remote], v4_list, num_v4*sizeof(*v4_list));
- gss->num_ip4_remote += num_v4;
+ memcpy(&gss->remote.ip4[gss->remote.num_ip4], v4_list, num_v4*sizeof(*v4_list));
+ gss->remote.num_ip4 += num_v4;
LOGPFSML(fi, LOGL_INFO, "Rx SNS-CONFIG: Remote IPv4 list now %u entries\n",
- gss->num_ip4_remote);
+ gss->remote.num_ip4);
}
if (TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
@@ -1024,19 +1126,19 @@ static int ns_sns_append_remote_eps(struct osmo_fsm_inst *fi, const struct tlv_p
v6_list = (const struct gprs_ns_ie_ip6_elem *) TLVP_VAL(tp, NS_IE_IPv6_LIST);
num_v6 = TLVP_LEN(tp, NS_IE_IPv6_LIST) / sizeof(*v6_list);
- if (num_v6 && gss->ip4_remote)
+ if (num_v6 && gss->remote.ip4)
return -NS_CAUSE_INVAL_NR_IPv6_EP;
/* realloc to the new size */
- gss->ip6_remote = talloc_realloc(gss, gss->ip6_remote,
+ gss->remote.ip6 = talloc_realloc(gss, gss->remote.ip6,
struct gprs_ns_ie_ip6_elem,
- gss->num_ip6_remote + num_v6);
+ gss->remote.num_ip6 + num_v6);
/* append the new entries to the end of the list */
- memcpy(&gss->ip6_remote[gss->num_ip6_remote], v6_list, num_v6*sizeof(*v6_list));
- gss->num_ip6_remote += num_v6;
+ memcpy(&gss->remote.ip6[gss->remote.num_ip6], v6_list, num_v6*sizeof(*v6_list));
+ gss->remote.num_ip6 += num_v6;
- LOGPFSML(fi, LOGL_INFO, "Rx SNS-CONFIG: Remote IPv6 list now %u entries\n",
- gss->num_ip6_remote);
+ LOGPFSML(fi, LOGL_INFO, "Rx SNS-CONFIG: Remote IPv6 list now %d entries\n",
+ gss->remote.num_ip6);
}
return 0;
@@ -1062,8 +1164,8 @@ static void ns2_sns_st_bss_config_sgsn(struct osmo_fsm_inst *fi, uint32_t event,
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
switch (event) {
- case GPRS_SNS_EV_RX_CONFIG_END:
- case GPRS_SNS_EV_RX_CONFIG:
+ case NS2_SNS_EV_RX_CONFIG_END:
+ case NS2_SNS_EV_RX_CONFIG:
rc = ns_sns_append_remote_eps(fi, data);
if (rc < 0) {
cause = -rc;
@@ -1071,9 +1173,9 @@ static void ns2_sns_st_bss_config_sgsn(struct osmo_fsm_inst *fi, uint32_t event,
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
return;
}
- if (event == GPRS_SNS_EV_RX_CONFIG_END) {
+ if (event == NS2_SNS_EV_RX_CONFIG_END) {
/* check if sum of data / sig weights == 0 */
- if (nss_weight_sum_data(gss) == 0 || nss_weight_sum_sig(gss) == 0) {
+ if (ip46_weight_sum_data(&gss->remote) == 0 || ip46_weight_sum_sig(&gss->remote) == 0) {
cause = NS_CAUSE_INVAL_WEIGH;
ns2_tx_sns_config_ack(gss->sns_nsvc, &cause);
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
@@ -1095,7 +1197,7 @@ static void ns2_sns_st_bss_config_sgsn(struct osmo_fsm_inst *fi, uint32_t event,
}
}
-/* called when receiving GPRS_SNS_EV_RX_ADD in state configure */
+/* called when receiving NS2_SNS_EV_RX_ADD in state configure */
static void ns2_sns_st_configured_add(struct osmo_fsm_inst *fi,
struct ns2_sns_state *gss,
struct tlv_parsed *tp)
@@ -1114,7 +1216,7 @@ static void ns2_sns_st_configured_add(struct osmo_fsm_inst *fi,
*/
trans_id = *TLVP_VAL(tp, NS_IE_TRANS_ID);
- if (gss->ip == IPv4) {
+ if (gss->family == AF_INET) {
if (!TLVP_PRESENT(tp, NS_IE_IPv4_LIST)) {
cause = NS_CAUSE_INVAL_NR_IPv4_EP;
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
@@ -1178,7 +1280,7 @@ static void ns2_sns_st_configured_delete(struct osmo_fsm_inst *fi,
* TODO: check if IPv4_LIST/IPv6_LIST and IP_ADDR is present at the same time
*/
trans_id = *TLVP_VAL(tp, NS_IE_TRANS_ID);
- if (gss->ip == IPv4) {
+ if (gss->family == AF_INET) {
if (TLVP_PRESENT(tp, NS_IE_IPv4_LIST)) {
v4_list = (const struct gprs_ns_ie_ip4_elem *) TLVP_VAL(tp, NS_IE_IPv4_LIST);
num_v4 = TLVP_LEN(tp, NS_IE_IPv4_LIST) / sizeof(*v4_list);
@@ -1206,9 +1308,9 @@ static void ns2_sns_st_configured_delete(struct osmo_fsm_inst *fi,
return;
}
/* make a copy as do_sns_delete() will change the array underneath us */
- ip4_remote = talloc_memdup(fi, gss->ip4_remote,
- gss->num_ip4_remote * sizeof(*v4_list));
- for (i = 0; i < gss->num_ip4_remote; i++) {
+ ip4_remote = talloc_memdup(fi, gss->remote.ip4,
+ gss->remote.num_ip4 * sizeof(*v4_list));
+ for (i = 0; i < gss->remote.num_ip4; i++) {
if (ip4_remote[i].ip_addr == ip_addr) {
rc = do_sns_delete(fi, &ip4_remote[i], NULL);
if (rc < 0) {
@@ -1257,9 +1359,9 @@ static void ns2_sns_st_configured_delete(struct osmo_fsm_inst *fi,
}
memcpy(&ip6_addr, (ie+1), sizeof(struct in6_addr));
/* make a copy as do_sns_delete() will change the array underneath us */
- ip6_remote = talloc_memdup(fi, gss->ip6_remote,
- gss->num_ip6_remote * sizeof(*v4_list));
- for (i = 0; i < gss->num_ip6_remote; i++) {
+ ip6_remote = talloc_memdup(fi, gss->remote.ip6,
+ gss->remote.num_ip6 * sizeof(*v4_list));
+ for (i = 0; i < gss->remote.num_ip6; i++) {
if (!memcmp(&ip6_remote[i].ip_addr, &ip6_addr, sizeof(struct in6_addr))) {
rc = do_sns_delete(fi, NULL, &ip6_remote[i]);
if (rc < 0) {
@@ -1338,16 +1440,16 @@ static void ns2_sns_st_configured(struct osmo_fsm_inst *fi, uint32_t event, void
struct tlv_parsed *tp = data;
switch (event) {
- case GPRS_SNS_EV_RX_ADD:
+ case NS2_SNS_EV_RX_ADD:
ns2_sns_st_configured_add(fi, gss, tp);
break;
- case GPRS_SNS_EV_RX_DELETE:
+ case NS2_SNS_EV_RX_DELETE:
ns2_sns_st_configured_delete(fi, gss, tp);
break;
- case GPRS_SNS_EV_RX_CHANGE_WEIGHT:
+ case NS2_SNS_EV_RX_CHANGE_WEIGHT:
ns2_sns_st_configured_change(fi, gss, tp);
break;
- case GPRS_SNS_EV_REQ_NSVC_ALIVE:
+ case NS2_SNS_EV_REQ_NSVC_ALIVE:
osmo_timer_del(&fi->timer);
break;
}
@@ -1372,7 +1474,231 @@ static void ns2_sns_st_configured_onenter(struct osmo_fsm_inst *fi, uint32_t old
if (gss->sns_nsvc->sns_only)
gprs_ns2_free_nsvc(gss->sns_nsvc);
- ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED);
+ if (old_state != GPRS_SNS_ST_LOCAL_PROCEDURE)
+ ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED);
+
+ if (!llist_empty(&gss->procedures)) {
+ osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,
+ gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);
+ }
+}
+
+static void ns2_sns_st_local_procedure_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+
+ /* check if resend or not */
+ if (!gss->current_procedure) {
+ /* take next procedure */
+ gss->current_procedure = llist_first_entry_or_null(&gss->procedures,
+ struct ns2_sns_procedure, list);
+ if (!gss->current_procedure) {
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIGURED, 0, 0);
+ return;
+ }
+ gss->N = 0;
+ gss->current_procedure->running = true;
+ gss->current_procedure->trans_id = ++gss->trans_id;
+ if (gss->trans_id == 0)
+ gss->trans_id = gss->current_procedure->trans_id = 1;
+
+ }
+
+ /* also takes care of retransmitting */
+ switch (gss->current_procedure->procedure) {
+ case SNS_PROC_ADD:
+ if (gss->family == AF_INET)
+ ns2_tx_sns_add(gss->sns_nsvc, gss->current_procedure->trans_id, &gss->current_procedure->ip4, 1, NULL, 0);
+ else
+ ns2_tx_sns_add(gss->sns_nsvc, gss->current_procedure->trans_id, NULL, 0, &gss->current_procedure->ip6, 1);
+ break;
+ case SNS_PROC_CHANGE_WEIGHT:
+ if (gss->family == AF_INET)
+ ns2_tx_sns_change_weight(gss->sns_nsvc, gss->current_procedure->trans_id, &gss->current_procedure->ip4, 1, NULL, 0);
+ else
+ ns2_tx_sns_change_weight(gss->sns_nsvc, gss->current_procedure->trans_id, NULL, 0, &gss->current_procedure->ip6, 1);
+ break;
+ case SNS_PROC_DEL:
+ if (gss->family == AF_INET)
+ ns2_tx_sns_del(gss->sns_nsvc, gss->current_procedure->trans_id, &gss->current_procedure->ip4, 1, NULL, 0);
+ else
+ ns2_tx_sns_del(gss->sns_nsvc, gss->current_procedure->trans_id, NULL, 0, &gss->current_procedure->ip6, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+static void create_nsvc_for_new_sbind(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind)
+{
+ struct gprs_ns2_nse *nse = gss->nse;
+ struct gprs_ns2_vc_bind *bind = sbind->bind;
+ struct gprs_ns2_vc *nsvc;
+ struct osmo_sockaddr remote = { };
+ unsigned int i;
+
+ /* iterate over all remote IPv4 endpoints */
+ for (i = 0; i < gss->remote.num_ip4; i++) {
+ const struct gprs_ns_ie_ip4_elem *ip4 = &gss->remote.ip4[i];
+
+ remote.u.sin.sin_family = AF_INET;
+ remote.u.sin.sin_addr.s_addr = ip4->ip_addr;
+ remote.u.sin.sin_port = ip4->udp_port;
+ /* we only care about UDP binds */
+ if (bind->ll != GPRS_NS2_LL_UDP)
+ continue;
+
+ nsvc = nsvc_for_bind_and_remote(nse, bind, &remote);
+ if (!nsvc) {
+ nsvc = gprs_ns2_ip_connect_inactive(bind, &remote, nse, 0);
+ if (!nsvc) {
+ /* TODO: add to a list to send back a NS-STATUS */
+ continue;
+ }
+ }
+
+ /* update data / signalling weight */
+ nsvc->data_weight = ip4->data_weight;
+ nsvc->sig_weight = ip4->sig_weight;
+ nsvc->sns_only = false;
+ }
+
+ /* iterate over all remote IPv4 endpoints */
+ for (i = 0; i < gss->remote.num_ip6; i++) {
+ const struct gprs_ns_ie_ip6_elem *ip6 = &gss->remote.ip6[i];
+
+ remote.u.sin6.sin6_family = AF_INET6;
+ remote.u.sin6.sin6_addr = ip6->ip_addr;
+ remote.u.sin6.sin6_port = ip6->udp_port;
+
+ /* we only care about UDP binds */
+ nsvc = nsvc_for_bind_and_remote(nse, bind, &remote);
+ if (!nsvc) {
+ nsvc = gprs_ns2_ip_connect_inactive(bind, &remote, nse, 0);
+ if (!nsvc) {
+ /* TODO: add to a list to send back a NS-STATUS */
+ continue;
+ }
+ }
+
+ /* update data / signalling weight */
+ nsvc->data_weight = ip6->data_weight;
+ nsvc->sig_weight = ip6->sig_weight;
+ nsvc->sns_only = false;
+ }
+}
+
+static void ns2_sns_st_local_procedure(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct gprs_ns_ie_ip4_elem *ip4, *proc4;
+ struct gprs_ns_ie_ip6_elem *ip6, *proc6;
+ struct tlv_parsed *tp = data;
+ uint8_t trans_id;
+ uint8_t cause;
+
+ switch (event) {
+ case NS2_SNS_EV_RX_ADD:
+ ns2_sns_st_configured_add(fi, gss, tp);
+ break;
+ case NS2_SNS_EV_RX_DELETE:
+ ns2_sns_st_configured_delete(fi, gss, tp);
+ break;
+ case NS2_SNS_EV_RX_CHANGE_WEIGHT:
+ ns2_sns_st_configured_change(fi, gss, tp);
+ break;
+ case NS2_SNS_EV_RX_ACK:
+ /* presence of trans_id is already checked here */
+ trans_id = tlvp_val8(tp, NS_IE_TRANS_ID, 0);
+ if (trans_id != gss->current_procedure->trans_id) {
+ LOGPFSML(fi, LOGL_INFO, "NSEI=%u Rx SNS ACK with invalid transaction id %d. Valid %d\n",
+ nse->nsei, trans_id, gss->current_procedure->trans_id);
+ break;
+ }
+
+ if (TLVP_PRESENT(tp, NS_IE_CAUSE)) {
+ /* what happend on error cause? return to size? */
+ cause = tlvp_val8(tp, NS_IE_CAUSE, 0);
+ LOGPFSML(fi, LOGL_ERROR, "NSEI=%u Rx SNS ACK trans %d with cause code %d.\n",
+ nse->nsei, trans_id, cause);
+ sns_failed(fi, NULL);
+ break;
+ }
+
+ switch (gss->current_procedure->procedure) {
+ case SNS_PROC_ADD:
+ switch (gss->family) {
+ case AF_INET:
+ add_ip4_elem(gss, &gss->local, &gss->current_procedure->ip4);
+ break;
+ case AF_INET6:
+ add_ip6_elem(gss, &gss->local, &gss->current_procedure->ip6);
+ break;
+ }
+ /* the sbind can be NULL if the bind has been released by del_bind */
+ if (gss->current_procedure->sbind) {
+ create_nsvc_for_new_sbind(gss, gss->current_procedure->sbind);
+ gprs_ns2_start_alive_all_nsvcs(nse);
+ }
+ break;
+ case SNS_PROC_CHANGE_WEIGHT:
+ switch (gss->family) {
+ case AF_INET:
+ proc4 = &gss->current_procedure->ip4;
+ for (unsigned int i=0; i<gss->local.num_ip4; i++) {
+ ip4 = &gss->local.ip4[i];
+ if (ip4->ip_addr != proc4->ip_addr ||
+ ip4->udp_port != proc4->udp_port)
+ continue;
+ ip4->sig_weight = proc4->sig_weight;
+ ip4->data_weight = proc4->data_weight;
+ break;
+ }
+ break;
+ case AF_INET6:
+ proc6 = &gss->current_procedure->ip6;
+ for (unsigned int i=0; i<gss->local.num_ip6; i++) {
+ ip6 = &gss->local.ip6[i];
+ if (memcmp(&ip6->ip_addr, &proc6->ip_addr, sizeof(proc6->ip_addr)) ||
+ ip6->udp_port != proc6->udp_port) {
+ continue;
+ }
+ ip6->sig_weight = proc6->sig_weight;
+ ip6->data_weight = proc6->data_weight;
+ break;
+ }
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ break;
+ case SNS_PROC_DEL:
+ switch (gss->family) {
+ case AF_INET:
+ remove_ip4_elem(gss, &gss->local, &gss->current_procedure->ip4);
+ break;
+ case AF_INET6:
+ remove_ip6_elem(gss, &gss->local, &gss->current_procedure->ip6);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ llist_del(&gss->current_procedure->list);
+ talloc_free(gss->current_procedure);
+ gss->current_procedure = NULL;
+
+ if (llist_empty(&gss->procedures))
+ osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_CONFIGURED,
+ 0, 0);
+ else
+ osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,
+ gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);
+ break;
+ }
}
static const struct osmo_fsm_state ns2_sns_bss_states[] = {
@@ -1384,7 +1710,7 @@ static const struct osmo_fsm_state ns2_sns_bss_states[] = {
.action = ns2_sns_st_bss_unconfigured,
},
[GPRS_SNS_ST_BSS_SIZE] = {
- .in_event_mask = S(GPRS_SNS_EV_RX_SIZE_ACK),
+ .in_event_mask = S(NS2_SNS_EV_RX_SIZE_ACK),
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
S(GPRS_SNS_ST_BSS_SIZE) |
S(GPRS_SNS_ST_BSS_CONFIG_BSS),
@@ -1393,7 +1719,7 @@ static const struct osmo_fsm_state ns2_sns_bss_states[] = {
.onenter = ns2_sns_st_bss_size_onenter,
},
[GPRS_SNS_ST_BSS_CONFIG_BSS] = {
- .in_event_mask = S(GPRS_SNS_EV_RX_CONFIG_ACK),
+ .in_event_mask = S(NS2_SNS_EV_RX_CONFIG_ACK),
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
S(GPRS_SNS_ST_BSS_CONFIG_BSS) |
S(GPRS_SNS_ST_BSS_CONFIG_SGSN) |
@@ -1403,8 +1729,8 @@ static const struct osmo_fsm_state ns2_sns_bss_states[] = {
.onenter = ns2_sns_st_bss_config_bss_onenter,
},
[GPRS_SNS_ST_BSS_CONFIG_SGSN] = {
- .in_event_mask = S(GPRS_SNS_EV_RX_CONFIG) |
- S(GPRS_SNS_EV_RX_CONFIG_END),
+ .in_event_mask = S(NS2_SNS_EV_RX_CONFIG) |
+ S(NS2_SNS_EV_RX_CONFIG_END),
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
S(GPRS_SNS_ST_BSS_CONFIG_SGSN) |
S(GPRS_SNS_ST_CONFIGURED) |
@@ -1414,16 +1740,32 @@ static const struct osmo_fsm_state ns2_sns_bss_states[] = {
.onenter = ns2_sns_st_bss_config_sgsn_onenter,
},
[GPRS_SNS_ST_CONFIGURED] = {
- .in_event_mask = S(GPRS_SNS_EV_RX_ADD) |
- S(GPRS_SNS_EV_RX_DELETE) |
- S(GPRS_SNS_EV_RX_CHANGE_WEIGHT) |
- S(GPRS_SNS_EV_REQ_NSVC_ALIVE),
+ .in_event_mask = S(NS2_SNS_EV_RX_ADD) |
+ S(NS2_SNS_EV_RX_DELETE) |
+ S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_REQ_NSVC_ALIVE),
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
- S(GPRS_SNS_ST_BSS_SIZE),
+ S(GPRS_SNS_ST_BSS_SIZE) |
+ S(GPRS_SNS_ST_LOCAL_PROCEDURE),
.name = "CONFIGURED",
.action = ns2_sns_st_configured,
.onenter = ns2_sns_st_configured_onenter,
},
+ [GPRS_SNS_ST_LOCAL_PROCEDURE] = {
+ .in_event_mask = S(NS2_SNS_EV_RX_ADD) |
+ S(NS2_SNS_EV_RX_DELETE) |
+ S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_RX_ACK) |
+ S(NS2_SNS_EV_REQ_NSVC_ALIVE),
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_BSS_SIZE) |
+ S(GPRS_SNS_ST_CONFIGURED) |
+ S(GPRS_SNS_ST_LOCAL_PROCEDURE),
+ .name = "LOCAL_PROCEDURE",
+ .action = ns2_sns_st_local_procedure,
+ .onenter = ns2_sns_st_local_procedure_onenter,
+ },
+
};
static int ns2_sns_fsm_bss_timer_cb(struct osmo_fsm_inst *fi)
@@ -1436,89 +1778,421 @@ static int ns2_sns_fsm_bss_timer_cb(struct osmo_fsm_inst *fi)
switch (fi->T) {
case 1:
if (gss->N >= nsi->timeout[NS_TOUT_TSNS_SIZE_RETRIES]) {
- LOGPFSML(fi, LOGL_ERROR, "NSE %d: Size retries failed. Selecting next IP-SNS endpoint.\n", nse->nsei);
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
+ sns_failed(fi, "Size retries failed. Selecting next IP-SNS endpoint.");
} else {
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_SIZE, nsi->timeout[NS_TOUT_TSNS_PROV], 1);
}
break;
case 2:
if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) {
- LOGPFSML(fi, LOGL_ERROR, "NSE %d: BSS Config retries failed. Selecting next IP-SNS endpoint.\n", nse->nsei);
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
+ sns_failed(fi, "BSS Config retries failed. Selecting next IP-SNS endpoint");
} else {
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_CONFIG_BSS, nsi->timeout[NS_TOUT_TSNS_PROV], 2);
}
break;
case 3:
if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) {
- LOGPFSML(fi, LOGL_ERROR, "NSE %d: SGSN Config retries failed. Selecting next IP-SNS endpoint.\n", nse->nsei);
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
+ sns_failed(fi, "SGSN Config retries failed. Selecting next IP-SNS endpoint.");
} else {
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_CONFIG_SGSN, nsi->timeout[NS_TOUT_TSNS_PROV], 3);
}
break;
case 4:
- LOGPFSML(fi, LOGL_ERROR, "NSE %d: Config succeeded but no NS-VC came online. Selecting next IP-SNS endpoint.\n", nse->nsei);
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
+ sns_failed(fi, "Config succeeded but no NS-VC came online. Selecting next IP-SNS endpoint.");
+ break;
+ case 5:
+ if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) {
+ sns_failed(fi, "SNS Procedure retries failed.");
+ } else {
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV], 5);
+ }
break;
}
return 0;
}
+static struct gprs_ns_ie_ip4_elem *ns2_get_sbind_ip4_entry(struct ns2_sns_state *gss,
+ struct ns2_sns_bind *sbind,
+ struct ns2_sns_elems *endpoints)
+{
+ const struct osmo_sockaddr *addr;
+ struct gprs_ns_ie_ip4_elem *ip4;
+
+ if (gss->family != AF_INET)
+ return NULL;
+
+ addr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+ if (addr->u.sa.sa_family != AF_INET)
+ return NULL;
+
+ for (unsigned int i=0; i<endpoints->num_ip4; i++) {
+ ip4 = &endpoints->ip4[i];
+ if (ip4->ip_addr == addr->u.sin.sin_addr.s_addr &&
+ ip4->udp_port == addr->u.sin.sin_port)
+ return ip4;
+ }
+
+ return NULL;
+}
+
+static struct gprs_ns_ie_ip6_elem *ns2_get_sbind_ip6_entry(struct ns2_sns_state *gss,
+ struct ns2_sns_bind *sbind,
+ struct ns2_sns_elems *endpoints)
+{
+ const struct osmo_sockaddr *addr;
+ struct gprs_ns_ie_ip6_elem *ip6;
+
+ if (gss->family != AF_INET6)
+ return NULL;
+
+ addr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+ if (addr->u.sa.sa_family != AF_INET6)
+ return NULL;
+
+ for (unsigned int i=0; i<endpoints->num_ip6; i++) {
+ ip6 = &endpoints->ip6[i];
+ if (memcmp(&ip6->ip_addr, &addr->u.sin6.sin6_addr, sizeof(ip6->ip_addr)) ||
+ ip6->udp_port != addr->u.sin6.sin6_port)
+ return ip6;
+ }
+
+ return NULL;
+}
+
+/* return != 0 if the resulting weight is invalid. return 1 if sbind doesn't have an entry */
+static int ns2_update_weight_entry(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind,
+ struct ns2_sns_elems *endpoints)
+{
+ struct gprs_ns_ie_ip4_elem *ip4;
+ struct gprs_ns_ie_ip6_elem *ip6;
+
+ switch (gss->family) {
+ case AF_INET:
+ ip4 = ns2_get_sbind_ip4_entry(gss, sbind, endpoints);
+ if (!ip4)
+ return 1;
+ ip4->sig_weight = sbind->bind->sns_sig_weight;
+ ip4->data_weight = sbind->bind->sns_data_weight;
+ return (ip4_weight_sum_sig(endpoints) != 0 && ip4_weight_sum_data(endpoints) != 0);
+ break;
+ case AF_INET6:
+ ip6 = ns2_get_sbind_ip6_entry(gss, sbind, endpoints);
+ if (!ip6)
+ return 1;
+ ip6->sig_weight = sbind->bind->sns_sig_weight;
+ ip6->data_weight = sbind->bind->sns_data_weight;
+ return (ip6_weight_sum_sig(endpoints) != 0 && ip6_weight_sum_data(endpoints) != 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+static void ns2_add_procedure(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind,
+ enum sns_procedure procedure_type)
+{
+ struct ns2_sns_procedure *procedure = NULL;
+ const struct osmo_sockaddr *saddr;
+ saddr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+
+ OSMO_ASSERT(saddr->u.sa.sa_family == gss->family);
+
+ switch (procedure_type) {
+ case SNS_PROC_ADD:
+ break;
+ case SNS_PROC_DEL:
+ break;
+ case SNS_PROC_CHANGE_WEIGHT:
+ llist_for_each_entry(procedure, &gss->procedures, list) {
+ if (procedure->sbind == sbind && procedure->procedure == procedure_type &&
+ !procedure->running) {
+ switch(gss->family) {
+ case AF_INET:
+ /* merge it with a previous procedure */
+ procedure->ip4.ip_addr = sbind->bind->sns_sig_weight;
+ procedure->ip4.data_weight = sbind->bind->sns_data_weight;
+ break;
+ case AF_INET6:
+ /* merge it with a previous procedure */
+ procedure->ip6.sig_weight = sbind->bind->sns_sig_weight;
+ procedure->ip6.data_weight = sbind->bind->sns_data_weight;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ return;
+ }
+ }
+ break;
+ default:
+ return;
+ }
+
+ procedure = talloc_zero(gss, struct ns2_sns_procedure);
+ if (!procedure)
+ return;
+
+ switch (procedure_type) {
+ case SNS_PROC_ADD:
+ case SNS_PROC_CHANGE_WEIGHT:
+ procedure->sbind = sbind;
+ break;
+ default:
+ break;
+ }
+
+ llist_add_tail(&procedure->list, &gss->procedures);
+ procedure->procedure = procedure_type;
+ procedure->sig_weight = sbind->bind->sns_sig_weight;
+ procedure->data_weight = sbind->bind->sns_data_weight;
+
+ switch(gss->family) {
+ case AF_INET:
+ procedure->ip4.ip_addr = saddr->u.sin.sin_addr.s_addr;
+ procedure->ip4.udp_port = saddr->u.sin.sin_port;
+ procedure->ip4.sig_weight = sbind->bind->sns_sig_weight;
+ procedure->ip4.data_weight = sbind->bind->sns_data_weight;
+ break;
+ case AF_INET6:
+ memcpy(&procedure->ip6.ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr));
+ procedure->ip6.udp_port = saddr->u.sin.sin_port;
+ procedure->ip6.sig_weight = sbind->bind->sns_sig_weight;
+ procedure->ip6.data_weight = sbind->bind->sns_data_weight;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ if (gss->nse->bss_sns_fi->state == GPRS_SNS_ST_CONFIGURED) {
+ osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,
+ gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);
+ }
+}
+
+/* add an entrypoint to sns_endpoints */
+static int ns2_sns_add_elements(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind,
+ struct ns2_sns_elems *elems)
+{
+ const struct osmo_sockaddr *saddr;
+ struct gprs_ns_ie_ip4_elem ip4;
+ struct gprs_ns_ie_ip6_elem ip6;
+ int rc = -1;
+
+ saddr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+ OSMO_ASSERT(saddr->u.sa.sa_family == gss->family);
+
+ switch (gss->family) {
+ case AF_INET:
+ ip4.ip_addr = saddr->u.sin.sin_addr.s_addr;
+ ip4.udp_port= saddr->u.sin.sin_port;
+ ip4.sig_weight = sbind->bind->sns_sig_weight;
+ ip4.data_weight = sbind->bind->sns_data_weight;
+ rc = add_ip4_elem(gss, elems, &ip4);
+ break;
+ case AF_INET6:
+ memcpy(&ip6.ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr));
+ ip6.udp_port= saddr->u.sin.sin_port;
+ ip6.sig_weight = sbind->bind->sns_sig_weight;
+ ip6.data_weight = sbind->bind->sns_data_weight;
+ rc = add_ip6_elem(gss, elems, &ip6);
+ break;
+ }
+
+ return rc;
+}
+
/* common allstate-action for both roles */
static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
struct ns2_sns_bind *sbind;
struct gprs_ns2_vc *nsvc, *nsvc2;
+ struct ns2_sns_procedure *procedure;
switch (event) {
- case GPRS_SNS_EV_REQ_ADD_BIND:
+ case NS2_SNS_EV_REQ_ADD_BIND:
sbind = data;
switch (fi->state) {
case GPRS_SNS_ST_UNCONFIGURED:
- osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
+ if (gss->role == GPRS_SNS_ROLE_BSS)
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
break;
case GPRS_SNS_ST_BSS_SIZE:
- /* TODO: add the ip4 element to the list */
+ switch (gss->family) {
+ case AF_INET:
+ if (gss->num_max_ip4_remote <= gss->local.num_ip4 ||
+ gss->num_max_ip4_remote * (gss->local.num_ip4 + 1) > gss->num_max_nsvcs) {
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, GPRS_SNS_FLAG_KEEP_SELECT_ENDPOINT_ORDER);
+ return;
+ }
+ break;
+ case AF_INET6:
+ if (gss->num_max_ip6_remote <= gss->local.num_ip6 ||
+ gss->num_max_ip6_remote * (gss->local.num_ip6 + 1) > gss->num_max_nsvcs) {
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, GPRS_SNS_FLAG_KEEP_SELECT_ENDPOINT_ORDER);
+ return;
+ }
+ break;
+ }
+ ns2_sns_add_elements(gss, sbind, &gss->local);
break;
case GPRS_SNS_ST_BSS_CONFIG_BSS:
case GPRS_SNS_ST_BSS_CONFIG_SGSN:
case GPRS_SNS_ST_CONFIGURED:
- /* TODO: add to SNS-IP procedure queue & add nsvc() */
+ switch (gss->family) {
+ case AF_INET:
+ if (gss->num_max_ip4_remote <= gss->local.num_ip4) {
+ LOGPFSML(fi, LOGL_ERROR,
+ "NSE %d: ignoring bind %s because there are too many endpoints for the SNS.\n",
+ nse->nsei, sbind->bind->name);
+ return;
+ }
+ if (gss->remote.num_ip4 * (gss->local.num_ip4 + 1) > gss->num_max_nsvcs) {
+ LOGPFSML(fi, LOGL_ERROR,
+ "NSE %d: ignoring bind %s because there are too many endpoints for the SNS.\n",
+ nse->nsei, sbind->bind->name);
+ return;
+ }
+ break;
+ case AF_INET6:
+ if (gss->num_max_ip6_remote <= gss->local.num_ip6) {
+ LOGPFSML(fi, LOGL_ERROR,
+ "NSE %d: ignoring bind %s because there are too many endpoints for the SNS.\n",
+ nse->nsei, sbind->bind->name);
+ return;
+ }
+ if (gss->remote.num_ip6 * (gss->local.num_ip6 + 1) > gss->num_max_nsvcs) {
+ LOGPFSML(fi, LOGL_ERROR,
+ "NSE %d: ignoring bind %s because there are too many endpoints for the SNS.\n",
+ nse->nsei, sbind->bind->name);
+ return;
+ }
+ break;
+ }
+ ns2_sns_add_elements(gss, sbind, &gss->local_procedure);
+ ns2_add_procedure(gss, sbind, SNS_PROC_ADD);
break;
}
break;
- case GPRS_SNS_EV_REQ_DELETE_BIND:
+ case NS2_SNS_EV_REQ_DELETE_BIND:
sbind = data;
switch (fi->state) {
case GPRS_SNS_ST_UNCONFIGURED:
break;
case GPRS_SNS_ST_BSS_SIZE:
- /* TODO: remove the ip4 element from the list */
llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, list) {
if (nsvc->bind == sbind->bind) {
gprs_ns2_free_nsvc(nsvc);
}
}
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
break;
case GPRS_SNS_ST_BSS_CONFIG_BSS:
case GPRS_SNS_ST_BSS_CONFIG_SGSN:
case GPRS_SNS_ST_CONFIGURED:
- /* TODO: do an delete SNS-IP procedure */
- /* TODO: remove the ip4 element to the list */
+ case GPRS_SNS_ST_LOCAL_PROCEDURE:
+ remove_bind_elem(gss, &gss->local_procedure, sbind);
+ if (ip46_weight_sum(&gss->local_procedure, true) == 0 ||
+ ip46_weight_sum(&gss->local_procedure, false) == 0) {
+ LOGPFSML(fi, LOGL_ERROR, "NSE %d: weight has become invalid because of removing bind %s. Resetting the configuration\n",
+ nse->nsei, sbind->bind->name);
+ sns_failed(fi, NULL);
+ break;
+ }
+ gss->block_no_nsvc_events = true;
llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, list) {
if (nsvc->bind == sbind->bind) {
gprs_ns2_free_nsvc(nsvc);
}
}
+ gss->block_no_nsvc_events = false;
+ if (nse->sum_sig_weight == 0 || !nse->alive || !gss->alive) {
+ sns_failed(fi, "While deleting a bind the current state became invalid (no signalling weight)");
+ break;
+ }
+
+ /* ensure other procedures doesn't use the sbind */
+ llist_for_each_entry(procedure, &gss->procedures, list) {
+ if (procedure->sbind == sbind)
+ procedure->sbind = NULL;
+ }
+ ns2_add_procedure(gss, sbind, SNS_PROC_DEL);
break;
}
+
/* if this is the last bind, the free_nsvc() will trigger a reselection */
talloc_free(sbind);
break;
+ case NS2_SNS_EV_REQ_CHANGE_WEIGHT:
+ sbind = data;
+ switch (fi->state) {
+ case GPRS_SNS_ST_UNCONFIGURED:
+ /* select_endpoint will check if this is a valid configuration */
+ if (gss->role == GPRS_SNS_ROLE_BSS)
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
+ break;
+ case GPRS_SNS_ST_BSS_SIZE:
+ /* invalid weight? */
+ if (!ns2_update_weight_entry(gss, sbind, &gss->local))
+ sns_failed(fi, "updating weights results in an invalid configuration.");
+ break;
+ default:
+ if (!ns2_update_weight_entry(gss, sbind, &gss->local_procedure)) {
+ sns_failed(fi, "updating weights results in an invalid configuration.");
+ break;
+ }
+ ns2_add_procedure(gss, sbind, SNS_PROC_CHANGE_WEIGHT);
+ break;
+ }
+ }
+}
+
+/* validate the bss configuration (sns endpoint and binds)
+ * - no endpoints -> invalid
+ * - no binds -> invalid
+ * - only v4 sns endpoints, only v6 binds -> invalid
+ * - only v4 sns endpoints, but v4 sig weights == 0 -> invalid ...
+ */
+static int ns2_sns_bss_valid_configuration(struct ns2_sns_state *gss)
+{
+ struct ns2_sns_bind *sbind;
+ struct sns_endpoint *endpoint;
+ const struct osmo_sockaddr *addr;
+ int v4_sig = 0, v4_data = 0, v6_sig = 0, v6_data = 0;
+ bool v4_endpoints = false;
+ bool v6_endpoints = false;
+
+ if (llist_empty(&gss->sns_endpoints) || llist_empty(&gss->binds))
+ return 0;
+
+ llist_for_each_entry(sbind, &gss->binds, list) {
+ addr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+ if (!addr)
+ continue;
+ switch (addr->u.sa.sa_family) {
+ case AF_INET:
+ v4_sig += sbind->bind->sns_sig_weight;
+ v4_data += sbind->bind->sns_data_weight;
+ break;
+ case AF_INET6:
+ v6_sig += sbind->bind->sns_sig_weight;
+ v6_data += sbind->bind->sns_data_weight;
+ break;
+ }
+ }
+
+ llist_for_each_entry(endpoint, &gss->sns_endpoints, list) {
+ switch (endpoint->saddr.u.sa.sa_family) {
+ case AF_INET:
+ v4_endpoints = true;
+ break;
+ case AF_INET6:
+ v6_endpoints = true;
+ break;
+ }
}
+
+ return (v4_endpoints && v4_sig && v4_data) || (v6_endpoints && v6_sig && v6_data);
}
/* allstate-action for BSS role */
@@ -1527,29 +2201,34 @@ static void ns2_sns_st_all_action_bss(struct osmo_fsm_inst *fi, uint32_t event,
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
- /* reset when receiving GPRS_SNS_EV_REQ_NO_NSVC */
+ /* reset when receiving NS2_SNS_EV_REQ_NO_NSVC */
switch (event) {
- case GPRS_SNS_EV_REQ_NO_NSVC:
+ case NS2_SNS_EV_REQ_NO_NSVC:
/* ignore reselection running */
- if (gss->reselection_running)
+ if (gss->reselection_running || gss->block_no_nsvc_events)
break;
- LOGPFSML(fi, LOGL_ERROR, "NSE %d: no remaining NSVC, resetting SNS FSM\n", nse->nsei);
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
+ sns_failed(fi, "no remaining NSVC, resetting SNS FSM");
break;
- case GPRS_SNS_EV_REQ_SELECT_ENDPOINT:
+ case NS2_SNS_EV_REQ_FREE_NSVCS:
+ case NS2_SNS_EV_REQ_SELECT_ENDPOINT:
+ /* TODO: keep the order of binds when data == GPRS_SNS_FLAG_KEEP_SELECT_ENDPOINT_ORDER */
/* tear down previous state
* gprs_ns2_free_nsvcs() will trigger NO_NSVC, prevent this from triggering a reselection */
+ if (gss->reselection_running || gss->block_no_nsvc_events)
+ break;
+
gss->reselection_running = true;
- gprs_ns2_free_nsvcs(nse);
- ns2_clear_ipv46_entries_local(gss);
- ns2_clear_ipv46_entries_remote(gss);
+ ns2_free_nsvcs(nse);
+ ns2_clear_elems(&gss->local);
+ ns2_clear_elems(&gss->remote);
/* Choose the next sns endpoint. */
- if (llist_empty(&gss->sns_endpoints) || llist_empty(&gss->binds)) {
+ if (!ns2_sns_bss_valid_configuration(gss)) {
gss->initial = NULL;
ns2_prim_status_ind(gss->nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_NO_ENDPOINTS);
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 3);
+ gss->reselection_running = false;
return;
} else if (!gss->initial) {
gss->initial = llist_first_entry(&gss->sns_endpoints, struct sns_endpoint, list);
@@ -1561,6 +2240,7 @@ static void ns2_sns_st_all_action_bss(struct osmo_fsm_inst *fi, uint32_t event,
gss->initial = llist_entry(gss->initial->list.next, struct sns_endpoint, list);
}
+ gss->family = gss->initial->saddr.u.sa.sa_family;
gss->reselection_running = false;
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_SIZE, nse->nsi->timeout[NS_TOUT_TSNS_PROV], 1);
break;
@@ -1574,10 +2254,12 @@ static struct osmo_fsm gprs_ns2_sns_bss_fsm = {
.name = "GPRS-NS2-SNS-BSS",
.states = ns2_sns_bss_states,
.num_states = ARRAY_SIZE(ns2_sns_bss_states),
- .allstate_event_mask = S(GPRS_SNS_EV_REQ_NO_NSVC) |
- S(GPRS_SNS_EV_REQ_SELECT_ENDPOINT) |
- S(GPRS_SNS_EV_REQ_ADD_BIND) |
- S(GPRS_SNS_EV_REQ_DELETE_BIND),
+ .allstate_event_mask = S(NS2_SNS_EV_REQ_NO_NSVC) |
+ S(NS2_SNS_EV_REQ_FREE_NSVCS) |
+ S(NS2_SNS_EV_REQ_SELECT_ENDPOINT) |
+ S(NS2_SNS_EV_REQ_ADD_BIND) |
+ S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_REQ_DELETE_BIND),
.allstate_action = ns2_sns_st_all_action_bss,
.cleanup = NULL,
.timer_cb = ns2_sns_fsm_bss_timer_cb,
@@ -1612,6 +2294,7 @@ struct osmo_fsm_inst *ns2_sns_bss_fsm_alloc(struct gprs_ns2_nse *nse,
gss->num_max_ip6_remote = 8192;
INIT_LLIST_HEAD(&gss->sns_endpoints);
INIT_LLIST_HEAD(&gss->binds);
+ INIT_LLIST_HEAD(&gss->procedures);
return fi;
err:
@@ -1643,39 +2326,38 @@ int ns2_sns_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp
/* FIXME: how to resolve SNS FSM Instance by NSEI (SGSN)? */
fi = nse->bss_sns_fi;
gss = (struct ns2_sns_state *) fi->priv;
- if (!gss->sns_nsvc)
- gss->sns_nsvc = nsvc;
+ gss->sns_nsvc = nsvc;
LOGPFSML(fi, LOGL_DEBUG, "NSEI=%u Rx SNS PDU type %s\n", nsei,
get_value_string(gprs_ns_pdu_strings, nsh->pdu_type));
switch (nsh->pdu_type) {
case SNS_PDUT_SIZE:
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_SIZE, tp);
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_SIZE, tp);
break;
case SNS_PDUT_SIZE_ACK:
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_SIZE_ACK, tp);
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_SIZE_ACK, tp);
break;
case SNS_PDUT_CONFIG:
if (nsh->data[0] & 0x01)
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_CONFIG_END, tp);
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_CONFIG_END, tp);
else
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_CONFIG, tp);
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_CONFIG, tp);
break;
case SNS_PDUT_CONFIG_ACK:
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_CONFIG_ACK, tp);
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_CONFIG_ACK, tp);
break;
case SNS_PDUT_ADD:
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_ADD, tp);
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_ADD, tp);
break;
case SNS_PDUT_DELETE:
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_DELETE, tp);
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_DELETE, tp);
break;
case SNS_PDUT_CHANGE_WEIGHT:
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_CHANGE_WEIGHT, tp);
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_CHANGE_WEIGHT, tp);
break;
case SNS_PDUT_ACK:
- osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_ACK, tp);
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_ACK, tp);
break;
default:
LOGPFSML(fi, LOGL_ERROR, "NSEI=%u Rx unknown SNS PDU type %s\n", nsei,
@@ -1728,24 +2410,24 @@ void ns2_sns_dump_vty(struct vty *vty, const char *prefix, const struct gprs_ns2
vty_out(vty, "%sMaximum number of remote NS-VCs: %zu, IPv4 Endpoints: %zu, IPv6 Endpoints: %zu%s",
prefix, gss->num_max_nsvcs, gss->num_max_ip4_remote, gss->num_max_ip6_remote, VTY_NEWLINE);
- if (gss->num_ip4_local && gss->num_ip4_remote) {
+ if (gss->local.num_ip4 && gss->remote.num_ip4) {
vty_out(vty, "%sLocal IPv4 Endpoints:%s", prefix, VTY_NEWLINE);
- for (i = 0; i < gss->num_ip4_local; i++)
- vty_dump_sns_ip4(vty, prefix, &gss->ip4_local[i]);
+ for (i = 0; i < gss->local.num_ip4; i++)
+ vty_dump_sns_ip4(vty, prefix, &gss->local.ip4[i]);
vty_out(vty, "%sRemote IPv4 Endpoints:%s", prefix, VTY_NEWLINE);
- for (i = 0; i < gss->num_ip4_remote; i++)
- vty_dump_sns_ip4(vty, prefix, &gss->ip4_remote[i]);
+ for (i = 0; i < gss->remote.num_ip4; i++)
+ vty_dump_sns_ip4(vty, prefix, &gss->remote.ip4[i]);
}
- if (gss->num_ip6_local && gss->num_ip6_remote) {
+ if (gss->local.num_ip6 && gss->remote.num_ip6) {
vty_out(vty, "%sLocal IPv6 Endpoints:%s", prefix, VTY_NEWLINE);
- for (i = 0; i < gss->num_ip6_local; i++)
- vty_dump_sns_ip6(vty, prefix, &gss->ip6_local[i]);
+ for (i = 0; i < gss->local.num_ip6; i++)
+ vty_dump_sns_ip6(vty, prefix, &gss->local.ip6[i]);
vty_out(vty, "%sRemote IPv6 Endpoints:%s", prefix, VTY_NEWLINE);
- for (i = 0; i < gss->num_ip6_remote; i++)
- vty_dump_sns_ip6(vty, prefix, &gss->ip6_remote[i]);
+ for (i = 0; i < gss->remote.num_ip6; i++)
+ vty_dump_sns_ip6(vty, prefix, &gss->remote.ip6[i]);
}
}
@@ -1818,7 +2500,7 @@ int gprs_ns2_sns_add_endpoint(struct gprs_ns2_nse *nse,
llist_add_tail(&endpoint->list, &gss->sns_endpoints);
if (do_selection)
- osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
return 0;
}
@@ -1854,7 +2536,7 @@ int gprs_ns2_sns_del_endpoint(struct gprs_ns2_nse *nse,
return 0;
}
- /* gprs_ns2_free_nsvcs() will trigger GPRS_SNS_EV_REQ_NO_NSVC on the last NS-VC
+ /* gprs_ns2_free_nsvcs() will trigger NS2_SNS_EV_REQ_NO_NSVC on the last NS-VC
* and restart SNS SIZE procedure which selects a new initial */
LOGNSE(nse, LOGL_INFO, "Current in-use SNS endpoint is being removed."
"Closing all NS-VC and restart SNS-SIZE procedure"
@@ -1911,24 +2593,41 @@ void ns2_sns_notify_alive(struct gprs_ns2_nse *nse, struct gprs_ns2_vc *nsvc, bo
return;
gss = nse->bss_sns_fi->priv;
- if(nse->bss_sns_fi->state != GPRS_SNS_ST_CONFIGURED)
+ if (nse->bss_sns_fi->state != GPRS_SNS_ST_CONFIGURED && nse->bss_sns_fi->state != GPRS_SNS_ST_LOCAL_PROCEDURE)
return;
- if (alive == gss->alive)
+ if (gss->block_no_nsvc_events)
+ return;
+
+ if (gss->alive && nse->sum_sig_weight == 0) {
+ sns_failed(nse->bss_sns_fi, "No signalling NSVC available");
return;
+ }
/* check if this is the current SNS NS-VC */
- if (nsvc == gss->sns_nsvc) {
+ if (nsvc == gss->sns_nsvc && !alive) {
/* only replace the SNS NS-VC if there are other alive NS-VC.
* There aren't any other alive NS-VC when the SNS fsm just reached CONFIGURED
* and couldn't confirm yet if the NS-VC comes up */
- if (gss->alive && !alive)
- ns2_sns_replace_nsvc(nsvc);
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ if (nsvc == tmp)
+ continue;
+ if (ns2_vc_is_unblocked(nsvc)) {
+ ns2_sns_replace_nsvc(nsvc);
+ break;
+ }
+ }
}
+ if (alive == gss->alive)
+ return;
+
if (alive) {
+ /* we need at least a signalling NSVC before become alive */
+ if (nse->sum_sig_weight == 0)
+ return;
gss->alive = true;
- osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_NSVC_ALIVE, NULL);
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_NSVC_ALIVE, NULL);
} else {
/* is there at least another alive nsvc? */
llist_for_each_entry(tmp, &nse->nsvc, list) {
@@ -1938,7 +2637,7 @@ void ns2_sns_notify_alive(struct gprs_ns2_nse *nse, struct gprs_ns2_vc *nsvc, bo
/* all NS-VC have failed */
gss->alive = false;
- osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_NO_NSVC, NULL);
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_NO_NSVC, NULL);
}
}
@@ -1968,7 +2667,7 @@ int gprs_ns2_sns_add_bind(struct gprs_ns2_nse *nse,
tmp->bind = bind;
llist_add_tail(&tmp->list, &gss->binds);
- osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_ADD_BIND, tmp);
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_ADD_BIND, tmp);
return 0;
}
@@ -1995,22 +2694,42 @@ int gprs_ns2_sns_del_bind(struct gprs_ns2_nse *nse,
if (tmp->bind == bind) {
llist_del(&tmp->list);
found = true;
+ break;
}
}
if (!found)
return -ENOENT;
- osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_DELETE_BIND, tmp);
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_DELETE_BIND, tmp);
return 0;
}
-/* Update SNS weights
- * \param[in] nsvc the NSVC which should be updated
+/* Update SNS weights for a bind (local endpoint).
+ * \param[in] bind the bind which has been updated
*/
void ns2_sns_update_weights(struct gprs_ns2_vc_bind *bind)
{
- /* TODO: implement weights after binds per sns implemented */
+ struct ns2_sns_bind *sbind;
+ struct gprs_ns2_nse *nse;
+ struct ns2_sns_state *gss;
+ const struct osmo_sockaddr *addr = gprs_ns2_ip_bind_sockaddr(bind);
+
+ llist_for_each_entry(nse, &bind->nsi->nse, list) {
+ if (!nse->bss_sns_fi)
+ continue;
+
+ gss = nse->bss_sns_fi->priv;
+ if (addr->u.sa.sa_family != gss->family)
+ return;
+
+ llist_for_each_entry(sbind, &gss->binds, list) {
+ if (sbind->bind == bind) {
+ osmo_fsm_inst_dispatch(gss->nse->bss_sns_fi, NS2_SNS_EV_REQ_CHANGE_WEIGHT, sbind);
+ break;
+ }
+ }
+ }
}
@@ -2020,6 +2739,32 @@ void ns2_sns_update_weights(struct gprs_ns2_vc_bind *bind)
* SGSN role
***********************************************************************/
+/* cleanup all state. If nsvc is given, don't remove this nsvc. (nsvc is given when a SIZE PDU received) */
+static void ns2_clear_sgsn(struct ns2_sns_state *gss, struct gprs_ns2_vc *size_nsvc)
+{
+ struct gprs_ns2_vc *nsvc, *nsvc2;
+
+ ns2_clear_procedures(gss);
+ ns2_clear_elems(&gss->local);
+ ns2_clear_elems(&gss->remote);
+ gss->block_no_nsvc_events = true;
+ llist_for_each_entry_safe(nsvc, nsvc2, &gss->nse->nsvc, list) {
+ /* Ignore the NSVC over which the SIZE PDU got received */
+ if (size_nsvc && size_nsvc == nsvc)
+ continue;
+
+ gprs_ns2_free_nsvc(nsvc);
+ }
+ gss->block_no_nsvc_events = false;
+}
+
+static void ns2_sns_st_sgsn_unconfigured_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+
+ ns2_clear_sgsn(gss, NULL);
+}
+
static void ns2_sns_st_sgsn_unconfigured(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
@@ -2039,8 +2784,8 @@ static void ns2_sns_st_sgsn_wait_config(struct osmo_fsm_inst *fi, uint32_t event
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
switch (event) {
- case GPRS_SNS_EV_RX_CONFIG:
- case GPRS_SNS_EV_RX_CONFIG_END:
+ case NS2_SNS_EV_RX_CONFIG:
+ case NS2_SNS_EV_RX_CONFIG_END:
rc = ns_sns_append_remote_eps(fi, data);
if (rc < 0) {
cause = -rc;
@@ -2049,9 +2794,9 @@ static void ns2_sns_st_sgsn_wait_config(struct osmo_fsm_inst *fi, uint32_t event
return;
}
/* only change state if last CONFIG was received */
- if (event == GPRS_SNS_EV_RX_CONFIG_END) {
+ if (event == NS2_SNS_EV_RX_CONFIG_END) {
/* ensure sum of data weight / sig weights is > 0 */
- if (nss_weight_sum_data(gss) == 0 || nss_weight_sum_sig(gss) == 0) {
+ if (ip46_weight_sum_data(&gss->remote) == 0 || ip46_weight_sum_sig(&gss->remote) == 0) {
cause = NS_CAUSE_INVAL_WEIGH;
ns2_tx_sns_config_ack(gss->sns_nsvc, &cause);
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
@@ -2074,8 +2819,8 @@ static void ns2_sns_st_sgsn_wait_config_ack_onenter(struct osmo_fsm_inst *fi, ui
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
/* transmit SGSN-oriented SNS-CONFIG */
- ns2_tx_sns_config(gss->sns_nsvc, true, gss->ip4_local, gss->num_ip4_local,
- gss->ip6_local, gss->num_ip6_local);
+ ns2_tx_sns_config(gss->sns_nsvc, true, gss->local.ip4, gss->local.num_ip4,
+ gss->local.ip6, gss->local.num_ip6);
}
/* We're waiting for SNS-CONFIG-ACK from the BSS (in response to our outbound SNS-CONFIG) */
@@ -2088,7 +2833,7 @@ static void ns2_sns_st_sgsn_wait_config_ack(struct osmo_fsm_inst *fi, uint32_t e
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
switch (event) {
- case GPRS_SNS_EV_RX_CONFIG_ACK:
+ case NS2_SNS_EV_RX_CONFIG_ACK:
tp = data;
if (TLVP_VAL_MINLEN(tp, NS_IE_CAUSE, 1)) {
LOGPFSML(fi, LOGL_ERROR, "Rx SNS-CONFIG-ACK with cause %s\n",
@@ -2115,10 +2860,11 @@ static const struct osmo_fsm_state ns2_sns_sgsn_states[] = {
S(GPRS_SNS_ST_SGSN_WAIT_CONFIG),
.name = "UNCONFIGURED",
.action = ns2_sns_st_sgsn_unconfigured,
+ .onenter = ns2_sns_st_sgsn_unconfigured_onenter,
},
[GPRS_SNS_ST_SGSN_WAIT_CONFIG] = {
- .in_event_mask = S(GPRS_SNS_EV_RX_CONFIG) |
- S(GPRS_SNS_EV_RX_CONFIG_END),
+ .in_event_mask = S(NS2_SNS_EV_RX_CONFIG) |
+ S(NS2_SNS_EV_RX_CONFIG_END),
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
S(GPRS_SNS_ST_SGSN_WAIT_CONFIG) |
S(GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK),
@@ -2126,8 +2872,9 @@ static const struct osmo_fsm_state ns2_sns_sgsn_states[] = {
.action = ns2_sns_st_sgsn_wait_config,
},
[GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK] = {
- .in_event_mask = S(GPRS_SNS_EV_RX_CONFIG_ACK),
+ .in_event_mask = S(NS2_SNS_EV_RX_CONFIG_ACK),
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_SGSN_WAIT_CONFIG) |
S(GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK) |
S(GPRS_SNS_ST_CONFIGURED),
.name = "SGSN_WAIT_CONFIG_ACK",
@@ -2135,16 +2882,33 @@ static const struct osmo_fsm_state ns2_sns_sgsn_states[] = {
.onenter = ns2_sns_st_sgsn_wait_config_ack_onenter,
},
[GPRS_SNS_ST_CONFIGURED] = {
- .in_event_mask = S(GPRS_SNS_EV_RX_ADD) |
- S(GPRS_SNS_EV_RX_DELETE) |
- S(GPRS_SNS_EV_RX_CHANGE_WEIGHT) |
- S(GPRS_SNS_EV_REQ_NSVC_ALIVE),
- .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED),
+ .in_event_mask = S(NS2_SNS_EV_RX_ADD) |
+ S(NS2_SNS_EV_RX_DELETE) |
+ S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_REQ_NSVC_ALIVE),
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_SGSN_WAIT_CONFIG) |
+ S(GPRS_SNS_ST_LOCAL_PROCEDURE),
.name = "CONFIGURED",
/* shared with BSS side; once configured there's no difference */
.action = ns2_sns_st_configured,
.onenter = ns2_sns_st_configured_onenter,
},
+ [GPRS_SNS_ST_LOCAL_PROCEDURE] = {
+ .in_event_mask = S(NS2_SNS_EV_RX_ADD) |
+ S(NS2_SNS_EV_RX_DELETE) |
+ S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_RX_ACK) |
+ S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_REQ_NSVC_ALIVE),
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_CONFIGURED) |
+ S(GPRS_SNS_ST_LOCAL_PROCEDURE),
+ .name = "LOCAL_PROCEDURE",
+ /* shared with BSS side; once configured there's no difference */
+ .action = ns2_sns_st_local_procedure,
+ .onenter = ns2_sns_st_local_procedure_onenter,
+ },
};
static int ns2_sns_fsm_sgsn_timer_cb(struct osmo_fsm_inst *fi)
@@ -2166,11 +2930,19 @@ static int ns2_sns_fsm_sgsn_timer_cb(struct osmo_fsm_inst *fi)
case 4:
LOGPFSML(fi, LOGL_ERROR, "NSE %d: Config succeeded but no NS-VC came online.\n", nse->nsei);
break;
+ case 5:
+ if (gss->N >= nsi->timeout[NS_TOUT_TSNS_PROCEDURES_RETRIES]) {
+ sns_failed(fi, "SNS Procedure retries failed.");
+ } else {
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV],
+ fi->T);
+ }
+ break;
}
+
return 0;
}
-
/* allstate-action for SGSN role */
static void ns2_sns_st_all_action_sgsn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
@@ -2183,19 +2955,23 @@ static void ns2_sns_st_all_action_sgsn(struct osmo_fsm_inst *fi, uint32_t event,
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
switch (event) {
- case GPRS_SNS_EV_RX_SIZE:
+ case NS2_SNS_EV_RX_SIZE:
tp = (struct tlv_parsed *) data;
/* check for mandatory / conditional IEs */
if (!TLVP_PRES_LEN(tp, NS_IE_RESET_FLAG, 1) ||
!TLVP_PRES_LEN(tp, NS_IE_MAX_NR_NSVC, 2)) {
cause = NS_CAUSE_MISSING_ESSENT_IE;
ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
+ if (fi->state == GPRS_SNS_ST_UNCONFIGURED)
+ sns_failed(fi, "Rx Size: Missing essential IE");
break;
}
if (!TLVP_PRES_LEN(tp, NS_IE_IPv4_EP_NR, 2) &&
!TLVP_PRES_LEN(tp, NS_IE_IPv6_EP_NR, 2)) {
cause = NS_CAUSE_MISSING_ESSENT_IE;
ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
+ if (fi->state == GPRS_SNS_ST_UNCONFIGURED)
+ sns_failed(fi, "Rx Size: Missing essential IE");
break;
}
if (TLVP_PRES_LEN(tp, NS_IE_IPv4_EP_NR, 2))
@@ -2203,22 +2979,24 @@ static void ns2_sns_st_all_action_sgsn(struct osmo_fsm_inst *fi, uint32_t event,
if (TLVP_PRES_LEN(tp, NS_IE_IPv6_EP_NR, 2))
gss->num_max_ip6_remote = tlvp_val16be(tp, NS_IE_IPv6_EP_NR);
/* decide if we go for IPv4 or IPv6 */
- if (gss->num_max_ip6_remote && ns2_sns_count_num_local_ep(fi, IPv6)) {
- gss->ip = IPv6;
+ if (gss->num_max_ip6_remote && ns2_sns_count_num_local_ep(fi, AF_INET6)) {
+ gss->family = AF_INET6;
ns2_sns_compute_local_ep_from_binds(fi);
- num_local_eps = gss->num_ip6_local;
+ num_local_eps = gss->local.num_ip6;
num_remote_eps = gss->num_max_ip6_remote;
- } else if (gss->num_max_ip4_remote && ns2_sns_count_num_local_ep(fi, IPv4)) {
- gss->ip = IPv4;
+ } else if (gss->num_max_ip4_remote && ns2_sns_count_num_local_ep(fi, AF_INET)) {
+ gss->family = AF_INET;
ns2_sns_compute_local_ep_from_binds(fi);
- num_local_eps = gss->num_ip4_local;
+ num_local_eps = gss->local.num_ip4;
num_remote_eps = gss->num_max_ip4_remote;
} else {
- if (gss->num_ip4_local && !gss->num_max_ip4_remote)
+ if (gss->local.num_ip4 && !gss->num_max_ip4_remote)
cause = NS_CAUSE_INVAL_NR_IPv4_EP;
else
cause = NS_CAUSE_INVAL_NR_IPv6_EP;
ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
+ if (fi->state == GPRS_SNS_ST_UNCONFIGURED)
+ sns_failed(fi, "Rx Size: Invalid Nr of IPv4/IPv6 EPs");
break;
}
/* ensure number of NS-VCs is sufficient for full mesh */
@@ -2229,36 +3007,40 @@ static void ns2_sns_st_all_action_sgsn(struct osmo_fsm_inst *fi, uint32_t event,
num_remote_eps, num_local_eps * num_remote_eps, gss->num_max_nsvcs);
cause = NS_CAUSE_INVAL_NR_NS_VC;
ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
+ if (fi->state == GPRS_SNS_ST_UNCONFIGURED)
+ sns_failed(fi, NULL);
break;
}
/* perform state reset, if requested */
flag = *TLVP_VAL(tp, NS_IE_RESET_FLAG);
if (flag & 1) {
- struct gprs_ns2_vc *nsvc, *nsvc2;
/* clear all state */
- osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
+ /* TODO: ensure gss->sns_nsvc is always the NSVC on which we received the SIZE PDU */
gss->N = 0;
- ns2_clear_ipv46_entries_local(gss);
- ns2_clear_ipv46_entries_remote(gss);
- llist_for_each_entry_safe(nsvc, nsvc2, &gss->nse->nsvc, list) {
- if (nsvc == gss->sns_nsvc) {
- /* keep the NSVC we need for SNS, but unconfigure it */
- nsvc->sig_weight = 0;
- nsvc->data_weight = 0;
- ns2_vc_force_unconfigured(nsvc);
- } else {
- /* free all other NS-VCs */
- gprs_ns2_free_nsvc(nsvc);
- }
- }
+ ns2_clear_sgsn(gss, gss->sns_nsvc);
+ /* keep the NSVC we need for SNS, but unconfigure it */
+ gss->sns_nsvc->sig_weight = 0;
+ gss->sns_nsvc->data_weight = 0;
+ gss->block_no_nsvc_events = true;
+ ns2_vc_force_unconfigured(gss->sns_nsvc);
+ gss->block_no_nsvc_events = false;
ns2_sns_compute_local_ep_from_binds(fi);
}
+
+ if (fi->state == GPRS_SNS_ST_UNCONFIGURED && !(flag & 1)) {
+ sns_failed(fi, "Rx Size without Reset flag, but NSE is unknown");
+ break;
+ }
+
/* send SIZE_ACK */
ns2_tx_sns_size_ack(gss->sns_nsvc, NULL);
/* only wait for SNS-CONFIG in case of Reset flag */
if (flag & 1)
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SGSN_WAIT_CONFIG, 0, 0);
break;
+ case NS2_SNS_EV_REQ_FREE_NSVCS:
+ sns_failed(fi, "On user request to free all NSVCs");
+ break;
default:
ns2_sns_st_all_action(fi, event, data);
break;
@@ -2269,10 +3051,12 @@ static struct osmo_fsm gprs_ns2_sns_sgsn_fsm = {
.name = "GPRS-NS2-SNS-SGSN",
.states = ns2_sns_sgsn_states,
.num_states = ARRAY_SIZE(ns2_sns_sgsn_states),
- .allstate_event_mask = S(GPRS_SNS_EV_RX_SIZE) |
- S(GPRS_SNS_EV_REQ_NO_NSVC) |
- S(GPRS_SNS_EV_REQ_ADD_BIND) |
- S(GPRS_SNS_EV_REQ_DELETE_BIND),
+ .allstate_event_mask = S(NS2_SNS_EV_RX_SIZE) |
+ S(NS2_SNS_EV_REQ_NO_NSVC) |
+ S(NS2_SNS_EV_REQ_FREE_NSVCS) |
+ S(NS2_SNS_EV_REQ_ADD_BIND) |
+ S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_REQ_DELETE_BIND),
.allstate_action = ns2_sns_st_all_action_sgsn,
.cleanup = NULL,
.timer_cb = ns2_sns_fsm_sgsn_timer_cb,
@@ -2303,6 +3087,7 @@ struct osmo_fsm_inst *ns2_sns_sgsn_fsm_alloc(struct gprs_ns2_nse *nse, const cha
gss->role = GPRS_SNS_ROLE_SGSN;
INIT_LLIST_HEAD(&gss->sns_endpoints);
INIT_LLIST_HEAD(&gss->binds);
+ INIT_LLIST_HEAD(&gss->procedures);
return fi;
err:
diff --git a/src/gb/gprs_ns2_udp.c b/src/gb/gprs_ns2_udp.c
index 7980df59..1dcc3030 100644
--- a/src/gb/gprs_ns2_udp.c
+++ b/src/gb/gprs_ns2_udp.c
@@ -28,6 +28,7 @@
#include <errno.h>
+#include <osmocom/core/osmo_io.h>
#include <osmocom/core/select.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/core/socket.h>
@@ -46,7 +47,7 @@ struct gprs_ns2_vc_driver vc_driver_ip = {
};
struct priv_bind {
- struct osmo_fd fd;
+ struct osmo_io_fd *iofd;
struct osmo_sockaddr addr;
int dscp;
uint8_t priority;
@@ -68,7 +69,8 @@ static void free_bind(struct gprs_ns2_vc_bind *bind)
priv = bind->priv;
- osmo_fd_close(&priv->fd);
+ osmo_iofd_free(priv->iofd);
+ priv->iofd = NULL;
talloc_free(priv);
}
@@ -118,10 +120,10 @@ static void dump_vty(const struct gprs_ns2_vc_bind *bind,
/*! Find a NS-VC by its remote socket address.
* \param[in] bind in which to search
- * \param[in] saddr remote peer socket adddress to search
+ * \param[in] rem_addr remote peer socket address to search
* \returns NS-VC matching sockaddr; NULL if none found */
struct gprs_ns2_vc *gprs_ns2_nsvc_by_sockaddr_bind(struct gprs_ns2_vc_bind *bind,
- const struct osmo_sockaddr *saddr)
+ const struct osmo_sockaddr *rem_addr)
{
struct gprs_ns2_vc *nsvc;
struct priv_vc *vcpriv;
@@ -130,9 +132,9 @@ struct gprs_ns2_vc *gprs_ns2_nsvc_by_sockaddr_bind(struct gprs_ns2_vc_bind *bind
llist_for_each_entry(nsvc, &bind->nsvc, blist) {
vcpriv = nsvc->priv;
- if (vcpriv->remote.u.sa.sa_family != saddr->u.sa.sa_family)
+ if (vcpriv->remote.u.sa.sa_family != rem_addr->u.sa.sa_family)
continue;
- if (osmo_sockaddr_cmp(&vcpriv->remote, saddr))
+ if (osmo_sockaddr_cmp(&vcpriv->remote, rem_addr))
continue;
return nsvc;
@@ -143,17 +145,11 @@ struct gprs_ns2_vc *gprs_ns2_nsvc_by_sockaddr_bind(struct gprs_ns2_vc_bind *bind
static inline int nsip_sendmsg(struct gprs_ns2_vc_bind *bind,
struct msgb *msg,
- struct osmo_sockaddr *dest)
+ const struct osmo_sockaddr *dest)
{
- int rc;
struct priv_bind *priv = bind->priv;
- rc = sendto(priv->fd.fd, msg->data, msg->len, 0,
- &dest->u.sa, sizeof(*dest));
-
- msgb_free(msg);
-
- return rc;
+ return osmo_iofd_sendto_msgb(priv->iofd, msg, 0, dest);
}
/*! send the msg and free it afterwards.
@@ -171,40 +167,7 @@ static int nsip_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
return rc;
}
-/* Read a single NS-over-IP message */
-static struct msgb *read_nsip_msg(struct osmo_fd *bfd, int *error, struct osmo_sockaddr *saddr,
- const struct gprs_ns2_vc_bind *bind)
-{
- struct msgb *msg = ns2_msgb_alloc();
- int ret = 0;
- socklen_t saddr_len = sizeof(*saddr);
-
- if (!msg) {
- *error = -ENOMEM;
- return NULL;
- }
-
- ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE - NS_ALLOC_HEADROOM, 0,
- &saddr->u.sa, &saddr_len);
- if (ret < 0) {
- LOGBIND(bind, LOGL_ERROR, "recv error %s during NSIP recvfrom %s\n",
- strerror(errno), osmo_sock_get_name2(bfd->fd));
- msgb_free(msg);
- *error = ret;
- return NULL;
- } else if (ret == 0) {
- msgb_free(msg);
- *error = ret;
- return NULL;
- }
-
- msg->l2h = msg->data;
- msgb_put(msg, ret);
-
- return msg;
-}
-
-static struct priv_vc *ns2_driver_alloc_vc(struct gprs_ns2_vc_bind *bind, struct gprs_ns2_vc *nsvc, struct osmo_sockaddr *remote)
+static struct priv_vc *ns2_driver_alloc_vc(struct gprs_ns2_vc_bind *bind, struct gprs_ns2_vc *nsvc, const struct osmo_sockaddr *remote)
{
struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
if (!priv)
@@ -216,24 +179,22 @@ static struct priv_vc *ns2_driver_alloc_vc(struct gprs_ns2_vc_bind *bind, struct
return priv;
}
-static int handle_nsip_read(struct osmo_fd *bfd)
+static void handle_nsip_recvfrom(struct osmo_io_fd *iofd, int error, struct msgb *msg,
+ const struct osmo_sockaddr *saddr)
{
int rc = 0;
- int error = 0;
- struct gprs_ns2_vc_bind *bind = bfd->data;
- struct osmo_sockaddr saddr;
+ struct gprs_ns2_vc_bind *bind = osmo_iofd_get_data(iofd);
struct gprs_ns2_vc *nsvc;
- struct msgb *msg = read_nsip_msg(bfd, &error, &saddr, bind);
+
struct msgb *reject;
- if (!msg)
- return -EINVAL;
+ msg->l2h = msgb_data(msg);
/* check if a vc is available */
- nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, &saddr);
+ nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, saddr);
if (!nsvc) {
/* VC not found */
- rc = ns2_create_vc(bind, msg, &saddr, "newconnection", &reject, &nsvc);
+ rc = ns2_create_vc(bind, msg, saddr, "newconnection", &reject, &nsvc);
switch (rc) {
case NS2_CS_FOUND:
break;
@@ -243,38 +204,42 @@ static int handle_nsip_read(struct osmo_fd *bfd)
goto out;
case NS2_CS_REJECTED:
/* nsip_sendmsg will free reject */
- rc = nsip_sendmsg(bind, reject, &saddr);
+ rc = nsip_sendmsg(bind, reject, saddr);
goto out;
case NS2_CS_CREATED:
- ns2_driver_alloc_vc(bind, nsvc, &saddr);
- ns2_vc_fsm_start(nsvc);
+ ns2_driver_alloc_vc(bind, nsvc, saddr);
+ /* only start the fsm for non SNS. SNS will take care of its own */
+ if (nsvc->nse->dialect != GPRS_NS2_DIALECT_SNS)
+ ns2_vc_fsm_start(nsvc);
break;
}
}
- return ns2_recv_vc(nsvc, msg);
+ ns2_recv_vc(nsvc, msg);
+ return;
out:
msgb_free(msg);
- return rc;
}
-static int handle_nsip_write(struct osmo_fd *bfd)
-{
- /* FIXME: actually send the data here instead of nsip_sendmsg() */
- return -EIO;
-}
-
-static int nsip_fd_cb(struct osmo_fd *bfd, unsigned int what)
+static void handle_nsip_sendto(struct osmo_io_fd *iofd, int res,
+ struct msgb *msg,
+ const struct osmo_sockaddr *daddr)
{
- int rc = 0;
+ struct gprs_ns2_vc_bind *bind = osmo_iofd_get_data(iofd);
+ struct gprs_ns2_vc *nsvc;
- if (what & OSMO_FD_READ)
- rc = handle_nsip_read(bfd);
- if (what & OSMO_FD_WRITE)
- rc = handle_nsip_write(bfd);
+ nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, daddr);
+ if (!nsvc)
+ return;
- return rc;
+ if (OSMO_LIKELY(res >= 0)) {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT, res);
+ } else {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT_DROP);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT_DROP, msgb_length(msg));
+ }
}
/*! Find NS bind for a given socket address
@@ -319,6 +284,11 @@ int gprs_ns2_ip_bind(struct gprs_ns2_inst *nsi,
struct priv_bind *priv;
int rc;
+ struct osmo_io_ops ioops = {
+ .sendto_cb = &handle_nsip_sendto,
+ .recvfrom_cb = &handle_nsip_recvfrom,
+ };
+
if (local->u.sa.sa_family != AF_INET && local->u.sa.sa_family != AF_INET6)
return -EINVAL;
@@ -351,12 +321,11 @@ int gprs_ns2_ip_bind(struct gprs_ns2_inst *nsi,
gprs_ns2_free_bind(bind);
return -ENOMEM;
}
- priv->fd.cb = nsip_fd_cb;
- priv->fd.data = bind;
+
priv->addr = *local;
priv->dscp = dscp;
- rc = osmo_sock_init_osa_ofd(&priv->fd, SOCK_DGRAM, IPPROTO_UDP,
+ rc = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP,
local, NULL,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_DSCP(priv->dscp));
if (rc < 0) {
@@ -364,6 +333,11 @@ int gprs_ns2_ip_bind(struct gprs_ns2_inst *nsi,
return rc;
}
+ priv->iofd = osmo_iofd_setup(bind, rc, "NS bind", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &ioops, bind);
+ osmo_iofd_register(priv->iofd, rc);
+ osmo_iofd_set_alloc_info(priv->iofd, 4096, 128);
+ osmo_iofd_set_txqueue_max_length(priv->iofd, nsi->txqueue_max_length);
+
/* IPv4: max fragmented payload can be (13 bit) * 8 byte => 65535.
* IPv6: max payload can be 65535 (RFC 2460).
* UDP header = 8 byte */
@@ -384,10 +358,10 @@ struct gprs_ns2_vc *ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
const struct osmo_sockaddr *remote)
{
struct gprs_ns2_vc *nsvc;
+ const struct osmo_sockaddr *local;
struct priv_vc *priv;
enum gprs_ns2_vc_mode vc_mode;
- char *sockaddr_str;
- char idbuf[64];
+ char idbuf[256], tmp[INET6_ADDRSTRLEN + 8];
OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
@@ -402,10 +376,12 @@ struct gprs_ns2_vc *ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
if (gprs_ns2_nsvc_by_sockaddr_bind(bind, remote))
return NULL;
- sockaddr_str = (char *)osmo_sockaddr_to_str(remote);
- osmo_identifier_sanitize_buf(sockaddr_str, NULL, '_');
- snprintf(idbuf, sizeof(idbuf), "%s-NSE%05u-remote-%s", gprs_ns2_lltype_str(nse->ll),
- nse->nsei, sockaddr_str);
+ local = gprs_ns2_ip_bind_sockaddr(bind);
+ osmo_sockaddr_to_str_buf(tmp, sizeof(tmp), local);
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-NSVC-%s-%s-%s", nse->nsei, gprs_ns2_lltype_str(nse->ll),
+ tmp, osmo_sockaddr_to_str(remote));
+ osmo_identifier_sanitize_buf(idbuf, NULL, '_');
+
nsvc = ns2_vc_alloc(bind, nse, true, vc_mode, idbuf);
if (!nsvc)
return NULL;
@@ -517,7 +493,7 @@ int gprs_ns2_ip_bind_set_dscp(struct gprs_ns2_vc_bind *bind, int dscp)
if (dscp != priv->dscp) {
priv->dscp = dscp;
- rc = osmo_sock_set_dscp(priv->fd.fd, dscp);
+ rc = osmo_sock_set_dscp(osmo_iofd_get_fd(priv->iofd), dscp);
if (rc < 0) {
LOGBIND(bind, LOGL_ERROR, "Failed to set the DSCP to %u with ret(%d) errno(%d)\n",
dscp, rc, errno);
@@ -539,7 +515,7 @@ int gprs_ns2_ip_bind_set_priority(struct gprs_ns2_vc_bind *bind, uint8_t priorit
if (priority != priv->priority) {
priv->priority = priority;
- rc = osmo_sock_set_priority(priv->fd.fd, priority);
+ rc = osmo_sock_set_priority(osmo_iofd_get_fd(priv->iofd), priority);
if (rc < 0) {
LOGBIND(bind, LOGL_ERROR, "Failed to set the priority to %u with ret(%d) errno(%d)\n",
priority, rc, errno);
@@ -599,6 +575,14 @@ struct gprs_ns2_vc_bind *ns2_ip_get_bind_by_index(struct gprs_ns2_inst *nsi,
return NULL;
}
+void ns2_ip_set_txqueue_max_length(struct gprs_ns2_vc_bind *bind, unsigned int max_length)
+{
+ struct priv_bind *priv = bind->priv;
+ OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
+
+ osmo_iofd_set_txqueue_max_length(priv->iofd, max_length);
+}
+
/*! set the signalling and data weight for this bind
* \param[in] bind
* \param[in] signalling the signalling weight
diff --git a/src/gb/gprs_ns2_vc_fsm.c b/src/gb/gprs_ns2_vc_fsm.c
index 7ed82997..9cd83c4f 100644
--- a/src/gb/gprs_ns2_vc_fsm.c
+++ b/src/gb/gprs_ns2_vc_fsm.c
@@ -58,8 +58,6 @@ struct gprs_ns2_vc_priv {
bool initiator;
bool initiate_block;
bool initiate_reset;
- /* if blocked by O&M/vty */
- bool om_blocked;
/* if unitdata is forwarded to the user */
bool accept_unitdata;
@@ -118,6 +116,7 @@ enum gprs_ns2_vc_event {
GPRS_NS2_EV_REQ_OM_RESET, /* vty cmd: reset */
GPRS_NS2_EV_REQ_OM_BLOCK, /* vty cmd: block */
GPRS_NS2_EV_REQ_OM_UNBLOCK, /* vty cmd: unblock*/
+ GPRS_NS2_EV_RX_BLOCK_FOREIGN, /* received a BLOCK over another NSVC */
};
static const struct value_string ns2_vc_event_names[] = {
@@ -127,6 +126,7 @@ static const struct value_string ns2_vc_event_names[] = {
{ GPRS_NS2_EV_RX_UNBLOCK, "RX-UNBLOCK" },
{ GPRS_NS2_EV_RX_UNBLOCK_ACK, "RX-UNBLOCK_ACK" },
{ GPRS_NS2_EV_RX_BLOCK, "RX-BLOCK" },
+ { GPRS_NS2_EV_RX_BLOCK_FOREIGN, "RX-BLOCK_FOREIGN" },
{ GPRS_NS2_EV_RX_BLOCK_ACK, "RX-BLOCK_ACK" },
{ GPRS_NS2_EV_RX_ALIVE, "RX-ALIVE" },
{ GPRS_NS2_EV_RX_ALIVE_ACK, "RX-ALIVE_ACK" },
@@ -181,6 +181,7 @@ static void start_test_procedure(struct osmo_fsm_inst *fi, bool start_tx_alive)
static void stop_test_procedure(struct gprs_ns2_vc_priv *priv)
{
+ osmo_stat_item_set(osmo_stat_item_group_get_item(priv->nsvc->statg, NS_STAT_ALIVE_DELAY), 0);
osmo_timer_del(&priv->alive.timer);
}
@@ -209,7 +210,7 @@ static void recv_test_procedure(struct osmo_fsm_inst *fi)
priv->alive.mode = NS_TOUT_TNS_TEST;
osmo_timer_schedule(&priv->alive.timer, nsi->timeout[NS_TOUT_TNS_TEST], 0);
- osmo_stat_item_set(nsvc->statg->items[NS_STAT_ALIVE_DELAY],
+ osmo_stat_item_set(osmo_stat_item_group_get_item(nsvc->statg, NS_STAT_ALIVE_DELAY),
alive_timer_elapsed_ms(priv));
}
@@ -229,7 +230,7 @@ static void alive_timeout_handler(void *data)
osmo_timer_schedule(&priv->alive.timer, nsi->timeout[NS_TOUT_TNS_ALIVE], 0);
break;
case NS_TOUT_TNS_ALIVE:
- rate_ctr_inc(&priv->nsvc->ctrg->ctr[NS_CTR_LOST_ALIVE]);
+ RATE_CTR_INC_NS(priv->nsvc, NS_CTR_LOST_ALIVE);
priv->alive.N++;
if (priv->alive.N <= nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) {
@@ -253,7 +254,10 @@ static void alive_timeout_handler(void *data)
static void ns2_st_unconfigured_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+
stop_test_procedure(fi->priv);
+ ns2_nse_notify_unblocked(priv->nsvc, false);
}
static void ns2_st_unconfigured(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -262,7 +266,7 @@ static void ns2_st_unconfigured(struct osmo_fsm_inst *fi, uint32_t event, void *
struct gprs_ns2_inst *nsi = priv->nsvc->nse->nsi;
priv->initiate_reset = priv->initiate_block = priv->initiator;
- priv->om_blocked = false;
+ priv->nsvc->om_blocked = false;
switch (event) {
case GPRS_NS2_EV_REQ_START:
@@ -335,15 +339,16 @@ static void ns2_st_blocked_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
if (old_state != GPRS_NS2_ST_BLOCKED) {
priv->N = 0;
- rate_ctr_inc(&priv->nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+ RATE_CTR_INC_NS(priv->nsvc, NS_CTR_BLOCKED);
}
- if (priv->om_blocked) {
+ ns2_nse_notify_unblocked(priv->nsvc, false);
+ if (priv->nsvc->om_blocked) {
/* we are already blocked after a RESET */
if (old_state == GPRS_NS2_ST_RESET) {
osmo_timer_del(&fi->timer);
} else {
- ns2_tx_block(priv->nsvc, NS_CAUSE_OM_INTERVENTION);
+ ns2_tx_block(priv->nsvc, NS_CAUSE_OM_INTERVENTION, NULL);
}
} else if (priv->initiate_block) {
ns2_tx_unblock(priv->nsvc);
@@ -356,28 +361,35 @@ static void ns2_st_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gprs_ns2_vc_priv *priv = fi->priv;
- if (priv->om_blocked) {
+ if (priv->nsvc->om_blocked) {
switch (event) {
case GPRS_NS2_EV_RX_BLOCK_ACK:
priv->accept_unitdata = false;
osmo_timer_del(&fi->timer);
break;
case GPRS_NS2_EV_RX_BLOCK:
+ ns2_tx_block_ack(priv->nsvc, NULL);
+ /* fall through */
+ case GPRS_NS2_EV_RX_BLOCK_FOREIGN:
+ /* the BLOCK ACK for foreign BLOCK PDUs (rx over another nsvc) will be sent
+ * from the receiving nsvc */
priv->accept_unitdata = false;
- ns2_tx_block_ack(priv->nsvc);
osmo_timer_del(&fi->timer);
break;
case GPRS_NS2_EV_RX_UNBLOCK:
priv->accept_unitdata = false;
- ns2_tx_block(priv->nsvc, NS_CAUSE_OM_INTERVENTION);
+ ns2_tx_block(priv->nsvc, NS_CAUSE_OM_INTERVENTION, NULL);
osmo_timer_add(&fi->timer);
break;
}
} else if (priv->initiate_block) {
switch (event) {
+ case GPRS_NS2_EV_RX_BLOCK_FOREIGN:
+ /* the block ack will be sent by the rx NSVC */
+ break;
case GPRS_NS2_EV_RX_BLOCK:
/* TODO: BLOCK is a UNBLOCK_NACK */
- ns2_tx_block_ack(priv->nsvc);
+ ns2_tx_block_ack(priv->nsvc, NULL);
break;
case GPRS_NS2_EV_RX_UNBLOCK:
ns2_tx_unblock_ack(priv->nsvc);
@@ -391,8 +403,11 @@ static void ns2_st_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
} else {
/* we are on the receiving end. The initiator who sent RESET is responsible to UNBLOCK! */
switch (event) {
+ case GPRS_NS2_EV_RX_BLOCK_FOREIGN:
+ /* the block ack will be sent by the rx NSVC */
+ break;
case GPRS_NS2_EV_RX_BLOCK:
- ns2_tx_block_ack(priv->nsvc);
+ ns2_tx_block_ack(priv->nsvc, NULL);
break;
case GPRS_NS2_EV_RX_UNBLOCK:
ns2_tx_unblock_ack(priv->nsvc);
@@ -409,8 +424,10 @@ static void ns2_st_unblocked_on_enter(struct osmo_fsm_inst *fi, uint32_t old_sta
struct gprs_ns2_vc *nsvc = priv->nsvc;
struct gprs_ns2_nse *nse = nsvc->nse;
- if (old_state != GPRS_NS2_ST_UNBLOCKED)
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_UNBLOCKED]);
+ if (old_state != GPRS_NS2_ST_UNBLOCKED) {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_UNBLOCKED);
+ osmo_clock_gettime(CLOCK_MONOTONIC, &nsvc->ts_alive_change);
+ }
priv->accept_unitdata = true;
ns2_nse_notify_unblocked(nsvc, true);
@@ -432,8 +449,13 @@ static void ns2_st_unblocked(struct osmo_fsm_inst *fi, uint32_t event, void *dat
ns2_tx_unblock_ack(priv->nsvc);
break;
case GPRS_NS2_EV_RX_BLOCK:
+ ns2_tx_block_ack(priv->nsvc, NULL);
+ /* fall through */
+ case GPRS_NS2_EV_RX_BLOCK_FOREIGN:
+ /* the BLOCK ACK for foreign BLOCK PDUs (rx over another nsvc) will be sent
+ * from the receiving nsvc */
priv->initiate_block = false;
- ns2_tx_block_ack(priv->nsvc);
+ priv->accept_unitdata = false;
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED,
0, 2);
break;
@@ -485,7 +507,8 @@ static const struct osmo_fsm_state ns2_vc_states[] = {
},
[GPRS_NS2_ST_BLOCKED] = {
.in_event_mask = S(GPRS_NS2_EV_RX_BLOCK) | S(GPRS_NS2_EV_RX_BLOCK_ACK) |
- S(GPRS_NS2_EV_RX_UNBLOCK) | S(GPRS_NS2_EV_RX_UNBLOCK_ACK),
+ S(GPRS_NS2_EV_RX_UNBLOCK) | S(GPRS_NS2_EV_RX_UNBLOCK_ACK) |
+ S(GPRS_NS2_EV_RX_BLOCK_FOREIGN),
.out_state_mask = S(GPRS_NS2_ST_RESET) |
S(GPRS_NS2_ST_UNBLOCKED) |
S(GPRS_NS2_ST_BLOCKED) |
@@ -496,7 +519,7 @@ static const struct osmo_fsm_state ns2_vc_states[] = {
},
[GPRS_NS2_ST_UNBLOCKED] = {
.in_event_mask = S(GPRS_NS2_EV_RX_BLOCK) | S(GPRS_NS2_EV_RX_UNBLOCK_ACK) |
- S(GPRS_NS2_EV_RX_UNBLOCK),
+ S(GPRS_NS2_EV_RX_UNBLOCK) | S(GPRS_NS2_EV_RX_BLOCK_FOREIGN),
.out_state_mask = S(GPRS_NS2_ST_RESET) | S(GPRS_NS2_ST_RECOVERING) |
S(GPRS_NS2_ST_BLOCKED) |
S(GPRS_NS2_ST_UNCONFIGURED),
@@ -525,7 +548,7 @@ static int ns2_vc_fsm_timer_cb(struct osmo_fsm_inst *fi)
switch (fi->state) {
case GPRS_NS2_ST_RESET:
if (priv->initiate_reset) {
- rate_ctr_inc(&priv->nsvc->ctrg->ctr[NS_CTR_LOST_RESET]);
+ RATE_CTR_INC_NS(priv->nsvc, NS_CTR_LOST_RESET);
priv->N++;
if (priv->N <= nsi->timeout[NS_TOUT_TNS_RESET_RETRIES]) {
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], 0);
@@ -538,7 +561,7 @@ static int ns2_vc_fsm_timer_cb(struct osmo_fsm_inst *fi)
case GPRS_NS2_ST_BLOCKED:
if (priv->initiate_block) {
priv->N++;
- if (priv->om_blocked) {
+ if (priv->nsvc->om_blocked) {
if (priv->N <= nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES]) {
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
} else {
@@ -610,7 +633,9 @@ static void ns2_vc_fsm_allstate_action(struct osmo_fsm_inst *fi,
{
struct gprs_ns2_vc_priv *priv = fi->priv;
struct gprs_ns2_inst *nsi = ns_inst_from_fi(fi);
+ struct tlv_parsed *tp;
struct msgb *msg = data;
+ uint8_t cause;
switch (event) {
case GPRS_NS2_EV_REQ_OM_RESET:
@@ -669,7 +694,7 @@ static void ns2_vc_fsm_allstate_action(struct osmo_fsm_inst *fi,
ns2_tx_status(priv->nsvc,
NS_CAUSE_NSVC_BLOCKED,
- 0, msg);
+ 0, msg, NULL);
break;
/* ALIVE can receive UNITDATA if the ALIVE_ACK is lost */
case GPRS_NS2_ST_RECOVERING:
@@ -690,17 +715,38 @@ static void ns2_vc_fsm_allstate_action(struct osmo_fsm_inst *fi,
case GPRS_NS2_EV_REQ_OM_BLOCK:
/* vty cmd: block */
priv->initiate_block = true;
- priv->om_blocked = true;
+ priv->nsvc->om_blocked = true;
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
break;
case GPRS_NS2_EV_REQ_OM_UNBLOCK:
/* vty cmd: unblock*/
- if (!priv->om_blocked)
+ if (!priv->nsvc->om_blocked)
return;
- priv->om_blocked = false;
+ priv->nsvc->om_blocked = false;
if (fi->state == GPRS_NS2_ST_BLOCKED)
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
break;
+ case GPRS_NS2_EV_RX_STATUS:
+ tp = data;
+ cause = tlvp_val8(tp, NS_IE_CAUSE, 0);
+ switch (cause) {
+ case NS_CAUSE_NSVC_BLOCKED:
+ if (fi->state != GPRS_NS2_ST_BLOCKED) {
+ LOG_NS_SIGNAL(priv->nsvc, "Rx", NS_PDUT_STATUS, LOGL_ERROR, ": remote side reported blocked state.\n");
+ priv->initiate_block = false;
+ priv->accept_unitdata = false;
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
+ }
+ break;
+ case NS_CAUSE_NSVC_UNKNOWN:
+ if (fi->state != GPRS_NS2_ST_RESET && fi->state != GPRS_NS2_ST_UNCONFIGURED) {
+ LOG_NS_SIGNAL(priv->nsvc, "Rx", NS_PDUT_STATUS, LOGL_ERROR, ": remote side reported unknown nsvc.\n");
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], 0);
+ }
+ break;
+ }
+
+ break;
}
}
@@ -720,6 +766,7 @@ static struct osmo_fsm ns2_vc_fsm = {
S(GPRS_NS2_EV_RX_RESET) |
S(GPRS_NS2_EV_RX_ALIVE) |
S(GPRS_NS2_EV_RX_ALIVE_ACK) |
+ S(GPRS_NS2_EV_RX_STATUS) |
S(GPRS_NS2_EV_REQ_FORCE_UNCONFIGURED) |
S(GPRS_NS2_EV_REQ_OM_RESET) |
S(GPRS_NS2_EV_REQ_OM_BLOCK) |
@@ -785,7 +832,7 @@ int ns2_vc_force_unconfigured(struct gprs_ns2_vc *nsvc)
int ns2_vc_block(struct gprs_ns2_vc *nsvc)
{
struct gprs_ns2_vc_priv *priv = nsvc->fi->priv;
- if (priv->om_blocked)
+ if (priv->nsvc->om_blocked)
return -EALREADY;
return osmo_fsm_inst_dispatch(nsvc->fi, GPRS_NS2_EV_REQ_OM_BLOCK, NULL);
@@ -797,7 +844,7 @@ int ns2_vc_block(struct gprs_ns2_vc *nsvc)
int ns2_vc_unblock(struct gprs_ns2_vc *nsvc)
{
struct gprs_ns2_vc_priv *priv = nsvc->fi->priv;
- if (!priv->om_blocked)
+ if (!priv->nsvc->om_blocked)
return -EALREADY;
return osmo_fsm_inst_dispatch(nsvc->fi, GPRS_NS2_EV_REQ_OM_UNBLOCK, NULL);
@@ -819,6 +866,7 @@ int ns2_vc_reset(struct gprs_ns2_vc *nsvc)
int ns2_vc_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp)
{
struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
+ struct gprs_ns2_vc *target_nsvc = nsvc;
struct osmo_fsm_inst *fi = nsvc->fi;
int rc = 0;
uint8_t cause;
@@ -829,8 +877,9 @@ int ns2_vc_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp)
/* TODO: handle BLOCK/UNBLOCK/ALIVE with different VCI */
if (ns2_validate(nsvc, nsh->pdu_type, msg, tp, &cause)) {
+ /* don't answer on a STATUS with a STATUS */
if (nsh->pdu_type != NS_PDUT_STATUS) {
- rc = ns2_tx_status(nsvc, cause, 0, msg);
+ rc = ns2_tx_status(nsvc, cause, 0, msg, NULL);
goto out;
}
}
@@ -842,20 +891,41 @@ int ns2_vc_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp)
if (nsh->pdu_type == NS_PDUT_RESET)
ns2_tx_reset_ack(nsvc);
- LOG_NS_SIGNAL(nsvc, "Rx", nsh->pdu_type, LOGL_ERROR, " with wrong NSEI=%05u. Ignoring PDU.\n", nsei);
+ LOG_NS_SIGNAL(nsvc, "Rx", nsh->pdu_type, LOGL_ERROR, " with wrong NSEI (exp: %05u, got %05u). Ignoring PDU.\n", nsvc->nse->nsei, nsei);
goto out;
}
}
- if (nsvc->nsvci_is_valid && TLVP_PRESENT(tp, NS_IE_VCI)) {
+ if (nsvc->nsvci_is_valid && TLVP_PRESENT(tp, NS_IE_VCI)) {
nsvci = tlvp_val16be(tp, NS_IE_VCI);
if (nsvci != nsvc->nsvci) {
/* 48.016 § 7.3.1 send RESET_ACK to wrong NSVCI + ignore */
- if (nsh->pdu_type == NS_PDUT_RESET)
+ if (nsh->pdu_type == NS_PDUT_RESET) {
ns2_tx_reset_ack(nsvc);
+ LOG_NS_SIGNAL(nsvc, "Rx", nsh->pdu_type, LOGL_ERROR, " with wrong NSVCI (exp: %05u, got %05u). Ignoring PDU.\n", nsvc->nsvci, nsvci);
+ goto out;
+ } else if (nsh->pdu_type == NS_PDUT_BLOCK || nsh->pdu_type == NS_PDUT_STATUS) {
+ /* this is a PDU received over a NSVC and reports a status/block for another NSVC */
+ target_nsvc = gprs_ns2_nsvc_by_nsvci(nsvc->nse->nsi, nsvci);
+ if (!target_nsvc) {
+ LOGPFSML(fi, LOGL_ERROR, "Received a %s PDU for unknown NSVC (NSVCI %d)\n",
+ get_value_string(gprs_ns_pdu_strings, nsh->pdu_type), nsvci);
+ if (nsh->pdu_type == NS_PDUT_BLOCK)
+ ns2_tx_status(nsvc, NS_CAUSE_NSVC_UNKNOWN, 0, msg, &nsvci);
+ goto out;
+ }
- LOG_NS_SIGNAL(nsvc, "Rx", nsh->pdu_type, LOGL_ERROR, " with wrong NSVCI=%05u. Ignoring PDU.\n", nsvci);
- goto out;
+ if (target_nsvc->nse != nsvc->nse) {
+ LOGPFSML(fi, LOGL_ERROR, "Received a %s PDU for a NSVC (NSVCI %d) but it belongs to a different NSE!\n",
+ get_value_string(gprs_ns_pdu_strings, nsh->pdu_type), nsvci);
+ goto out;
+ }
+
+ /* the status/block will be passed to the nsvc/target nsvc in the switch */
+ } else {
+ LOG_NS_SIGNAL(nsvc, "Rx", nsh->pdu_type, LOGL_ERROR, " with wrong NSVCI=%05u. Ignoring PDU.\n", nsvci);
+ goto out;
+ }
}
}
@@ -867,7 +937,12 @@ int ns2_vc_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp)
osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_RESET_ACK, tp);
break;
case NS_PDUT_BLOCK:
- osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_BLOCK, tp);
+ if (target_nsvc != nsvc) {
+ osmo_fsm_inst_dispatch(target_nsvc->fi, GPRS_NS2_EV_RX_BLOCK_FOREIGN, tp);
+ ns2_tx_block_ack(nsvc, &nsvci);
+ } else {
+ osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_BLOCK, tp);
+ }
break;
case NS_PDUT_BLOCK_ACK:
osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_BLOCK_ACK, tp);
@@ -888,6 +963,9 @@ int ns2_vc_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp)
/* UNITDATA have to free msg because it might send the msg layer upwards */
osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_UNITDATA, msg);
return 0;
+ case NS_PDUT_STATUS:
+ osmo_fsm_inst_dispatch(target_nsvc->fi, GPRS_NS2_EV_RX_STATUS, tp);
+ break;
default:
LOGPFSML(fi, LOGL_ERROR, "NSEI=%u Rx unknown NS PDU type %s\n", nsvc->nse->nsei,
get_value_string(gprs_ns_pdu_strings, nsh->pdu_type));
diff --git a/src/gb/gprs_ns2_vty.c b/src/gb/gprs_ns2_vty.c
index a782c5cf..32de49d4 100644
--- a/src/gb/gprs_ns2_vty.c
+++ b/src/gb/gprs_ns2_vty.c
@@ -36,6 +36,7 @@
#include <osmocom/core/fsm.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/osmo_io.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/select.h>
#include <osmocom/core/talloc.h>
@@ -90,17 +91,18 @@ struct vty_nse_bind {
};
/* TODO: this should into osmo timer */
-static const struct value_string gprs_ns_timer_strs[] = {
- { 0, "tns-block" },
- { 1, "tns-block-retries" },
- { 2, "tns-reset" },
- { 3, "tns-reset-retries" },
- { 4, "tns-test" },
- { 5, "tns-alive" },
- { 6, "tns-alive-retries" },
- { 7, "tsns-prov" },
- { 8, "tsns-size-retries" },
- { 9, "tsns-config-retries" },
+const struct value_string gprs_ns_timer_strs[] = {
+ { NS_TOUT_TNS_BLOCK, TNS_BLOCK_STR },
+ { NS_TOUT_TNS_BLOCK_RETRIES, TNS_BLOCK_RETRIES_STR },
+ { NS_TOUT_TNS_RESET, TNS_RESET_STR },
+ { NS_TOUT_TNS_RESET_RETRIES, TNS_RESET_RETRIES_STR },
+ { NS_TOUT_TNS_TEST, TNS_TEST_STR },
+ { NS_TOUT_TNS_ALIVE, TNS_ALIVE_STR },
+ { NS_TOUT_TNS_ALIVE_RETRIES, TNS_ALIVE_RETRIES_STR },
+ { NS_TOUT_TSNS_PROV, TSNS_PROV_STR },
+ { NS_TOUT_TSNS_SIZE_RETRIES, TSNS_SIZE_RETRIES_STR },
+ { NS_TOUT_TSNS_CONFIG_RETRIES, TSNS_CONFIG_RETRIES_STR },
+ { NS_TOUT_TSNS_PROCEDURES_RETRIES, TSNS_PROCEDURES_RETRIES_STR },
{ 0, NULL }
};
@@ -141,7 +143,7 @@ static struct vty_bind *vty_bind_alloc(const char *name)
vbind->ip_sns_sig_weight = 1;
vbind->ip_sns_data_weight = 1;
- llist_add(&vbind->list, &binds);
+ llist_add_tail(&vbind->list, &binds);
return vbind;
}
@@ -172,7 +174,7 @@ static struct vty_nse *vty_nse_alloc(uint16_t nsei)
vnse->nsei = nsei;
INIT_LLIST_HEAD(&vnse->binds);
- llist_add(&vnse->list, &nses);
+ llist_add_tail(&vnse->list, &nses);
return vnse;
}
@@ -217,6 +219,7 @@ static int vty_nse_remove_vbind(struct vty_nse *vnse, struct vty_bind *vbind)
if (vnse_bind->vbind == vbind) {
llist_del(&vnse_bind->list);
talloc_free(vnse_bind);
+ return 0;
}
}
@@ -422,7 +425,7 @@ DEFUN(cfg_no_ns_bind, cfg_no_ns_bind_cmd,
vty_bind_free(vbind);
bind = gprs_ns2_bind_by_name(vty_nsi, name);
if (bind)
- bind->driver->free_bind(bind);
+ gprs_ns2_free_bind(bind);
return CMD_SUCCESS;
}
@@ -593,6 +596,9 @@ static int config_write_ns(struct vty *vty)
get_value_string(gprs_ns_timer_strs, i),
vty_nsi->timeout[i], VTY_NEWLINE);
+ if (vty_nsi->txqueue_max_length != NS_DEFAULT_TXQUEUE_MAX_LENGTH)
+ vty_out(vty, " txqueue-max-length %u%s", vty_nsi->txqueue_max_length, VTY_NEWLINE);
+
ret = config_write_ns_bind(vty);
if (ret)
return ret;
@@ -677,7 +683,7 @@ DEFUN(cfg_no_ns_bind_listen, cfg_no_ns_bind_listen_cmd,
return CMD_ERR_NOTHING_TODO;
OSMO_ASSERT(bind->ll == GPRS_NS2_LL_UDP);
- bind->driver->free_bind(bind);
+ gprs_ns2_free_bind(bind);
return CMD_SUCCESS;
}
@@ -938,7 +944,7 @@ DEFUN(cfg_no_ns_bind_fr, cfg_no_ns_bind_fr_cmd,
return CMD_WARNING;
}
- bind->driver->free_bind(bind);
+ gprs_ns2_free_bind(bind);
return CMD_SUCCESS;
}
@@ -1705,6 +1711,27 @@ DEFUN(cfg_no_ns_ip_sns_default_bind, cfg_no_ns_ip_sns_default_bind_cmd,
return CMD_WARNING;
}
+DEFUN(cfg_ns_txqueue_max_length, cfg_ns_txqueue_max_length_cmd,
+ "txqueue-max-length <1-4096>",
+ "Set the maximum length of the txqueue (for UDP)\n"
+ "Maximum length of the txqueue\n")
+{
+ struct gprs_ns2_vc_bind *bind;
+ uint32_t max_length = atoi(argv[0]);
+ vty_nsi->txqueue_max_length = max_length;
+
+
+ llist_for_each_entry(bind, &vty_nsi->binding, list) {
+ if (!gprs_ns2_is_ip_bind(bind))
+ continue;
+
+ ns2_ip_set_txqueue_max_length(bind, max_length);
+ }
+
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_ns_nse_ip_sns_bind, cfg_ns_nse_ip_sns_bind_cmd,
"ip-sns-bind BINDID",
"IP SNS binds\n"
@@ -1872,18 +1899,25 @@ DEFUN(cfg_no_ns_nse_ip_sns_bind, cfg_no_ns_nse_ip_sns_bind_cmd,
/* non-config commands */
void ns2_vty_dump_nsvc(struct vty *vty, struct gprs_ns2_vc *nsvc, bool stats)
{
- char nsvci_str[32];
-
if (nsvc->nsvci_is_valid)
- snprintf(nsvci_str, sizeof(nsvci_str), "%05u", nsvc->nsvci);
+ vty_out(vty, " NSVCI %05u: %s %s %s %s %ssince ", nsvc->nsvci,
+ osmo_fsm_inst_state_name(nsvc->fi),
+ nsvc->persistent ? "PERSIST" : "DYNAMIC",
+ gprs_ns2_ll_str(nsvc),
+ ns2_vc_is_unblocked(nsvc) ? "ALIVE" : "DEAD",
+ nsvc->om_blocked ? "(blocked by O&M/vty) " :
+ !ns2_vc_is_unblocked(nsvc) ? "(cause: remote) " : "");
else
- snprintf(nsvci_str, sizeof(nsvci_str), "none");
+ vty_out(vty, " %s %s sig_weight=%u data_weight=%u %s %s %ssince ",
+ osmo_fsm_inst_state_name(nsvc->fi),
+ nsvc->persistent ? "PERSIST" : "DYNAMIC",
+ nsvc->sig_weight, nsvc->data_weight,
+ gprs_ns2_ll_str(nsvc),
+ ns2_vc_is_unblocked(nsvc) ? "ALIVE" : "DEAD",
+ !ns2_vc_is_unblocked(nsvc) ? "(cause: remote) " : "");
- vty_out(vty, " NSVCI %s: %s %s data_weight=%u sig_weight=%u %s%s", nsvci_str,
- osmo_fsm_inst_state_name(nsvc->fi),
- nsvc->persistent ? "PERSIST" : "DYNAMIC",
- nsvc->data_weight, nsvc->sig_weight,
- gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
+ vty_out_uptime(vty, &nsvc->ts_alive_change);
+ vty_out_newline(vty);
if (stats) {
vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
@@ -1899,8 +1933,10 @@ static void dump_nse(struct vty *vty, const struct gprs_ns2_nse *nse, bool stats
if (persistent_only && !nse->persistent)
return;
- vty_out(vty, "NSEI %05u: %s, %s%s", nse->nsei, gprs_ns2_lltype_str(nse->ll),
- nse->alive ? "ALIVE" : "DEAD", VTY_NEWLINE);
+ vty_out(vty, "NSEI %05u: %s, %s since ", nse->nsei, gprs_ns2_lltype_str(nse->ll),
+ nse->alive ? "ALIVE" : "DEAD");
+ vty_out_uptime(vty, &nse->ts_alive_change);
+ vty_out_newline(vty);
ns2_sns_dump_vty(vty, " ", nse, stats);
llist_for_each_entry(nsvc, &nse->nsvc, list) {
@@ -2061,6 +2097,31 @@ DEFUN_HIDDEN(nsvc_force_unconf, nsvc_force_unconf_cmd,
return CMD_SUCCESS;
}
+DEFUN(nse_restart_sns, nse_restart_sns_cmd,
+ "nse <0-65535> restart-sns",
+ "NSE specific commands\n"
+ "NS Entity ID (NSEI)\n"
+ "Restart SNS procedure\n")
+{
+ struct gprs_ns2_inst *nsi = vty_nsi;
+ struct gprs_ns2_nse *nse;
+
+ uint16_t id = atoi(argv[0]);
+ nse = gprs_ns2_nse_by_nsei(nsi, id);
+ if (!nse) {
+ vty_out(vty, "Could not find NSE for NSEI %u%s", id, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
+ vty_out(vty, "Given NSEI %u doesn't use IP-SNS%s", id, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gprs_ns2_free_nsvcs(nse);
+ return CMD_SUCCESS;
+}
+
DEFUN(nsvc_block, nsvc_block_cmd,
"nsvc <0-65535> (block|unblock|reset)",
"NS Virtual Connection\n"
@@ -2228,6 +2289,7 @@ int gprs_ns2_vty_init_reduced(struct gprs_ns2_inst *nsi)
install_lib_element(ENABLE_NODE, &nsvc_force_unconf_cmd);
install_lib_element(ENABLE_NODE, &nsvc_block_cmd);
+ install_lib_element(ENABLE_NODE, &nse_restart_sns_cmd);
install_lib_element(CFG_LOG_NODE, &logging_fltr_nse_cmd);
install_lib_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
@@ -2255,6 +2317,8 @@ int gprs_ns2_vty_init(struct gprs_ns2_inst *nsi)
install_lib_element(L_NS_NODE, &cfg_ns_ip_sns_default_bind_cmd);
install_lib_element(L_NS_NODE, &cfg_no_ns_ip_sns_default_bind_cmd);
+ install_lib_element(L_NS_NODE, &cfg_ns_txqueue_max_length_cmd);
+
install_node(&ns_bind_node, NULL);
install_lib_element(L_NS_BIND_NODE, &cfg_ns_bind_listen_cmd);
install_lib_element(L_NS_BIND_NODE, &cfg_no_ns_bind_listen_cmd);
diff --git a/src/gb/libosmogb.map b/src/gb/libosmogb.map
index ff5b34a2..e5a5c8fd 100644
--- a/src/gb/libosmogb.map
+++ b/src/gb/libosmogb.map
@@ -34,6 +34,7 @@ bssgp_nacc_cause_strs;
bssgp_parse_cell_id;
bssgp_parse_rim_pdu;
bssgp_parse_rim_ri;
+bssgp_parse_rim_ra;
bssgp_ran_inf_app_id_strs;
bssgp_rim_routing_info_discr_strs;
bssgp_rim_ri_name_buf;
@@ -56,6 +57,7 @@ bssgp_tx_resume;
bssgp_tx_resume_ack;
bssgp_tx_resume_nack;
bssgp_tx_rim;
+bssgp_tx_rim_encoded;
bssgp_tx_simple_bvci;
bssgp_tx_status;
bssgp_tx_suspend;
@@ -85,6 +87,7 @@ bssgp2_enc_fc_bvc;
bssgp2_enc_fc_bvc_ack;
bssgp2_enc_fc_ms;
bssgp2_enc_fc_ms_ack;
+bssgp2_enc_flush_ll;
bssgp2_enc_status;
bssgp_bvc_fsm_alloc_sig_bss;
diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am
index f1e2a5a5..e6b687d2 100644
--- a/src/gsm/Makefile.am
+++ b/src/gsm/Makefile.am
@@ -1,10 +1,10 @@
# 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=16:0:0
+LIBVERSION=20:0:0
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS)
-AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN}
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
if ENABLE_PSEUDOTALLOC
AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
@@ -15,6 +15,8 @@ noinst_HEADERS = milenage/aes.h milenage/aes_i.h milenage/aes_wrap.h \
milenage/common.h milenage/crypto.h milenage/includes.h \
milenage/milenage.h
+noinst_HEADERS += tuak/KeccakP-1600-3gpp.h tuak/tuak.h
+
noinst_LTLIBRARIES = libgsmint.la
lib_LTLIBRARIES = libosmogsm.la
@@ -25,28 +27,38 @@ libgsmint_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \
gsm48_ie.c gsm0808.c sysinfo.c \
gprs_cipher_core.c gprs_rlc.c gsm0480.c abis_nm.c gsm0502.c \
gsm0411_utils.c gsm0411_smc.c gsm0411_smr.c gsm0414.c \
- lapd_core.c lapdm.c kasumi.c gsm29205.c gsm_04_08_gprs.c \
- auth_core.c auth_comp128v1.c auth_comp128v23.c auth_xor.c \
+ lapdm.c kasumi.c gsm29205.c gsm_04_08_gprs.c \
+ auth_core.c auth_comp128v1.c auth_comp128v23.c auth_xor.c auth_xor_2g.c \
auth_milenage.c milenage/aes-encblock.c gea.c \
milenage/aes-internal.c milenage/aes-internal-enc.c \
milenage/milenage.c gan.c ipa.c gsm0341.c apn.c \
- gsup.c gsup_sms.c gprs_gea.c gsm0503_conv.c oap.c gsm0808_utils.c \
+ tuak/KeccakP-1600-3gpp.c tuak/tuak.c auth_tuak.c \
+ gprs_gea.c gsm0503_conv.c oap.c gsm0808_utils.c \
gsm23003.c gsm23236.c mncc.c bts_features.c oap_client.c \
- gsm29118.c gsm48_rest_octets.c cbsp.c gsm48049.c i460_mux.c \
- gad.c bsslap.c bssmap_le.c
+ gsm29118.c gsm48_rest_octets.c cbsp.c gsm48049.c \
+ gad.c bsslap.c bssmap_le.c kdf.c iuup.c gsm44021.c gsm44068.c rlp.c
+if !EMBEDDED
+libgsmint_la_SOURCES += gsup.c gsup_sms.c
+endif # !EMBEDDED
+
libgsmint_la_LDFLAGS = -no-undefined
-libgsmint_la_LIBADD = $(top_builddir)/src/libosmocore.la
+libgsmint_la_LIBADD = $(top_builddir)/src/core/libosmocore.la $(top_builddir)/src/isdn/libosmoisdn.la
libosmogsm_la_SOURCES =
libosmogsm_la_LDFLAGS = $(LTLDFLAGS_OSMOGSM) -version-info $(LIBVERSION) -no-undefined
libosmogsm_la_LIBADD = libgsmint.la $(TALLOC_LIBS)
if ENABLE_GNUTLS
-AM_CPPFLAGS += $(LIBGNUTLS_CFLAGS)
+AM_CFLAGS += $(LIBGNUTLS_CFLAGS)
libosmogsm_la_LIBADD += $(LIBGNUTLS_LIBS)
+else
+noinst_HEADERS += kdf/sha1.h kdf/sha256.h kdf/common.h kdf/sha1_i.h kdf/sha256_i.h
+libgsmint_la_SOURCES += kdf/sha256.c kdf/sha256-internal.c \
+ kdf/sha1.c kdf/sha1-internal.c
endif
EXTRA_DIST = libosmogsm.map
+EXTRA_libosmogsm_la_DEPENDENCIES = libosmogsm.map
# Convolutional codes generation
gsm0503_conv.c: $(top_srcdir)/utils/conv_gen.py $(top_srcdir)/utils/conv_codes_gsm.py
diff --git a/src/gsm/a5.c b/src/gsm/a5.c
index 223d3ad8..d223cb76 100644
--- a/src/gsm/a5.c
+++ b/src/gsm/a5.c
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*! \addtogroup a5
diff --git a/src/gsm/abis_nm.c b/src/gsm/abis_nm.c
index f7793d79..c4060e01 100644
--- a/src/gsm/abis_nm.c
+++ b/src/gsm/abis_nm.c
@@ -703,10 +703,104 @@ static const enum abis_nm_chan_comb chcomb4pchan[] = {
[GSM_PCHAN_UNKNOWN] = 0xff,
[GSM_PCHAN_CCCH_SDCCH4_CBCH] = NM_CHANC_BCCH_CBCH,
[GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = NM_CHANC_SDCCH_CBCH,
- [GSM_PCHAN_TCH_F_TCH_H_PDCH] = NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH,
+ [GSM_PCHAN_OSMO_DYN] = NM_CHANC_OSMO_DYN,
/* FIXME: bounds check */
};
+const struct value_string abis_nm_ipacc_freq_band_desc[] = {
+ { NM_IPAC_F_FREQ_BAND_PGSM, "P-GSM" },
+ { NM_IPAC_F_FREQ_BAND_EGSM, "E-GSM" },
+ { NM_IPAC_F_FREQ_BAND_RGSM, "R-GSM" },
+ { NM_IPAC_F_FREQ_BAND_DCS, "DCS-1800" },
+ { NM_IPAC_F_FREQ_BAND_PCS, "PCS-1900" },
+ { NM_IPAC_F_FREQ_BAND_850, "850" },
+ { NM_IPAC_F_FREQ_BAND_480, "480" },
+ { NM_IPAC_F_FREQ_BAND_450, "450" },
+ { 0, NULL }
+};
+
+const struct value_string abis_nm_ipacc_ciph_algo_desc[] = {
+ { NM_IPAC_F_CIPH_ALGO_A51, "A5/1" },
+ { NM_IPAC_F_CIPH_ALGO_A52, "A5/2" },
+ { NM_IPAC_F_CIPH_ALGO_A53, "A5/3" },
+ { NM_IPAC_F_CIPH_ALGO_A54, "A5/4" },
+ { NM_IPAC_F_CIPH_ALGO_A55, "A5/5" },
+ { NM_IPAC_F_CIPH_ALGO_A56, "A5/6" },
+ { NM_IPAC_F_CIPH_ALGO_A57, "A5/7" },
+ { NM_IPAC_F_CIPH_ALGO_A58, "A5/8" },
+ { 0, NULL }
+};
+
+const struct value_string abis_nm_ipacc_chant_desc[] = {
+ { NM_IPAC_F_CHANT_TCHF, "TCH/F" },
+ { NM_IPAC_F_CHANT_TCHH, "TCH/H" },
+ { NM_IPAC_F_CHANT_SDCCH8, "SDCCH/8" },
+ { NM_IPAC_F_CHANT_BCCH, "BCCH" },
+ { NM_IPAC_F_CHANT_BCCH_SDCCH4, "BCCH+SDCCH/4" },
+ { NM_IPAC_F_CHANT_BCH, "BCH" },
+ { NM_IPAC_F_CHANT_BCCH_SDCCH4_CBCH, "BCCH+SDCCH/4+CBCH" },
+ { NM_IPAC_F_CHANT_SDCCH8_CBCH, "SDCCH/8+CBCH" },
+ { NM_IPAC_F_CHANT_PDCHF, "PDCH/F" },
+ { NM_IPAC_F_CHANT_TCHF_PDCHF, "TCH/F or PDCH/F" },
+ { NM_IPAC_F_CHANT_TCHH_PDCHH, "TCH/H+PDCH/H" },
+ { NM_IPAC_F_CHANT_TCHF_TCHH, "TCH/F or TCH/H" },
+ { 0, NULL }
+};
+
+const struct value_string abis_nm_ipacc_chanm_desc[] = {
+ { NM_IPAC_F_CHANM_SPEECH_FS, "FR1/FS" },
+ { NM_IPAC_F_CHANM_SPEECH_EFS, "FR2/EFS" },
+ { NM_IPAC_F_CHANM_SPEECH_AFS, "FR3/AFS" },
+ { NM_IPAC_F_CHANM_SPEECH_HS, "HR1/HS" },
+ { NM_IPAC_F_CHANM_SPEECH_AHS, "HR3/AHS" },
+ { NM_IPAC_F_CHANM_CSD_NT_4k8, "CSD NT 4.8 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_NT_9k6, "CSD NT 9.6 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_NT_14k4, "CSD NT 14.4 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_1200_75, "CSD T 1200/75 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_600, "CSD T 600 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_1k2, "CSD T 1k2 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_2k4, "CSD T 2.4 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_4k8, "CSD T 4.8 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_9k6, "CSD T 9.6 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_14k4, "CSD T 14.4 kbit/s" },
+ { 0, NULL }
+};
+
+const struct value_string abis_nm_ipacc_gprs_coding_desc[] = {
+ { NM_IPAC_F_GPRS_CODING_CS1, "CS1" },
+ { NM_IPAC_F_GPRS_CODING_CS2, "CS2" },
+ { NM_IPAC_F_GPRS_CODING_CS3, "CS3" },
+ { NM_IPAC_F_GPRS_CODING_CS4, "CS4" },
+ { NM_IPAC_F_GPRS_CODING_MCS1, "MCS1" },
+ { NM_IPAC_F_GPRS_CODING_MCS2, "MCS2" },
+ { NM_IPAC_F_GPRS_CODING_MCS3, "MCS3" },
+ { NM_IPAC_F_GPRS_CODING_MCS4, "MCS4" },
+ { NM_IPAC_F_GPRS_CODING_MCS5, "MCS5" },
+ { NM_IPAC_F_GPRS_CODING_MCS6, "MCS6" },
+ { NM_IPAC_F_GPRS_CODING_MCS7, "MCS7" },
+ { NM_IPAC_F_GPRS_CODING_MCS8, "MCS8" },
+ { NM_IPAC_F_GPRS_CODING_MCS9, "MCS9" },
+ { 0, NULL }
+};
+
+const struct value_string abis_nm_ipacc_rtp_feat_desc[] = {
+ { NM_IPAC_F_RTP_FEAT_COMPR_CONTROL, "Compression Control" },
+ { NM_IPAC_F_RTP_FEAT_IR_8k, "Intermediate Rate 8 kbit/s" },
+ { NM_IPAC_F_RTP_FEAT_IR_16k, "Intermediate Rate 16 kbit/s" },
+ { NM_IPAC_F_RTP_FEAT_IR_32k, "Intermediate Rate 32 kbit/s" },
+ { NM_IPAC_F_RTP_FEAT_IR_64k, "Intermediate Rate 64 kbit/s" },
+ { NM_IPAC_F_RTP_FEAT_MULTIPLEX_RTP, "RTP Multiplex" },
+ { NM_IPAC_F_RTP_FEAT_MULTIPLEX_SRTP, "SRTP Multiplex" },
+ { 0, NULL }
+};
+
+const struct value_string abis_nm_ipacc_rsl_feat_desc[] = {
+ { NM_IPAC_F_RSL_FEAT_PHYSICAL_CONTEXT, "Physical Context" },
+ { NM_IPAC_F_RSL_FEAT_DYN_PDCH_ACT, "Dynamic PDCH Activation" },
+ { NM_IPAC_F_RSL_FEAT_RTP_PT2, "RTP Payload Type 2" },
+ { 0, NULL }
+};
+
/*! Pack 3GPP TS 12.21 § 8.8.2 Failure Event Report into msgb */
struct msgb *abis_nm_fail_evt_rep(enum abis_nm_event_type t,
enum abis_nm_severity s,
diff --git a/src/gsm/apn.c b/src/gsm/apn.c
index a7074eab..4800702c 100644
--- a/src/gsm/apn.c
+++ b/src/gsm/apn.c
@@ -24,8 +24,8 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-#include <talloc.h>
+#include <osmocom/core/talloc.h>
#include <osmocom/gsm/apn.h>
#define APN_OI_GPRS_FMT "mnc%03u.mcc%03u.gprs"
diff --git a/src/gsm/auth_comp128v1.c b/src/gsm/auth_comp128v1.c
index 493ebfd0..ded3f8a6 100644
--- a/src/gsm/auth_comp128v1.c
+++ b/src/gsm/auth_comp128v1.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/crypt/auth.h>
@@ -31,9 +27,10 @@
*/
static int c128v1_gen_vec(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *_rand)
{
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_COMP128v1);
comp128v1(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
vec->auth_types = OSMO_AUTH_TYPE_GSM;
diff --git a/src/gsm/auth_comp128v23.c b/src/gsm/auth_comp128v23.c
index 279d2b74..f942bc02 100644
--- a/src/gsm/auth_comp128v23.c
+++ b/src/gsm/auth_comp128v23.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/crypt/auth.h>
@@ -33,9 +29,10 @@
*/
static int c128v2_gen_vec(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *_rand)
{
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_COMP128v2);
comp128v2(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
vec->auth_types = OSMO_AUTH_TYPE_GSM;
@@ -50,9 +47,10 @@ static struct osmo_auth_impl c128v2_alg = {
};
static int c128v3_gen_vec(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *_rand)
{
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_COMP128v3);
comp128v3(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
vec->auth_types = OSMO_AUTH_TYPE_GSM;
diff --git a/src/gsm/auth_core.c b/src/gsm/auth_core.c
index 9e750a01..6972cb77 100644
--- a/src/gsm/auth_core.c
+++ b/src/gsm/auth_core.c
@@ -1,4 +1,4 @@
-/* (C) 2010-2012 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2010-2023 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include "config.h"
@@ -40,6 +36,35 @@
static LLIST_HEAD(osmo_auths);
+/* generate auth_data2 from auth_data (for legacy API/ABI compatibility */
+static int auth_data2auth_data2(struct osmo_sub_auth_data2 *out, const struct osmo_sub_auth_data *in)
+{
+ out->type = in->type;
+ out->algo = in->algo;
+ switch (in->type) {
+ case OSMO_AUTH_TYPE_NONE:
+ return 0;
+ case OSMO_AUTH_TYPE_GSM:
+ memcpy(out->u.gsm.ki, in->u.gsm.ki, sizeof(out->u.gsm.ki));
+ break;
+ case OSMO_AUTH_TYPE_UMTS:
+ memcpy(out->u.umts.opc, in->u.umts.opc, sizeof(in->u.umts.opc));
+ out->u.umts.opc_len = sizeof(in->u.umts.opc);
+ memcpy(out->u.umts.k, in->u.umts.k, sizeof(in->u.umts.k));
+ out->u.umts.k_len = sizeof(in->u.umts.k);
+ memcpy(out->u.umts.amf, in->u.umts.amf, sizeof(out->u.umts.amf));
+ out->u.umts.sqn = in->u.umts.sqn;
+ out->u.umts.opc_is_op = in->u.umts.opc_is_op;
+ out->u.umts.ind_bitlen = in->u.umts.ind_bitlen;
+ out->u.umts.ind = in->u.umts.ind;
+ out->u.umts.sqn_ms = in->u.umts.sqn_ms;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
static struct osmo_auth_impl *selected_auths[_OSMO_AUTH_ALG_NUM];
/*! Register an authentication algorithm implementation with the core
@@ -142,6 +167,42 @@ int osmo_auth_3g_from_2g(struct osmo_auth_vector *vec)
}
/*! Generate authentication vector
+ * \param[out] vec Generated authentication vector. See below!
+ * \param[in] aud Subscriber-specific key material
+ * \param[in] _rand Random challenge to be used
+ * \returns 0 on success, negative error on failure
+ *
+ * This function performs the core cryptographic function of the AUC,
+ * computing authentication triples/quintuples based on the permanent
+ * subscriber data and a random value. The result is what is forwarded
+ * by the AUC via HLR and VLR to the MSC which will then be able to
+ * invoke authentication with the MS.
+ *
+ * Contrary to the older osmo_auth_gen_vec(), the caller must specify
+ * the desired RES length in the vec->res_len field prior to calling
+ * this function. The requested length must match the capabilities of
+ * the chosen algorithm (e.g. 4/8 for MILENAGE).
+ */
+int osmo_auth_gen_vec2(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *_rand)
+{
+ struct osmo_auth_impl *impl = selected_auths[aud->algo];
+ int rc;
+
+ if (!impl)
+ return -ENOENT;
+
+ rc = impl->gen_vec(vec, aud, _rand);
+ if (rc < 0)
+ return rc;
+
+ memcpy(vec->rand, _rand, sizeof(vec->rand));
+
+ return 0;
+}
+
+/*! Generate authentication vector
* \param[out] vec Generated authentication vector
* \param[in] aud Subscriber-specific key material
* \param[in] _rand Random challenge to be used
@@ -157,13 +218,58 @@ int osmo_auth_gen_vec(struct osmo_auth_vector *vec,
struct osmo_sub_auth_data *aud,
const uint8_t *_rand)
{
+ struct osmo_sub_auth_data2 aud2;
+ int rc;
+
+ if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+ /* old API callers are not expected to initialize this struct field,
+ * and always expect an 8-byte RES value */
+ vec->res_len = 8;
+ }
+
+ rc = auth_data2auth_data2(&aud2, aud);
+ if (rc < 0)
+ return rc;
+
+ rc = osmo_auth_gen_vec2(vec, &aud2, _rand);
+ if (aud->type == OSMO_AUTH_TYPE_UMTS)
+ aud->u.umts.sqn = aud2.u.umts.sqn;
+
+ return rc;
+}
+
+/*! Generate authentication vector and re-sync sequence
+ * \param[out] vec Generated authentication vector. See below!
+ * \param[in] aud Subscriber-specific key material
+ * \param[in] auts AUTS value sent by the SIM/MS
+ * \param[in] rand_auts RAND value sent by the SIM/MS
+ * \param[in] _rand Random challenge to be used to generate vector
+ * \returns 0 on success, negative error on failure
+ *
+ * This function performs a special variant of the core cryptographic
+ * function of the AUC: computing authentication triples/quintuples
+ * based on the permanent subscriber data, a random value as well as the
+ * AUTS and RAND values returned by the SIM/MS. This special variant is
+ * needed if the sequence numbers between MS and AUC have for some
+ * reason become different.
+ *
+ * Contrary to the older osmo_auth_gen_vec_auts(), the caller must specify
+ * the desired RES length in the vec->res_len field prior to calling
+ * this function. The requested length must match the capabilities of
+ * the chosen algorithm (e.g. 4/8 for MILENAGE).
+ */
+int osmo_auth_gen_vec_auts2(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *auts, const uint8_t *rand_auts,
+ const uint8_t *_rand)
+{
struct osmo_auth_impl *impl = selected_auths[aud->algo];
int rc;
- if (!impl)
+ if (!impl || !impl->gen_vec_auts)
return -ENOENT;
- rc = impl->gen_vec(vec, aud, _rand);
+ rc = impl->gen_vec_auts(vec, aud, auts, rand_auts, _rand);
if (rc < 0)
return rc;
@@ -192,19 +298,26 @@ int osmo_auth_gen_vec_auts(struct osmo_auth_vector *vec,
const uint8_t *auts, const uint8_t *rand_auts,
const uint8_t *_rand)
{
- struct osmo_auth_impl *impl = selected_auths[aud->algo];
+ struct osmo_sub_auth_data2 aud2;
int rc;
- if (!impl || !impl->gen_vec_auts)
- return -ENOENT;
+ if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+ /* old API callers are not expected to initialize this struct field,
+ * and always expect an 8-byte RES value */
+ vec->res_len = 8;
+ }
- rc = impl->gen_vec_auts(vec, aud, auts, rand_auts, _rand);
+ rc = auth_data2auth_data2(&aud2, aud);
if (rc < 0)
return rc;
- memcpy(vec->rand, _rand, sizeof(vec->rand));
+ rc = osmo_auth_gen_vec_auts2(vec, &aud2, auts, rand_auts, _rand);
+ if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+ aud->u.umts.sqn = aud2.u.umts.sqn;
+ aud->u.umts.sqn_ms = aud2.u.umts.sqn_ms;
+ }
- return 0;
+ return rc;
}
static const struct value_string auth_alg_vals[] = {
@@ -212,8 +325,10 @@ static const struct value_string auth_alg_vals[] = {
{ OSMO_AUTH_ALG_COMP128v1, "COMP128v1" },
{ OSMO_AUTH_ALG_COMP128v2, "COMP128v2" },
{ OSMO_AUTH_ALG_COMP128v3, "COMP128v3" },
- { OSMO_AUTH_ALG_XOR, "XOR" },
+ { OSMO_AUTH_ALG_XOR_3G, "XOR-3G" },
{ OSMO_AUTH_ALG_MILENAGE, "MILENAGE" },
+ { OSMO_AUTH_ALG_XOR_2G, "XOR-2G" },
+ { OSMO_AUTH_ALG_TUAK, "TUAK" },
{ 0, NULL }
};
@@ -249,4 +364,33 @@ void osmo_auth_c3(uint8_t kc[], const uint8_t ck[], const uint8_t ik[])
kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8];
}
+/*! Derive GSM SRES from UMTS [X]RES (auth function c2 from 3GPP TS 33.103 Section 6.8.1.2
+ * \param[out] sres GSM SRES value, 4 byte target buffer
+ * \param[in] res UMTS XRES, 4..16 bytes input buffer
+ * \param[in] res_len length of res parameter (in bytes)
+ * \param[in] sres_deriv_func SRES derivation function (1 or 2, see 3GPP TS 55.205 Section 4
+ */
+void osmo_auth_c2(uint8_t sres[4], const uint8_t *res, size_t res_len, uint8_t sres_deriv_func)
+{
+ uint8_t xres[16];
+
+ OSMO_ASSERT(sres_deriv_func == 1 || sres_deriv_func == 2);
+ OSMO_ASSERT(res_len <= sizeof(xres));
+
+ memcpy(xres, res, res_len);
+
+ /* zero-pad the end, if XRES is < 16 bytes */
+ if (res_len < sizeof(xres))
+ memset(xres+res_len, 0, sizeof(xres)-res_len);
+
+ if (sres_deriv_func == 1) {
+ /* SRES derivation function #1 */
+ for (unsigned int i = 0; i < 4; i++)
+ sres[i] = xres[i] ^ xres[4+i] ^ xres[8+i] ^ xres[12+i];
+ } else {
+ /* SRES derivation function #2 */
+ memcpy(sres, xres, 4);
+ }
+}
+
/*! @} */
diff --git a/src/gsm/auth_milenage.c b/src/gsm/auth_milenage.c
index 95891000..2bd05a6d 100644
--- a/src/gsm/auth_milenage.c
+++ b/src/gsm/auth_milenage.c
@@ -17,12 +17,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
+#include <errno.h>
#include <osmocom/crypt/auth.h>
#include <osmocom/core/bits.h>
#include "milenage/common.h"
@@ -32,7 +29,7 @@
* @{
*/
-static const uint8_t *gen_opc_if_needed(const struct osmo_sub_auth_data *aud, uint8_t *gen_opc)
+static const uint8_t *gen_opc_if_needed(const struct osmo_sub_auth_data2 *aud, uint8_t *gen_opc)
{
int rc;
@@ -47,7 +44,7 @@ static const uint8_t *gen_opc_if_needed(const struct osmo_sub_auth_data *aud, ui
}
static int milenage_gen_vec(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *_rand)
{
size_t res_len = sizeof(vec->res);
@@ -57,7 +54,15 @@ static int milenage_gen_vec(struct osmo_auth_vector *vec,
uint8_t sqn[6];
uint64_t ind_mask;
uint64_t seq_1;
- int rc;
+
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_MILENAGE);
+
+ if (aud->u.umts.k_len != 16)
+ return -EINVAL;
+ if (aud->u.umts.opc_len != 16)
+ return -EINVAL;
+ if (vec->res_len != 4 && vec->res_len != 8)
+ return -EINVAL;
opc = gen_opc_if_needed(aud, gen_opc);
if (!opc)
@@ -131,10 +136,9 @@ static int milenage_gen_vec(struct osmo_auth_vector *vec,
milenage_generate(opc, aud->u.umts.amf, aud->u.umts.k,
sqn, _rand,
vec->autn, vec->ik, vec->ck, vec->res, &res_len);
- vec->res_len = res_len;
- rc = gsm_milenage(opc, aud->u.umts.k, _rand, vec->sres, vec->kc);
- if (rc < 0)
- return rc;
+
+ osmo_auth_c3(vec->kc, vec->ck, vec->ik);
+ osmo_auth_c2(vec->sres, vec->res, vec->res_len, 1);
vec->auth_types = OSMO_AUTH_TYPE_UMTS | OSMO_AUTH_TYPE_GSM;
@@ -145,7 +149,7 @@ static int milenage_gen_vec(struct osmo_auth_vector *vec,
}
static int milenage_gen_vec_auts(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *auts, const uint8_t *rand_auts,
const uint8_t *_rand)
{
@@ -154,6 +158,13 @@ static int milenage_gen_vec_auts(struct osmo_auth_vector *vec,
const uint8_t *opc;
int rc;
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_MILENAGE);
+
+ if (aud->u.umts.k_len != 16)
+ return -EINVAL;
+ if (aud->u.umts.opc_len != 16)
+ return -EINVAL;
+
opc = gen_opc_if_needed(aud, gen_opc);
rc = milenage_auts(opc, aud->u.umts.k, rand_auts, auts, sqn_out);
diff --git a/src/gsm/auth_tuak.c b/src/gsm/auth_tuak.c
new file mode 100644
index 00000000..05fbf691
--- /dev/null
+++ b/src/gsm/auth_tuak.c
@@ -0,0 +1,207 @@
+/*! \file auth_tuak.c
+ * GSM/GPRS/3G authentication core infrastructure */
+/*
+ * (C) 2023 by Harald Welte <laforge@osmocom.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* NOTE: TUAK offers a lot of size variability in terms of size of length of MAC_A, MAC_S,
+ * but this is not used within 3GPP. The different sizes of Kc and RES are handled via
+ * osmo_sub_auth_data2. */
+
+#include <errno.h>
+#include <osmocom/crypt/auth.h>
+#include <osmocom/core/bits.h>
+#include "tuak/tuak.h"
+
+/*! \addtogroup auth
+ * @{
+ */
+
+static const uint8_t *gen_opc_if_needed(const struct osmo_sub_auth_data2 *aud, uint8_t *gen_opc)
+{
+ int rc;
+
+ /* Check if we only know OP and compute OPC if required */
+ if (aud->type == OSMO_AUTH_TYPE_UMTS && aud->u.umts.opc_is_op) {
+ rc = tuak_opc_gen(gen_opc, aud->u.umts.k, aud->u.umts.k_len, aud->u.umts.opc);
+ if (rc < 0)
+ return NULL;
+ return gen_opc;
+ }
+
+ return aud->u.umts.opc;
+}
+
+static int tuak_gen_vec(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *_rand)
+{
+ size_t res_len = vec->res_len;
+ uint64_t next_sqn;
+ uint8_t gen_opc[32];
+ const uint8_t *opc;
+ uint8_t sqn[6];
+ uint64_t ind_mask;
+ uint64_t seq_1;
+
+ switch (vec->res_len) {
+ case 4:
+ case 8:
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_TUAK);
+
+ if (aud->u.umts.k_len != 16 && aud->u.umts.k_len != 32)
+ return -EINVAL;
+ if (aud->u.umts.opc_len != 32)
+ return -EINVAL;
+
+ opc = gen_opc_if_needed(aud, gen_opc);
+ if (!opc)
+ return -1;
+
+ /* Determine next SQN, according to 3GPP TS 33.102:
+ * SQN consists of SEQ and a lower significant part of IND bits:
+ *
+ * |----------SEQ------------|
+ * |------------------------SQN-----------|
+ * |-----IND----|
+ *
+ * The IND part is used as "slots": e.g. a given HLR client will always
+ * get the same IND part, called ind here, with incrementing SEQ. In
+ * the USIM, each IND slot enforces that its SEQ are used in ascending
+ * order -- as long as that constraint is satisfied, the SQN may jump
+ * forwards and backwards. For example, for ind_bitlen == 5, asking the
+ * USIM for SQN = 32, 64, 33 is allowed, because 32 and 64 are
+ * SEQ || (ind == 0), and though 33 is below 64, it is ind == 1 and
+ * allowed. Not allowed would be 32, 96, 64, because 64 would go
+ * backwards after 96, both being ind == 0.
+ *
+ * From the last used SQN, we want to increment SEQ + 1, and then pick
+ * the matching IND part.
+ *
+ * IND size is suggested in TS 33.102 as 5 bits. SQN is 48 bits long.
+ * If ind_bitlen is passed too large here, the algorithms will break
+ * down. But at which point should we return an error? A sane limit
+ * seems to be ind_bitlen == 10, but to protect against failure,
+ * limiting ind_bitlen to 28 is enough, 28 being the number of bits
+ * suggested for the delta in 33.102, which is discussed to still
+ * require 2^15 > 32000 authentications to wrap the SQN back to the
+ * start.
+ *
+ * Note that if a caller with ind == 1 generates N vectors, the SQN
+ * stored after this will reflect SEQ + N. If then another caller with
+ * ind == 2 generates another N vectors, this will then use SEQ + N
+ * onwards and end up with SEQ + N + N. In other words, most of each
+ * SEQ's IND slots will remain unused. When looking at SQN being 48
+ * bits wide, after dropping ind_bitlen (say 5) from it, we will still
+ * have a sequence range of 2^43 = 8.8e12, eight trillion sequences,
+ * which is large enough to not bother further. With the maximum
+ * ind_bitlen of 28 enforced below, we still get more than 1 million
+ * sequences, which is also sufficiently large.
+ *
+ * An ind_bitlen of zero may be passed from legacy callers that are not
+ * aware of the IND extension. For these, below algorithm works out as
+ * before, simply incrementing SQN by 1.
+ *
+ * This is also a mechanism for tools like the osmo-auc-gen to directly
+ * request a given SQN to be used. With ind_bitlen == 0 the caller can
+ * be sure that this code will increment SQN by exactly one before
+ * generating a tuple, thus a caller would simply pass
+ * { .ind_bitlen = 0, .ind = 0, .sqn = (desired_sqn - 1) }
+ */
+
+ if (aud->u.umts.ind_bitlen > OSMO_MILENAGE_IND_BITLEN_MAX)
+ return -2;
+
+ seq_1 = 1LL << aud->u.umts.ind_bitlen;
+ ind_mask = ~(seq_1 - 1);
+
+ /* the ind index must not affect the SEQ part */
+ if (aud->u.umts.ind >= seq_1)
+ return -3;
+
+ /* keep the incremented SQN local until gsm_milenage() succeeded. */
+ next_sqn = ((aud->u.umts.sqn + seq_1) & ind_mask) + aud->u.umts.ind;
+
+ osmo_store64be_ext(next_sqn, sqn, 6);
+
+ tuak_generate(opc, aud->u.umts.amf, aud->u.umts.k, aud->u.umts.k_len,
+ sqn, _rand, vec->autn, vec->ik, vec->ck, vec->res, &res_len);
+
+ /* generate the GSM Kc + SRES values using C2 + C3 functions */
+ osmo_auth_c3(vec->kc, vec->ck, vec->ik);
+ osmo_auth_c2(vec->sres, vec->res, vec->res_len, 1);
+
+ vec->auth_types = OSMO_AUTH_TYPE_UMTS | OSMO_AUTH_TYPE_GSM;
+
+ /* for storage in the caller's AUC database */
+ aud->u.umts.sqn = next_sqn;
+
+ return 0;
+}
+
+static int tuak_gen_vec_auts(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *auts, const uint8_t *rand_auts,
+ const uint8_t *_rand)
+{
+ uint8_t sqn_out[6];
+ uint8_t gen_opc[32];
+ const uint8_t *opc;
+ int rc;
+
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_TUAK);
+
+ if (aud->u.umts.k_len != 16 && aud->u.umts.k_len != 32)
+ return -EINVAL;
+ if (aud->u.umts.opc_len != 32)
+ return -EINVAL;
+
+ opc = gen_opc_if_needed(aud, gen_opc);
+
+ rc = tuak_auts(opc, aud->u.umts.k, sizeof(aud->u.umts.k), rand_auts, auts, sqn_out);
+ if (rc < 0)
+ return rc;
+
+ aud->u.umts.sqn_ms = osmo_load64be_ext(sqn_out, 6) >> 16;
+ /* Update our "largest used SQN" from the USIM -- milenage_gen_vec()
+ * below will increment SQN. */
+ aud->u.umts.sqn = aud->u.umts.sqn_ms;
+
+ return tuak_gen_vec(vec, aud, _rand);
+}
+
+static struct osmo_auth_impl tuak_alg = {
+ .algo = OSMO_AUTH_ALG_TUAK,
+ .name = "TUAK (libosmogsm built-in)",
+ .priority = 1000,
+ .gen_vec = &tuak_gen_vec,
+ .gen_vec_auts = &tuak_gen_vec_auts,
+};
+
+static __attribute__((constructor)) void on_dso_load_tuak(void)
+{
+ osmo_auth_register(&tuak_alg);
+}
+
+/*! @} */
diff --git a/src/gsm/auth_xor.c b/src/gsm/auth_xor.c
index 36e006e6..a506a03d 100644
--- a/src/gsm/auth_xor.c
+++ b/src/gsm/auth_xor.c
@@ -20,10 +20,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <string.h>
@@ -47,19 +43,23 @@ static void xor(uint8_t *out, const uint8_t *a, const uint8_t *b, size_t len)
/* 3GPP TS 34.108, section 8.1.2.1 */
static int xor_gen_vec(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *_rand)
{
uint8_t xdout[16], cdout[8];
uint8_t ak[6], xmac[8];
int i;
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_XOR_3G);
+
/* Step 1: xdout = (ki or k) ^ rand */
if (aud->type == OSMO_AUTH_TYPE_GSM)
xor(xdout, aud->u.gsm.ki, _rand, sizeof(xdout));
- else if (aud->type == OSMO_AUTH_TYPE_UMTS)
+ else if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+ if (aud->u.umts.k_len != 16)
+ return -EINVAL;
xor(xdout, aud->u.umts.k, _rand, sizeof(xdout));
- else
+ } else
return -ENOTSUP;
/**
@@ -129,7 +129,7 @@ static int xor_gen_vec(struct osmo_auth_vector *vec,
/* 3GPP TS 34.108, section 8.1.2.2 */
static int xor_gen_vec_auts(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *auts,
const uint8_t *rand_auts,
const uint8_t *_rand)
@@ -138,12 +138,16 @@ static int xor_gen_vec_auts(struct osmo_auth_vector *vec,
uint8_t ak[6], xmac[8];
uint8_t sqnms[6];
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_XOR_3G);
+
/* Step 1: xdout = (ki or k) ^ rand */
if (aud->type == OSMO_AUTH_TYPE_GSM)
xor(xdout, aud->u.gsm.ki, _rand, sizeof(xdout));
- else if (aud->type == OSMO_AUTH_TYPE_UMTS)
+ else if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+ if (aud->u.umts.k_len != 16)
+ return -EINVAL;
xor(xdout, aud->u.umts.k, _rand, sizeof(xdout));
- else
+ } else
return -ENOTSUP;
/* Step 2: ak = xdout[2-8] */
@@ -172,8 +176,8 @@ static int xor_gen_vec_auts(struct osmo_auth_vector *vec,
}
static struct osmo_auth_impl xor_alg = {
- .algo = OSMO_AUTH_ALG_XOR,
- .name = "XOR (libosmogsm built-in)",
+ .algo = OSMO_AUTH_ALG_XOR_3G,
+ .name = "XOR-3G (libosmogsm built-in)",
.priority = 1000,
.gen_vec = &xor_gen_vec,
.gen_vec_auts = &xor_gen_vec_auts,
diff --git a/src/gsm/auth_xor_2g.c b/src/gsm/auth_xor_2g.c
new file mode 100644
index 00000000..367c79d4
--- /dev/null
+++ b/src/gsm/auth_xor_2g.c
@@ -0,0 +1,81 @@
+/*! \file auth_xor.c
+ * GSM XOR-2G algorithm as specified in Annex 4 (A.4.1.2) of 3GPP TS 51.010-1.
+ * This is implemented by typical GSM MS tester */
+
+/*
+ * (C) 2023 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * Author: Daniel Willmann <dwillmann@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/crypt/auth.h>
+
+/*! \addtogroup auth
+ * @{
+ */
+
+static void xor(uint8_t *out, const uint8_t *a, const uint8_t *b, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ out[i] = a[i] ^ b[i];
+}
+
+/* GSM XOR-2G algorithm as specified in Annex 4 (A.4.1.2) of 3GPP TS 51.010-1. */
+static int xor2g_gen_vec(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *_rand)
+{
+ uint8_t res1[16];
+
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_XOR_2G);
+
+ if (aud->type != OSMO_AUTH_TYPE_GSM)
+ return -ENOTSUP;
+
+ /* Step 1: XOR to the challenge RAND, a predefined number Ki, having the same bit length (128 bits) as
+ * RAND. */
+ xor(res1, aud->u.gsm.ki, _rand, sizeof(res1));
+
+ /* Step 2: The most significant 32 bits of RES1 form SRES. */
+ memcpy(vec->sres, res1, 4);
+ /* The next 64 bits of RES1 form Kc */
+ memcpy(vec->kc, res1+4, 8);
+
+ vec->auth_types = OSMO_AUTH_TYPE_GSM;
+ return 0;
+}
+
+static struct osmo_auth_impl xor2g_alg = {
+ .algo = OSMO_AUTH_ALG_XOR_2G,
+ .name = "XOR-2G (libosmogsm built-in)",
+ .priority = 1000,
+ .gen_vec = &xor2g_gen_vec,
+};
+
+static __attribute__((constructor)) void on_dso_load_xor(void)
+{
+ osmo_auth_register(&xor2g_alg);
+}
+
+/*! @} */
diff --git a/src/gsm/bsslap.c b/src/gsm/bsslap.c
index 7886da68..70ded13f 100644
--- a/src/gsm/bsslap.c
+++ b/src/gsm/bsslap.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
@@ -221,7 +217,7 @@ int osmo_bsslap_dec(struct bsslap_pdu *pdu,
int ies_len;
struct tlv_parsed tp;
- *pdu = (struct bsslap_pdu){};
+ memset(pdu, 0x00, sizeof(*pdu));
if (err)
*err = NULL;
diff --git a/src/gsm/bssmap_le.c b/src/gsm/bssmap_le.c
index 79546532..1ee45517 100644
--- a/src/gsm/bssmap_le.c
+++ b/src/gsm/bssmap_le.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <string.h>
@@ -184,7 +180,7 @@ int osmo_bssmap_le_ie_dec_location_type(struct bssmap_le_location_type *lt,
struct osmo_bssmap_le_err **err, void *err_ctx,
const uint8_t *elem, uint8_t len)
{
- *lt = (struct bssmap_le_location_type){};
+ memset(lt, 0x00, sizeof(*lt));
if (!elem || len < 1)
DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length");
@@ -265,6 +261,57 @@ static int osmo_bssmap_le_ie_dec_lcs_client_type(enum bssmap_le_lcs_client_type
}
}
+/*! Encode full BSSMAP-LE LCS Priority IE, including IEI tag and length.
+ * \param[inout] msg Message buffer to append to.
+ * \param[in] priority Value to enconde.
+ * \returns length of bytes written to the msgb.
+ */
+static uint8_t osmo_bssmap_le_ie_enc_lcs_priority(struct msgb *msg, uint8_t priority)
+{
+ OSMO_ASSERT(msg);
+ msgb_put_u8(msg, BSSMAP_LE_IEI_LCS_PRIORITY);
+ /* length */
+ msgb_put_u8(msg, 1);
+ msgb_put_u8(msg, priority);
+ return 3;
+}
+
+static int osmo_bssmap_le_ie_dec_lcs_priority(uint8_t *priority,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *elem, uint8_t len)
+{
+ if (!elem || len != 1)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "unexpected length");
+
+ *priority = elem[0];
+ return 0;
+}
+
+/*! Encode full BSSMAP-LE LCS QoS IE, including IEI tag and length.
+ * \param[inout] msg Message buffer to append to.
+ * \param[in] priority Value to enconde.
+ * \returns length of bytes written to the msgb.
+ */
+static uint8_t osmo_bssmap_le_ie_enc_lcs_qos(struct msgb *msg, const struct osmo_bssmap_le_lcs_qos *qos)
+{
+ OSMO_ASSERT(msg);
+ msgb_tlv_put(msg, BSSMAP_LE_IEI_LCS_QoS, sizeof(*qos), (const uint8_t *)qos);
+ return 2 + sizeof(*qos);
+}
+
+static int osmo_bssmap_le_ie_dec_lcs_qos(struct osmo_bssmap_le_lcs_qos *qos,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *elem, uint8_t len)
+{
+ if (!elem || len != sizeof(*qos))
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "unexpected length");
+
+ memcpy(qos, elem, len);
+ return 0;
+}
+
/*! Encode the value part of 3GPP TS 49.031 10.13 LCS Cause, without IEI and len.
* Identically used in 3GPP TS 48.008 3.2.2.66. Usage example:
*
@@ -301,7 +348,7 @@ int osmo_lcs_cause_dec(struct lcs_cause_ie *lcs_cause,
struct osmo_bssmap_le_err **err, void *err_ctx,
const uint8_t *data, uint8_t len)
{
- *lcs_cause = (struct lcs_cause_ie){};
+ memset(lcs_cause, 0x00, sizeof(*lcs_cause));
if (!data || len < 1)
DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length");
@@ -476,6 +523,12 @@ static int osmo_bssmap_le_enc_perform_loc_req(struct msgb *msg, const struct bss
if (params->lcs_client_type_present)
osmo_bssmap_le_ie_enc_lcs_client_type(msg, params->lcs_client_type);
+ if (params->more_items && params->lcs_priority_present)
+ osmo_bssmap_le_ie_enc_lcs_priority(msg, params->lcs_priority);
+
+ if (params->more_items && params->lcs_qos_present)
+ osmo_bssmap_le_ie_enc_lcs_qos(msg, &params->lcs_qos);
+
if (params->apdu_present) {
int rc = osmo_bssmap_le_ie_enc_apdu(msg, &params->apdu);
if (rc < 0)
@@ -505,7 +558,7 @@ static int osmo_bssmap_le_dec_perform_loc_req(struct bssmap_le_perform_loc_req *
struct osmo_bssmap_le_err **err, void *err_ctx,
const struct tlv_parsed *tp)
{
- *params = (struct bssmap_le_perform_loc_req){};
+ memset(params, 0x00, sizeof(*params));
DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_LOCATION_TYPE, osmo_bssmap_le_ie_dec_location_type,
&params->location_type);
@@ -513,11 +566,18 @@ static int osmo_bssmap_le_dec_perform_loc_req(struct bssmap_le_perform_loc_req *
&params->cell_id);
DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_LCS_CLIENT_TYPE, osmo_bssmap_le_ie_dec_lcs_client_type,
&params->lcs_client_type, params->lcs_client_type_present);
+ DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_LCS_PRIORITY, osmo_bssmap_le_ie_dec_lcs_priority,
+ &params->lcs_priority, params->lcs_priority_present);
+ DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_LCS_QoS, osmo_bssmap_le_ie_dec_lcs_qos,
+ &params->lcs_qos, params->lcs_qos_present);
DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_APDU, osmo_bssmap_le_ie_dec_apdu, &params->apdu,
params->apdu_present);
DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_IMSI, osmo_bssmap_le_ie_dec_imsi, &params->imsi);
DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_IMEI, osmo_bssmap_le_ie_dec_imei, &params->imei);
+ if (params->lcs_priority_present || params->lcs_qos_present)
+ params->more_items = true;
+
return 0;
}
@@ -546,7 +606,7 @@ static int osmo_bssmap_le_dec_perform_loc_resp(struct bssmap_le_perform_loc_resp
struct osmo_bssmap_le_err **err, void *err_ctx,
const struct tlv_parsed *tp)
{
- *params = (struct bssmap_le_perform_loc_resp){};
+ memset(params, 0x00, sizeof(*params));
DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_GEO_LOCATION, osmo_bssmap_le_ie_dec_gad, &params->location_estimate,
params->location_estimate_present);
@@ -570,7 +630,7 @@ static int osmo_bssmap_le_dec_perform_loc_abort(struct lcs_cause_ie *params,
struct osmo_bssmap_le_err **err, void *err_ctx,
const struct tlv_parsed *tp)
{
- *params = (struct lcs_cause_ie){};
+ memset(params, 0x00, sizeof(*params));
DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_LCS_CAUSE, osmo_lcs_cause_dec, params);
return 0;
@@ -587,7 +647,8 @@ static int osmo_bssmap_le_dec_conn_oriented_info(struct bssmap_le_conn_oriented_
struct osmo_bssmap_le_err **err, void *err_ctx,
const struct tlv_parsed *tp)
{
- *params = (struct bssmap_le_conn_oriented_info){};
+ memset(params, 0x00, sizeof(*params));
+
DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_APDU, osmo_bssmap_le_ie_dec_apdu, &params->apdu);
return 0;
}
@@ -651,7 +712,7 @@ static int osmo_bssmap_le_dec(struct bssmap_le_pdu *pdu,
int ies_len;
struct tlv_parsed tp;
- *pdu = (struct bssmap_le_pdu){};
+ memset(pdu, 0x00, sizeof(*pdu));
if (len < 1)
DEC_ERR(-EINVAL, -1, -1, LCS_CAUSE_UNSPECIFIED, "zero length");
@@ -738,7 +799,7 @@ int osmo_bssap_le_dec(struct bssap_le_pdu *pdu, struct osmo_bssap_le_err **err,
return RC; \
} while(0)
- *pdu = (struct bssap_le_pdu){};
+ memset(pdu, 0x00, sizeof(*pdu));
h = msgb_l2(msg);
if (!h)
diff --git a/src/gsm/bts_features.c b/src/gsm/bts_features.c
index fb3193f2..b6cd82ec 100644
--- a/src/gsm/bts_features.c
+++ b/src/gsm/bts_features.c
@@ -14,13 +14,9 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
*/
+#include <osmocom/core/utils.h>
#include <osmocom/gsm/bts_features.h>
const struct value_string osmo_bts_features_descs[] = {
@@ -44,9 +40,19 @@ const struct value_string osmo_bts_features_descs[] = {
{ BTS_FEAT_ACCH_REP, "FACCH/SACCH Repetition" },
{ BTS_FEAT_CCN, "Cell Change Notification (CCN)" },
{ BTS_FEAT_VAMOS, "VAMOS (Voice services over Adaptive Multi-user channels on One Slot)" },
+ { BTS_FEAT_ABIS_OSMO_PCU, "OsmoPCU over OML link IPA multiplex" },
+ { BTS_FEAT_BCCH_POWER_RED, "BCCH carrier power reduction mode" },
+ { BTS_FEAT_DYN_TS_SDCCH8, "Dynamic Timeslot configuration as SDCCH8" },
+ { BTS_FEAT_ACCH_TEMP_OVP, "FACCH/SACCH Temporary overpower" },
+ { BTS_FEAT_OSMUX, "Osmux (Osmocom RTP multiplexing)" },
+ { BTS_FEAT_VBS, "Voice Broadcast Service" },
+ { BTS_FEAT_VGCS, "Voice Group Call Service" },
{ 0, NULL }
};
+/* Ensure that all BTS_FEAT_* entries are present in osmo_bts_features_descs[] */
+osmo_static_assert(ARRAY_SIZE(osmo_bts_features_descs) == _NUM_BTS_FEAT + 1, _bts_features_descs);
+
/*! return description string of a BTS feature (osmo_bts_features_descs).
* To get the plain feature name, use osmo_bts_features_name() instead. */
const char *osmo_bts_feature_name(enum osmo_bts_features feature)
@@ -75,5 +81,15 @@ const struct value_string osmo_bts_features_names[] = {
{ BTS_FEAT_ACCH_REP, "ACCH_REP" },
{ BTS_FEAT_CCN, "CCN" },
{ BTS_FEAT_VAMOS, "VAMOS" },
+ { BTS_FEAT_ABIS_OSMO_PCU, "ABIS_OSMO_PCU" },
+ { BTS_FEAT_BCCH_POWER_RED, "BCCH_PWR_RED" },
+ { BTS_FEAT_DYN_TS_SDCCH8, "DYN_TS_SDCCH8" },
+ { BTS_FEAT_ACCH_TEMP_OVP, "ACCH_TEMP_OVP" },
+ { BTS_FEAT_OSMUX, "OSMUX" },
+ { BTS_FEAT_VBS, "VBS" },
+ { BTS_FEAT_VGCS, "VGCS" },
{}
};
+
+/* Ensure that all BTS_FEAT_* entries are present in osmo_bts_features_names[] */
+osmo_static_assert(ARRAY_SIZE(osmo_bts_features_names) == _NUM_BTS_FEAT + 1, _bts_features_names);
diff --git a/src/gsm/cbsp.c b/src/gsm/cbsp.c
index 353f53f4..a5e58f4c 100644
--- a/src/gsm/cbsp.c
+++ b/src/gsm/cbsp.c
@@ -15,10 +15,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
@@ -34,7 +30,7 @@
#include <osmocom/gsm/cbsp.h>
#include <osmocom/gsm/gsm0808_utils.h>
-const __thread char *osmo_cbsp_errstr;
+__thread const char *osmo_cbsp_errstr;
struct msgb *osmo_cbsp_msgb_alloc(void *ctx, const char *name)
{
@@ -541,8 +537,8 @@ static int cbsp_decode_fail_list(struct llist_head *fl, void *ctx,
struct osmo_cbsp_fail_ent *ent = talloc_zero(ctx, struct osmo_cbsp_fail_ent);
unsigned int len_remain = len - (cur - buf);
OSMO_ASSERT(ent);
- ent->id_discr = cur[0];
- rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur+1, len_remain-1);
+ ent->id_discr = *cur++;
+ rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur, len_remain-1);
if (rc < 0) {
osmo_cbsp_errstr = "fail list: error decoding cell_id_union";
return rc;
@@ -643,6 +639,7 @@ static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct
struct msgb *in, void *ctx)
{
unsigned int i;
+ int rc;
/* check for mandatory IEs */
if (!TLVP_PRESENT(tp, CBSP_IEI_MSG_ID) ||
@@ -660,8 +657,10 @@ static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
if (TLVP_PRESENT(tp, CBSP_IEI_CHANNEL_IND)) {
uint8_t num_of_pages;
@@ -722,6 +721,8 @@ static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct
static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
@@ -737,14 +738,18 @@ static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *ou
INIT_LLIST_HEAD(&out->num_compl_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
- cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
- TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ if (rc < 0)
+ return rc;
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
@@ -757,6 +762,8 @@ static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *ou
static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
@@ -772,21 +779,27 @@ static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out,
}
INIT_LLIST_HEAD(&out->fail_list);
- cbsp_decode_fail_list(&out->fail_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
- TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ rc = cbsp_decode_fail_list(&out->fail_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
+ TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ if (rc < 0)
+ return rc;
INIT_LLIST_HEAD(&out->num_compl_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
- cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
- TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ if (rc < 0)
+ return rc;
}
INIT_LLIST_HEAD(&out->cell_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
}
if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
@@ -800,6 +813,8 @@ static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out,
static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
@@ -811,8 +826,10 @@ static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp
out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
@@ -825,6 +842,8 @@ static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp
static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
@@ -837,14 +856,18 @@ static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct
INIT_LLIST_HEAD(&out->num_compl_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
- cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
- TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ if (rc < 0)
+ return rc;
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
@@ -857,6 +880,8 @@ static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct
static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
@@ -868,21 +893,27 @@ static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct t
out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
INIT_LLIST_HEAD(&out->fail_list);
- cbsp_decode_fail_list(&out->fail_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
- TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ rc = cbsp_decode_fail_list(&out->fail_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
+ TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ if (rc < 0)
+ return rc;
INIT_LLIST_HEAD(&out->num_compl_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
- cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
- TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ if (rc < 0)
+ return rc;
}
INIT_LLIST_HEAD(&out->cell_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
}
if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
@@ -896,6 +927,8 @@ static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct t
static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
@@ -903,8 +936,10 @@ static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tl
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
return 0;
@@ -914,6 +949,8 @@ static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tl
static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
@@ -921,9 +958,11 @@ static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out,
}
INIT_LLIST_HEAD(&out->loading_list.list);
- cbsp_decode_loading_list(&out->loading_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
- TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
+ rc = cbsp_decode_loading_list(&out->loading_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
+ TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
+ if (rc < 0)
+ return rc;
out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
return 0;
@@ -933,6 +972,8 @@ static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out,
static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
@@ -940,25 +981,29 @@ static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out,
}
INIT_LLIST_HEAD(&out->fail_list);
- cbsp_decode_fail_list(&out->fail_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
- TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ rc = cbsp_decode_fail_list(&out->fail_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
+ TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ if (rc < 0)
+ return rc;
out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
INIT_LLIST_HEAD(&out->loading_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6)) {
- cbsp_decode_loading_list(&out->loading_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
- TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
+ rc = cbsp_decode_loading_list(&out->loading_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
+ TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
}
- return 0;
+ return rc;
}
/* 8.1.3.10 STATUS QUERY */
static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
@@ -971,8 +1016,10 @@ static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out,
out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
return 0;
@@ -982,6 +1029,8 @@ static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out,
static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_complete *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7) ||
@@ -994,9 +1043,11 @@ static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_com
out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
INIT_LLIST_HEAD(&out->num_compl_list.list);
- cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
- TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ if (rc < 0)
+ return rc;
out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
return 0;
}
@@ -1005,6 +1056,8 @@ static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_com
static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_failure *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
@@ -1017,17 +1070,21 @@ static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_fail
out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
INIT_LLIST_HEAD(&out->fail_list);
- cbsp_decode_fail_list(&out->fail_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
- TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ rc = cbsp_decode_fail_list(&out->fail_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
+ TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ if (rc < 0)
+ return rc;
out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
INIT_LLIST_HEAD(&out->num_compl_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
- cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
- TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ if (rc < 0)
+ return rc;
}
return 0;
}
@@ -1036,14 +1093,19 @@ static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_fail
static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
return -EINVAL;
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
+
return 0;
}
@@ -1051,14 +1113,19 @@ static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed *
static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
return -EINVAL;
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
+
return 0;
}
@@ -1066,20 +1133,26 @@ static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const stru
static int cbsp_dec_reset_fail(struct osmo_cbsp_reset_failure *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
return -EINVAL;
}
INIT_LLIST_HEAD(&out->fail_list);
- cbsp_decode_fail_list(&out->fail_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
- TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ rc = cbsp_decode_fail_list(&out->fail_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
+ TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ if (rc < 0)
+ return rc;
INIT_LLIST_HEAD(&out->cell_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
}
return 0;
}
@@ -1110,6 +1183,8 @@ static int cbsp_dec_keep_alive_compl(struct osmo_cbsp_keep_alive_complete *out,
static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_RECOVERY_IND, 1)) {
@@ -1118,8 +1193,10 @@ static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_pars
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
out->recovery_ind = *TLVP_VAL(tp, CBSP_IEI_RECOVERY_IND);
@@ -1130,6 +1207,8 @@ static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_pars
static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
@@ -1137,9 +1216,11 @@ static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_pars
}
INIT_LLIST_HEAD(&out->fail_list);
- cbsp_decode_fail_list(&out->fail_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
- TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ rc = cbsp_decode_fail_list(&out->fail_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
+ TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ if (rc < 0)
+ return rc;
out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
return 0;
@@ -1180,6 +1261,7 @@ static int cbsp_dec_error_ind(struct osmo_cbsp_error_ind *out, const struct tlv_
* \returns callee-allocated decoded representation of CBSP message; NULL on error */
struct osmo_cbsp_decoded *osmo_cbsp_decode(void *ctx, struct msgb *in)
{
+ OSMO_ASSERT(in->l1h != NULL && in->l2h != NULL);
struct osmo_cbsp_decoded *out = talloc_zero(ctx, struct osmo_cbsp_decoded);
const struct cbsp_header *h = msgb_l1(in);
struct tlv_parsed tp[16]; /* max. number of pages in a given CBS message */
@@ -1445,6 +1527,10 @@ int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb *
needed = len - msgb_l2len(msg);
if (needed > 0) {
+ if (needed > msgb_tailroom(msg)) {
+ rc = -ENOMEM;
+ goto discard_msg;
+ }
rc = recv(fd, msg->tail, needed, 0);
if (rc == 0)
goto discard_msg;
@@ -1481,4 +1567,45 @@ discard_msg:
return rc;
}
+/*! call-back function to segment the data at message boundaries.
+ * Returns the size of the next message. If it returns -EAGAIN or a value larger than msgb_length() (message
+ * is incomplete), the caller (e.g. osmo_io) has to wait for more data to be read. */
+int osmo_cbsp_segmentation_cb(struct msgb *msg)
+{
+ const struct cbsp_header *h;
+ int len;
+
+ if (msgb_length(msg) < sizeof(*h))
+ return -EAGAIN;
+
+ h = (const struct cbsp_header *) msg->data;
+ msg->l1h = msg->data;
+ msg->l2h = msg->data + sizeof(*h);
+ /* then read the length as specified in the header */
+ len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
+
+ return sizeof(*h) + len;
+}
+
+/*! value_string[] for enum osmo_cbsp_cause. */
+const struct value_string osmo_cbsp_cause_names[] = {
+ { OSMO_CBSP_CAUSE_PARAM_NOT_RECOGNISED, "Parameter-not-recognised" },
+ { OSMO_CBSP_CAUSE_PARAM_VALUE_INVALID, "Parameter-value-invalid" },
+ { OSMO_CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED, "Message-reference-not-identified" },
+ { OSMO_CBSP_CAUSE_CELL_ID_NOT_VALID, "Cell-identity-not-valid" },
+ { OSMO_CBSP_CAUSE_UNRECOGNISED_MESSAGE, "Unrecognised-message" },
+ { OSMO_CBSP_CAUSE_MISSING_MANDATORY_ELEMENT, "Missing-mandatory-element" },
+ { OSMO_CBSP_CAUSE_BSC_CAPACITY_EXCEEDED, "BSC-capacity-exceeded" },
+ { OSMO_CBSP_CAUSE_CELL_MEMORY_EXCEEDED, "Cell-memory-exceeded" },
+ { OSMO_CBSP_CAUSE_BSC_MEMORY_EXCEEDED, "BSC-memory-exceeded" },
+ { OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_SUPPORTED, "Cell-broadcast-not-supported" },
+ { OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_OPERATIONAL, "Cell-broadcast-not-operational" },
+ { OSMO_CBSP_CAUSE_INCOMPATIBLE_DRX_PARAM, "Incompatible-DRX-parameter:"},
+ { OSMO_CBSP_CAUSE_EXT_CHAN_NOT_SUPPORTED, "Extended-channel-not-supported"},
+ { OSMO_CBSP_CAUSE_MSG_REF_ALREADY_USED, "Message-reference-already-used"},
+ { OSMO_CBSP_CAUSE_UNSPECIFIED_ERROR, "Unspecified-error"},
+ { OSMO_CBSP_CAUSE_LAI_OR_LAC_NOT_VALID, "LAI-or-LAC-not-valid"},
+ { 0, NULL }
+};
+
#endif /* HAVE_SYS_SOCKET_H */
diff --git a/src/gsm/comp128.c b/src/gsm/comp128.c
index b28a8437..e252b04c 100644
--- a/src/gsm/comp128.c
+++ b/src/gsm/comp128.c
@@ -58,10 +58,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <string.h>
diff --git a/src/gsm/comp128v23.c b/src/gsm/comp128v23.c
index 550f6a49..0947017c 100644
--- a/src/gsm/comp128v23.c
+++ b/src/gsm/comp128v23.c
@@ -23,10 +23,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
diff --git a/src/gsm/gea.c b/src/gsm/gea.c
index 5756bb08..aedc898e 100644
--- a/src/gsm/gea.c
+++ b/src/gsm/gea.c
@@ -12,10 +12,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <osmocom/core/bits.h>
diff --git a/src/gsm/gprs_cipher_core.c b/src/gsm/gprs_cipher_core.c
index 7f2b1a50..97e581db 100644
--- a/src/gsm/gprs_cipher_core.c
+++ b/src/gsm/gprs_cipher_core.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include "config.h"
diff --git a/src/gsm/gprs_gea.c b/src/gsm/gprs_gea.c
index 73147886..eae1cf12 100644
--- a/src/gsm/gprs_gea.c
+++ b/src/gsm/gprs_gea.c
@@ -16,10 +16,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <osmocom/crypt/gprs_cipher.h>
diff --git a/src/gsm/gprs_rlc.c b/src/gsm/gprs_rlc.c
index bdfc8eac..4f02a7a9 100644
--- a/src/gsm/gprs_rlc.c
+++ b/src/gsm/gprs_rlc.c
@@ -13,9 +13,8 @@
#include <string.h>
#include <osmocom/core/utils.h>
-#include <osmocom/gprs/gprs_rlc.h>
#include <osmocom/coding/gsm0503_coding.h>
-#include <osmocom/gprs/protocol/gsm_04_60.h>
+#include <osmocom/gsm/protocol/gsm_44_060.h>
#define EGPRS_CPS_TYPE1_TBL_SZ 29
#define EGPRS_CPS_TYPE2_TBL_SZ 8
diff --git a/src/gsm/gsm0341.c b/src/gsm/gsm0341.c
index 89f5de3f..a6238b36 100644
--- a/src/gsm/gsm0341.c
+++ b/src/gsm/gsm0341.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
diff --git a/src/gsm/gsm0411_utils.c b/src/gsm/gsm0411_utils.c
index ccefe546..470b017f 100644
--- a/src/gsm/gsm0411_utils.c
+++ b/src/gsm/gsm0411_utils.c
@@ -27,7 +27,7 @@
*
*/
-#include "../../config.h"
+#include "config.h"
#include <time.h>
#include <string.h>
@@ -293,7 +293,7 @@ enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs)
* \param[in] type GSM340_TYPE_*
* \param[in] plan Numbering Plan
* \param[in] number string containing number
- * \reurns number of bytes of \a oa that have been used */
+ * \returns number of bytes of \a oa that have been used */
int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type,
uint8_t plan, const char *number)
{
@@ -348,7 +348,7 @@ int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
* \param[in] proto Protocol
* \param[in] trans Transaction
* \param[in] msg_type Message Type
- * \retrns 0 */
+ * \returns 0 */
int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
uint8_t msg_type)
{
diff --git a/src/gsm/gsm0480.c b/src/gsm/gsm0480.c
index 1d5cde95..7a7f71f0 100644
--- a/src/gsm/gsm0480.c
+++ b/src/gsm/gsm0480.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/gsm/gsm48.h>
diff --git a/src/gsm/gsm0502.c b/src/gsm/gsm0502.c
index e34d3f57..e4a761da 100644
--- a/src/gsm/gsm0502.c
+++ b/src/gsm/gsm0502.c
@@ -34,7 +34,7 @@
#include <inttypes.h>
unsigned int
-gsm0502_calc_paging_group(struct gsm48_control_channel_descr *chan_desc, uint64_t imsi)
+gsm0502_calc_paging_group(const struct gsm48_control_channel_descr *chan_desc, uint64_t imsi)
{
int ccch_conf;
int bs_cc_chans;
@@ -179,7 +179,6 @@ uint32_t gsm0502_fn_remap(uint32_t fn, enum gsm0502_fn_remap_channel channel)
uint8_t fn_cycle;
uint8_t i;
int sub = -1;
- uint32_t fn_map;
struct fn_remap_table *table;
OSMO_ASSERT(channel < ARRAY_SIZE(fn_remap_table_ptr));
@@ -199,9 +198,7 @@ uint32_t gsm0502_fn_remap(uint32_t fn, enum gsm0502_fn_remap_channel channel)
return fn;
}
- fn_map = (fn + GSM_MAX_FN - sub) % GSM_MAX_FN;
-
- return fn_map;
+ return GSM_TDMA_FN_SUB(fn, sub);
}
/* Magic numbers (RNTABLE) for pseudo-random hopping sequence generation. */
@@ -263,3 +260,38 @@ uint16_t gsm0502_hop_seq_gen(const struct gsm_time *t,
return ma ? ma[mai] : mai;
}
+
+#define CB_FCCH -1
+#define CB_SCH -2
+#define CB_BCCH -3
+#define CB_IDLE -4
+
+/* Clause 7 Table 3 and Figure 8a */
+static const int ccch_block_table[51] = {
+ CB_FCCH, CB_SCH, /* 0..1 */
+ CB_BCCH, CB_BCCH, CB_BCCH, CB_BCCH, /* 2..5: BCCH */
+ 0, 0, 0, 0, /* 6..9: B0 */
+ CB_FCCH, CB_SCH, /* 10..11 */
+ 1, 1, 1, 1, /* 12..15: B1 */
+ 2, 2, 2, 2, /* 16..19: B2 */
+ CB_FCCH, CB_SCH, /* 20..21 */
+ 3, 3, 3, 3, /* 22..25: B3 */
+ 4, 4, 4, 4, /* 26..29: B4 */
+ CB_FCCH, CB_SCH, /* 30..31 */
+ 5, 5, 5, 5, /* 32..35: B5 */
+ 6, 6, 6, 6, /* 36..39: B6 */
+ CB_FCCH, CB_SCH, /* 40..41 */
+ 7, 7, 7, 7, /* 42..45: B7 */
+ 8, 8, 8, 8, /* 46..49: B8 */
+ CB_IDLE /* 50: Idle */
+};
+
+/*! Calculate CCCH block number from the given TDMA frame number.
+ * \param[in] fn TDMA frame number (of first or last burst).
+ * \returns CCCH block number 0..8 or a negative value,
+ * if the given frame number cannot carry CCCH.
+ */
+int gsm0502_fn2ccch_block(uint32_t fn)
+{
+ return ccch_block_table[fn % ARRAY_SIZE(ccch_block_table)];
+}
diff --git a/src/gsm/gsm0808.c b/src/gsm/gsm0808.c
index 3a39fd14..529dbdfe 100644
--- a/src/gsm/gsm0808.c
+++ b/src/gsm/gsm0808.c
@@ -15,12 +15,10 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
+#include "config.h"
+
#include <string.h>
#include <osmocom/core/byteswap.h>
@@ -104,13 +102,19 @@ struct msgb *gsm0808_create_layer3_2(const struct msgb *msg_l3, const struct osm
msgb_l3len(msg_l3), msg_l3->l3h);
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (scl)
- gsm0808_enc_speech_codec_list(msg, scl);
+ if (scl) {
+ if (gsm0808_enc_speech_codec_list2(msg, scl) < 0)
+ goto exit_free;
+ }
/* push the bssmap header */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create "Complete L3 Info" for A, legacy implementation.
@@ -228,22 +232,32 @@ struct msgb *gsm0808_create_clear_command2(uint8_t cause, bool csfb_ind)
return msg;
}
-/*! Create BSSMAP Cipher Mode Command message
+/*! Superseded by gsm0808_create_cipher2() to include Kc128.
+ * Create BSSMAP Cipher Mode Command message (without Kc128).
* \param[in] ei Mandatory Encryption Information
+ * \param[in] kc128 optional kc128 key for A5/4
* \param[in] cipher_response_mode optional 1-byte Cipher Response Mode
* \returns callee-allocated msgb with BSSMAP Cipher Mode Command message */
struct msgb *gsm0808_create_cipher(const struct gsm0808_encrypt_info *ei,
const uint8_t *cipher_response_mode)
{
+ struct gsm0808_cipher_mode_command cmc = {
+ .ei = *ei,
+ .cipher_response_mode_present = (cipher_response_mode != NULL),
+ .cipher_response_mode = (cipher_response_mode ? *cipher_response_mode : 0),
+ };
+ return gsm0808_create_cipher2(&cmc);
+}
+
+/*! Create BSSMAP Cipher Mode Command message.
+ * \param[in] cmc Information to encode.
+ * \returns callee-allocated msgb with BSSMAP Cipher Mode Command message */
+struct msgb *gsm0808_create_cipher2(const struct gsm0808_cipher_mode_command *cmc)
+{
/* See also: 3GPP TS 48.008 3.2.1.30 CIPHER MODE COMMAND */
struct msgb *msg;
- /* Mandatory emelent! */
- OSMO_ASSERT(ei);
-
- msg =
- msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
- "cipher-mode-command");
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "cipher-mode-command");
if (!msg)
return NULL;
@@ -251,12 +265,16 @@ struct msgb *gsm0808_create_cipher(const struct gsm0808_encrypt_info *ei,
msgb_v_put(msg, BSS_MAP_MSG_CIPHER_MODE_CMD);
/* Encryption Information 3.2.2.10 */
- gsm0808_enc_encrypt_info(msg, ei);
+ gsm0808_enc_encrypt_info(msg, &cmc->ei);
/* Cipher Response Mode 3.2.2.34 */
- if (cipher_response_mode)
+ if (cmc->cipher_response_mode_present)
msgb_tv_put(msg, GSM0808_IE_CIPHER_RESPONSE_MODE,
- *cipher_response_mode);
+ cmc->cipher_response_mode);
+
+ /* Kc128 3.2.2.109 */
+ if (cmc->kc128_present)
+ gsm0808_enc_kc128(msg, cmc->kc128);
/* pre-pend the header */
msg->l3h =
@@ -363,7 +381,7 @@ struct msgb *gsm0808_create_lcls_conn_ctrl(enum gsm0808_lcls_config config,
if (config != GSM0808_LCLS_CFG_NA)
msgb_tv_put(msg, GSM0808_IE_LCLS_CONFIG, config);
if (control != GSM0808_LCLS_CSC_NA)
- msgb_tv_put(msg, GSM0808_IE_LCLS_CONFIG, control);
+ msgb_tv_put(msg, GSM0808_IE_LCLS_CONN_STATUS_CTRL, control);
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
@@ -408,7 +426,7 @@ struct msgb *gsm0808_create_lcls_notification(enum gsm0808_lcls_status status, b
/*! Create BSSMAP Classmark Request message
* \returns callee-allocated msgb with BSSMAP Classmark Request message */
-struct msgb *gsm0808_create_classmark_request()
+struct msgb *gsm0808_create_classmark_request(void)
{
struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
"classmark-request");
@@ -520,8 +538,10 @@ struct msgb *gsm0808_create_ass2(const struct gsm0808_channel_type *ct,
}
/* AoIP: Codec List (MSC Preferred) 3.2.2.103 */
- if (scl)
- gsm0808_enc_speech_codec_list(msg, scl);
+ if (scl) {
+ if (gsm0808_enc_speech_codec_list2(msg, scl) < 0)
+ goto exit_free;
+ }
/* AoIP: Call Identifier 3.2.2.105 */
if (ci) {
@@ -542,6 +562,10 @@ struct msgb *gsm0808_create_ass2(const struct gsm0808_channel_type *ct,
msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP Assignment Request message, 3GPP TS 48.008 §3.2.1.1.
@@ -606,12 +630,16 @@ struct msgb *gsm0808_create_ass_compl2(uint8_t rr_cause, uint8_t chosen_channel,
gsm0808_enc_aoip_trasp_addr(msg, ss);
/* AoIP: Speech Codec (Chosen) 3.2.2.104 */
- if (sc)
- gsm0808_enc_speech_codec(msg, sc);
+ if (sc) {
+ if (gsm0808_enc_speech_codec2(msg, sc) < 0)
+ goto exit_free;
+ }
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (scl)
- gsm0808_enc_speech_codec_list(msg, scl);
+ if (scl) {
+ if (gsm0808_enc_speech_codec_list2(msg, scl) < 0)
+ goto exit_free;
+ }
/* FIXME: write LSA identifier 3.2.2.15 - see 3GPP TS 43.073 */
@@ -622,6 +650,10 @@ struct msgb *gsm0808_create_ass_compl2(uint8_t rr_cause, uint8_t chosen_channel,
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP Assignment Completed message
@@ -683,13 +715,19 @@ struct msgb *gsm0808_create_ass_fail(uint8_t cause, const uint8_t *rr_cause,
/* Circuit pool list 3.2.2.46 */
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (scl)
- gsm0808_enc_speech_codec_list(msg, scl);
+ if (scl) {
+ if (gsm0808_enc_speech_codec_list2(msg, scl) < 0)
+ goto exit_free;
+ }
/* update the size */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP Assignment Failure message
@@ -982,8 +1020,10 @@ struct msgb *gsm0808_create_handover_request(const struct gsm0808_handover_reque
if (params->aoip_transport_layer)
gsm0808_enc_aoip_trasp_addr(msg, params->aoip_transport_layer);
- if (params->codec_list_msc_preferred)
- gsm0808_enc_speech_codec_list(msg, params->codec_list_msc_preferred);
+ if (params->codec_list_msc_preferred) {
+ if (gsm0808_enc_speech_codec_list2(msg, params->codec_list_msc_preferred) < 0)
+ goto exit_free;
+ }
if (params->call_id_present) {
uint8_t val[4];
@@ -991,6 +1031,9 @@ struct msgb *gsm0808_create_handover_request(const struct gsm0808_handover_reque
msgb_tv_fixed_put(msg, GSM0808_IE_CALL_ID, 4, val);
}
+ if (params->more_items && params->kc128_present)
+ gsm0808_enc_kc128(msg, params->kc128);
+
if (params->global_call_reference && params->global_call_reference_len) {
msgb_tlv_put(msg, GSM0808_IE_GLOBAL_CALL_REF,
params->global_call_reference_len, params->global_call_reference);
@@ -1000,6 +1043,10 @@ struct msgb *gsm0808_create_handover_request(const struct gsm0808_handover_reque
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP HANDOVER REQUEST ACKNOWLEDGE message, 3GPP TS 48.008 3.2.1.10.
@@ -1032,14 +1079,28 @@ struct msgb *gsm0808_create_handover_request_ack2(const struct gsm0808_handover_
if (params->aoip_transport_layer)
gsm0808_enc_aoip_trasp_addr(msg, params->aoip_transport_layer);
+ /* AoIP: add Codec List (BSS Supported) 3.2.2.103.
+ * (codec_list_bss_supported was added to struct gsm0808_handover_request_ack later than speech_codec_chosen
+ * below, but it needs to come before it in the message coding). */
+ if (params->more_items && params->codec_list_bss_supported.len) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_bss_supported) < 0)
+ goto exit_free;
+ }
+
/* AoIP: Speech Codec (Chosen) 3.2.2.104 */
- if (params->speech_codec_chosen_present)
- gsm0808_enc_speech_codec(msg, &params->speech_codec_chosen);
+ if (params->speech_codec_chosen_present) {
+ if (gsm0808_enc_speech_codec2(msg, &params->speech_codec_chosen) < 0)
+ goto exit_free;
+ }
/* prepend header with final length */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Same as gsm0808_create_handover_request_ack2() but with less parameters.
@@ -1091,7 +1152,7 @@ struct msgb *gsm0808_create_handover_command(const struct gsm0808_handover_comma
/*! Create BSSMAP HANDOVER DETECT message, 3GPP TS 48.008 3.2.1.40.
* Sent from the MT BSC back to the MSC when the MS has sent a handover RACH request and the MT BSC has
* received the Handover Detect message. */
-struct msgb *gsm0808_create_handover_detect()
+struct msgb *gsm0808_create_handover_detect(void)
{
struct msgb *msg;
@@ -1110,7 +1171,7 @@ struct msgb *gsm0808_create_handover_detect()
/*! Create BSSMAP HANDOVER SUCCEEDED message, 3GPP TS 48.008 3.2.1.13.
* Sent from the MSC back to the old BSS to notify that the MS has successfully accessed the new BSS. */
-struct msgb *gsm0808_create_handover_succeeded()
+struct msgb *gsm0808_create_handover_succeeded(void)
{
struct msgb *msg;
@@ -1145,12 +1206,16 @@ struct msgb *gsm0808_create_handover_complete(const struct gsm0808_handover_comp
msgb_tlv_put(msg, GSM0808_IE_RR_CAUSE, 1, &params->rr_cause);
/* AoIP: Speech Codec (Chosen) 3.2.2.104 */
- if (params->speech_codec_chosen_present)
- gsm0808_enc_speech_codec(msg, &params->speech_codec_chosen);
+ if (params->speech_codec_chosen_present) {
+ if (gsm0808_enc_speech_codec2(msg, &params->speech_codec_chosen) < 0)
+ goto exit_free;
+ }
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (params->codec_list_bss_supported.len)
- gsm0808_enc_speech_codec_list(msg, &params->codec_list_bss_supported);
+ if (params->codec_list_bss_supported.len) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_bss_supported) < 0)
+ goto exit_free;
+ }
/* Chosen Encryption Algorithm 3.2.2.44 */
if (params->chosen_encr_alg_present && params->chosen_encr_alg > 0)
@@ -1164,6 +1229,10 @@ struct msgb *gsm0808_create_handover_complete(const struct gsm0808_handover_comp
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP HANDOVER FAILURE message, 3GPP TS 48.008 3.2.1.16.
@@ -1187,13 +1256,19 @@ struct msgb *gsm0808_create_handover_failure(const struct gsm0808_handover_failu
msgb_tlv_put(msg, GSM0808_IE_RR_CAUSE, 1, &params->rr_cause);
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (params->codec_list_bss_supported.len)
- gsm0808_enc_speech_codec_list(msg, &params->codec_list_bss_supported);
+ if (params->codec_list_bss_supported.len) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_bss_supported) < 0)
+ goto exit_free;
+ }
/* prepend header with final length */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP HANDOVER PERFORMED message, 3GPP TS 48.008 3.2.1.25.
@@ -1229,8 +1304,10 @@ struct msgb *gsm0808_create_handover_performed(const struct gsm0808_handover_per
msgb_tv_put(msg, GSM0808_IE_SPEECH_VERSION, params->speech_version_chosen);
/* AoIP: Speech Codec (chosen) 3.2.2.104 */
- if (params->speech_codec_chosen_present)
- gsm0808_enc_speech_codec(msg, &params->speech_codec_chosen);
+ if (params->speech_codec_chosen_present) {
+ if (gsm0808_enc_speech_codec2(msg, &params->speech_codec_chosen) < 0)
+ goto exit_free;
+ }
/* LCLS-BSS-Status 3.2.2.119 */
if (params->lcls_bss_status_present)
@@ -1240,6 +1317,10 @@ struct msgb *gsm0808_create_handover_performed(const struct gsm0808_handover_per
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP COMMON ID message, 3GPP TS 48.008 3.2.1.68.
@@ -1435,6 +1516,663 @@ struct msgb *gsm0808_create_perform_location_abort(const struct lcs_cause_ie *lc
return msg;
}
+/*! Create BSSMAP VGCS/VBS SETUP message, 3GPP TS 48.008 3.2.1.50.
+ * Sent from the MSC to the BSC to request VGCS/VBS call. */
+struct msgb *gsm0808_create_vgcs_vbs_setup(const struct gsm0808_vgcs_vbs_setup *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-SETUP");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_SETUP);
+
+ /* Group Call Reference, 3.2.2.55 */
+ gsm0808_enc_group_callref(msg, &params->callref);
+
+ /* Priority, 3.2.2.18 */
+ if (params->priority_present)
+ gsm0808_enc_priority(msg, &params->priority);
+
+ /* VGCS Feature Flags, 3.2.2.88 */
+ if (params->vgcs_feature_flags_present)
+ gsm0808_enc_vgcs_feature_flags(msg, &params->flags);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS SETUP ACK message, 3GPP TS 48.008 3.2.1.51.
+ * Sent from the BSC to the MSC to confirm VGCS/VBS call. */
+struct msgb *gsm0808_create_vgcs_vbs_setup_ack(const struct gsm0808_vgcs_vbs_setup_ack *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-SETUP-ACK");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_SETUP_ACK);
+
+ /* VGCS Feature Flags, 3.2.2.88 */
+ if (params->vgcs_feature_flags_present)
+ gsm0808_enc_vgcs_feature_flags(msg, &params->flags);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS SETUP REFUSE message, 3GPP TS 48.008 3.2.1.52.
+ * Sent from the BSC to the MSC to reject VGCS/VBS call. */
+struct msgb *gsm0808_create_vgcs_vbs_setup_refuse(enum gsm0808_cause cause)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-SETUP-REFUSE");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, cause);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS ASSIGNMENT REQUEST message, 3GPP TS 48.008 3.2.1.53.
+ * Sent from the MSC to the BSC to assign radio resources for a VGCS/VBS. */
+struct msgb *gsm0808_create_vgcs_vbs_assign_req(const struct gsm0808_vgcs_vbs_assign_req *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-ASSIGNMENT-REQUEST");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST);
+
+ /* Channel Type, 3.2.2.11 */
+ gsm0808_enc_channel_type(msg, &params->channel_type);
+
+ /* Assignment Requrirement, 3.2.2.52 */
+ gsm0808_enc_assign_req(msg, params->ass_req);
+
+ /* Cell Identifier, 3.2.2.17 */
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Group Call Reference, 3.2.2.55 */
+ gsm0808_enc_group_callref(msg, &params->callref);
+
+ /* Priority, 3.2.2.18 */
+ if (params->priority_present)
+ gsm0808_enc_priority(msg, &params->priority);
+
+ /* Circuit Identity Code, 3.2.2.2 */
+ if (params->cic_present)
+ msgb_tv16_put(msg, GSM0808_IE_CIRCUIT_IDENTITY_CODE, params->cic);
+
+ /* Downlink DTX Flag, 3.2.2.26 */
+ if (params->downlink_dtx_flag_present)
+ msgb_tv_put(msg, GSM0808_IE_DOWNLINK_DTX_FLAG, params->downlink_dtx_flag);
+
+ /* Encryption Information, 3.2.2.10 */
+ if (params->encryption_information_present)
+ gsm0808_enc_encrypt_info(msg, &params->encryption_information);
+
+ /* VSTK_RAND Imformation, 3.2.2.83 */
+ if (params->vstk_rand_present)
+ msgb_tlv_put(msg, GSM0808_IE_VSTK_RAND_INFO, sizeof(params->vstk_rand), params->vstk_rand);
+
+ /* VSTK Information, 3.2.2.84 */
+ if (params->vstk_present)
+ msgb_tlv_put(msg, GSM0808_IE_VSTK_INFO, sizeof(params->vstk), params->vstk);
+
+ /* Cell Identifier List Segment, 3.2.2.27a */
+ if (params->cils_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEGMENT, &params->cils);
+
+ /* AoIP Transport Layer Address (MGW), 3.2.2.102 */
+ if (params->aoip_transport_layer_present)
+ gsm0808_enc_aoip_trasp_addr(msg, &params->aoip_transport_layer);
+
+ /* Call Identifier, 3.2.2.105 */
+ if (params->call_id_present) {
+ /* NOTE: 3GPP TS 48.008, section 3.2.2.105 specifies that
+ * the least significant byte shall be transmitted first. */
+ msgb_v_put(msg, GSM0808_IE_CALL_ID);
+ osmo_store32le(params->call_id, msgb_put(msg, sizeof(uint32_t)));
+ }
+
+ /* Codec List (MSC Preferred) 3.2.2.103 */
+ if (params->codec_list_present) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_msc_preferred) < 0)
+ goto exit_free;
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
+}
+
+/*! Create BSSMAP VGCS/VBS ASSIGNMENT RESULT message, 3GPP TS 48.008 3.2.1.54.
+ * Sent from the BSC to the MSC to indicate assignment/deassingment of radio resources for a VGCS/VBS. */
+struct msgb *gsm0808_create_vgcs_vbs_assign_res(const struct gsm0808_vgcs_vbs_assign_res *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-ASSIGNMENT-RESULT");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT);
+
+ /* Channel Type, 3.2.2.11 */
+ gsm0808_enc_channel_type(msg, &params->channel_type);
+
+ /* Cell Identifier, 3.2.2.17 */
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Chosen Channel, 3.2.2.33 */
+ if (params->chosen_channel_present)
+ msgb_tv_put(msg, GSM0808_IE_CHOSEN_CHANNEL, params->chosen_channel);
+
+ /* Circuit Identity Code, 3.2.2.2 */
+ if (params->cic_present)
+ msgb_tv16_put(msg, GSM0808_IE_CIRCUIT_IDENTITY_CODE, params->cic);
+
+ /* Circuit Pool, 3.2.2.45 */
+ if (params->circuit_pool_present)
+ msgb_tv_put(msg, GSM0808_IE_CIRCUIT_POOL, params->circuit_pool);
+
+ /* AoIP Transport Layer Address (BSS), 3.2.2.102 */
+ if (params->aoip_transport_layer_present)
+ gsm0808_enc_aoip_trasp_addr(msg, &params->aoip_transport_layer);
+
+ /* Codec (MSC Chosen) 3.2.2.103 */
+ if (params->codec_present) {
+ if (gsm0808_enc_speech_codec2(msg, &params->codec_msc_chosen) < 0)
+ goto exit_free;
+ }
+
+ /* Call Identifier, 3.2.2.105 */
+ if (params->call_id_present) {
+ /* NOTE: 3GPP TS 48.008, section 3.2.2.105 specifies that
+ * the least significant byte shall be transmitted first. */
+ msgb_v_put(msg, GSM0808_IE_CALL_ID);
+ osmo_store32le(params->call_id, msgb_put(msg, sizeof(uint32_t)));
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
+}
+
+/*! Create BSSMAP VGCS/VBS ASSIGNMENT FAILURE message, 3GPP TS 48.008 3.2.1.55.
+ * Sent from the BSC to the MSC to indicate assignment failure for a VGCS/VBS. */
+struct msgb *gsm0808_create_vgcs_vbs_assign_fail(const struct gsm0808_vgcs_vbs_assign_fail *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-ASSIGNMENT-RESULT");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, params->cause);
+
+ /* Circuit Pool, 3.2.2.45 */
+ if (params->circuit_pool_present)
+ msgb_tv_put(msg, GSM0808_IE_CIRCUIT_POOL, params->circuit_pool);
+
+ /* Circuit Pool List, 3.2.2.46 */
+ if (params->circuit_pool_present)
+ msgb_tlv_put(msg, GSM0808_IE_CIRCUIT_POOL_LIST, params->cpl.list_len, params->cpl.pool);
+
+ /* Codec List (BSS Supported) 3.2.2.103 */
+ if (params->codec_list_present) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_bss_supported) < 0)
+ goto exit_free;
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
+}
+
+/*! Create BSSMAP VGCS/VBS QUEUING INDICATION message, 3GPP TS 48.008 3.2.1.56.
+ * Sent from the BSC to the MSC to indicate delay in assignment for a VGCS/VBS. */
+struct msgb *gsm0808_create_vgcs_queuing_ind(void)
+{
+ struct msgb *msg;
+ uint8_t val = BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-QUEUING-INDICATION");
+ if (!msg)
+ return NULL;
+
+ msg->l3h = msg->data;
+ msgb_tlv_put(msg, BSSAP_MSG_BSS_MANAGEMENT, 1, &val);
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK REQUEST message, 3GPP TS 48.008 3.2.1.57.
+ * Sent from the BSC to the MSC to indicate that a mobile requested access to uplink. */
+struct msgb *gsm0808_create_uplink_request(const struct gsm0808_uplink_request *params)
+{
+ struct msgb *msg;
+ int rc;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-REQUEST");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RQST);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->talker_priority);
+
+ /* Cell Identifier, 3.2.2.17 */
+ if (params->cell_identifier_present)
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Layer 3 Information, 3.2.2.24 */
+ if (params->l3_present)
+ msgb_tlv_put(msg, GSM0808_IE_LAYER_3_INFORMATION, params->l3.l3_len, params->l3.l3);
+
+ /* Mobile Identity, 3.2.2.41 */
+ if (params->mi_present) {
+ rc = osmo_mobile_identity_encode_msgb(msg, &params->mi, false);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK REQUEST ACKNOWLEDGE message, 3GPP TS 48.008 3.2.1.58.
+ * Sent from the MSC to the BSC to indicate that access to uplink was granted. */
+struct msgb *gsm0808_create_uplink_request_ack(const struct gsm0808_uplink_request_ack *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-REQUEST-ACKNOWLEDGE");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->talker_priority);
+
+ /* Emergency set indication, 3.2.2.90 */
+ if (params->emerg_set_ind_present)
+ msgb_v_put(msg, GSM0808_IE_EMERGENCY_SET_INDICATION);
+
+ /* Talker Identity, 3.2.2.91 */
+ if (params->talker_identity_present)
+ gsm0808_enc_talker_identity(msg, &params->talker_identity);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK CONFIRM message, 3GPP TS 48.008 3.2.1.59.
+ * Sent from the BSC to the MSC to indicate that access to uplink was has been successfully established. */
+struct msgb *gsm0808_create_uplink_request_cnf(const struct gsm0808_uplink_request_cnf *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-REQUEST-CONFIRM");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION);
+
+ /* Cell Identifier, 3.2.2.17 */
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Talker Identity, 3.2.2.91 */
+ if (params->talker_identity_present)
+ gsm0808_enc_talker_identity(msg, &params->talker_identity);
+
+ /* Layer 3 Information, 3.2.2.24 */
+ msgb_tlv_put(msg, GSM0808_IE_LAYER_3_INFORMATION, params->l3.l3_len, params->l3.l3);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK APPLICATION DATA message, 3GPP TS 48.008 3.2.1.59a.
+ * Sent from the BSC to the MSC to pass L3 info from the talker. */
+struct msgb *gsm0808_create_uplink_app_data(const struct gsm0808_uplink_app_data *params)
+{
+ struct msgb *msg;
+ uint8_t val;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-APPLICATION-DATA");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_APP_DATA);
+
+ /* Cell Identifier, 3.2.2.17 */
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Layer 3 Information, 3.2.2.24 */
+ msgb_tlv_put(msg, GSM0808_IE_LAYER_3_INFORMATION, params->l3.l3_len, params->l3.l3);
+
+ /* Application Data Information, 3.2.2.100 */
+ val = params->bt_ind;
+ msgb_tlv_put(msg, GSM0808_IE_APP_DATA_INFO, 1, &val);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK RELEASE INDICATION message, 3GPP TS 48.008 3.2.1.60.
+ * Sent from the BSC to the MSC to indicate that the uplink has been released. */
+struct msgb *gsm0808_create_uplink_release_ind(const struct gsm0808_uplink_release_ind *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-RELEASE-INDICATION");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RELEASE_INDICATION);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, params->cause);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->talker_priority);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK REJECT COMMAND message, 3GPP TS 48.008 3.2.1.61.
+ * Sent from the MSC to the BSC to indicate that the uplink is not available for allocation. */
+struct msgb *gsm0808_create_uplink_reject_cmd(const struct gsm0808_uplink_reject_cmd *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-REJECT-COMMAND");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_REJECT_CMD);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, params->cause);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->current_talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->current_talker_priority);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->rejected_talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->rejected_talker_priority);
+
+ /* Talker Identity, 3.2.2.91 */
+ if (params->talker_identity_present)
+ gsm0808_enc_talker_identity(msg, &params->talker_identity);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK RELEASE COMMAND message, 3GPP TS 48.008 3.2.1.62.
+ * Sent from the MSC to the BSC to indicate that the uplink is available for allocation. */
+struct msgb *gsm0808_create_uplink_release_cmd(const enum gsm0808_cause cause)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-RELEASE-COMMAND");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RELEASE_CMD);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, cause);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK SEIZED COMMAND message, 3GPP TS 48.008 3.2.1.62.
+ * Sent from the MSC to the BSC to indicate that the uplink is no longer available for allocation. */
+struct msgb *gsm0808_create_uplink_seized_cmd(const struct gsm0808_uplink_seized_cmd *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-SEIZED-COMMAND");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_SEIZED_CMD);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, params->cause);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->talker_priority);
+
+ /* Emergency set indication, 3.2.2.90 */
+ if (params->emerg_set_ind_present)
+ msgb_v_put(msg, GSM0808_IE_EMERGENCY_SET_INDICATION);
+
+ /* Talker Identity, 3.2.2.91 */
+ if (params->talker_identity_present)
+ gsm0808_enc_talker_identity(msg, &params->talker_identity);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS ADDITIONAL INFORMATION message, 3GPP TS 48.008 3.2.1.78.
+ * Sent from the MSC to the BSC to transfer talker identity. */
+struct msgb *gsm0808_create_vgcs_additional_info(const struct gsm0808_talker_identity *ti)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS-ADDITIONAL-INFO");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_ADDL_INFO);
+
+ /* Talker Identity, 3.2.2.91 */
+ gsm0808_enc_talker_identity(msg, ti);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS AREA CELL INFO message, 3GPP TS 48.008 3.2.1.79.
+ * Sent from the BSC to the MSC to transfer additional infos about cells. */
+struct msgb *gsm0808_create_vgcs_vbs_area_cell_info(const struct gsm0808_vgcs_vbs_area_cell_info *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-AREA-CELL-INFO");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST);
+
+ /* Cell Identifier List Segment, 3.2.2.27a */
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEGMENT, &params->cils);
+
+ /* Assignment Requrirement, 3.2.2.52 */
+ if (params->ass_req_present)
+ gsm0808_enc_assign_req(msg, params->ass_req);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS ASSIGNMENT STATUS message, 3GPP TS 48.008 3.2.1.80.
+ * Sent from the BSC to the MSC to indicate assignment status for each cell. */
+struct msgb *gsm0808_create_vgcs_vbs_assign_stat(const struct gsm0808_vgcs_vbs_assign_stat *params)
+{
+ struct msgb *msg;
+ uint8_t val;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-ASSIGNMENT-STATUS");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_STATUS);
+
+ /* Cell Identifier List Segment, 3.2.2.27b */
+ if (params->cils_est_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEG_EST_CELLS, &params->cils_est);
+ /* Cell Identifier List Segment, 3.2.2.27c */
+ if (params->cils_tbe_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEG_CELLS_TBE, &params->cils_tbe);
+ /* Cell Identifier List Segment, 3.2.2.27e */
+ if (params->cils_rel_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEG_REL_CELLS, &params->cils_rel);
+ /* Cell Identifier List Segment, 3.2.2.27f */
+ if (params->cils_ne_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEG_NE_CELLS, &params->cils_ne);
+
+ /* VGCS/VBS Cell Status, 3.2.2.94 */
+ if (params->cell_status_present) {
+ val = params->cell_status;
+ msgb_tlv_put(msg, GSM0808_IE_VGCS_VBS_CELL_STATUS, 1, &val);
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS SMS message, 3GPP TS 48.008 3.2.1.81.
+ * Sent from the MSC to the BSC to send an SMS to VGCS. */
+struct msgb *gsm0808_create_vgcs_sms(const struct gsm0808_sms_to_vgcs *sms)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS-SMS");
+ if (!msg)
+ return NULL;
+
+ /* SMS to VGCS, 3.2.2.92 */
+ msgb_tlv_put(msg, GSM0808_IE_VGCS_VBS_CELL_STATUS, sms->sms_len, sms->sms);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS/VBS) NOTIFICATION DATA message, 3GPP TS 48.008 3.2.1.82.
+ * Sent from the MSC to the BSC to send application specific data. */
+struct msgb *gsm0808_create_notification_data(const struct gsm0808_notification_data *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS-SMS");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_NOTIFICATION_DATA);
+
+ /* Application Data, 3.2.2.98 */
+ msgb_tlv_put(msg, GSM0808_IE_APP_DATA, params->app_data.data_len, params->app_data.data);
+
+ /* Data Identity, 3.2.2.99 */
+ gsm0808_enc_data_identity(msg, &params->data_ident);
+
+ /* MSISDN, 3.2.2.101 */
+ if (params->msisdn_present)
+ gsm0808_enc_msisdn(msg, params->msisdn);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/* Note that EMERGENCY RESET INDICATION and EMERGENCY RESET COMMAND cannot be implemented, due to lack of
+ * message type information in the specifications. */
+
/* As per 3GPP TS 48.008 version 11.7.0 Release 11 */
static const struct tlv_definition bss_att_tlvdef = {
.def = {
@@ -1506,6 +2244,7 @@ static const struct tlv_definition bss_att_tlvdef = {
[GSM0808_IE_LOCATION_ESTIMATE] = { TLV_TYPE_TLV },
[GSM0808_IE_POSITIONING_DATA] = { TLV_TYPE_TLV },
[GSM0808_IE_LCS_CAUSE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_LCS_CLIENT_TYPE] = { TLV_TYPE_TLV },
[GSM0808_IE_APDU] = { TLV_TYPE_TLV },
[GSM0808_IE_NETWORK_ELEMENT_IDENTITY] = { TLV_TYPE_TLV },
[GSM0808_IE_GPS_ASSISTANCE_DATA] = { TLV_TYPE_TLV },
@@ -1522,6 +2261,7 @@ static const struct tlv_definition bss_att_tlvdef = {
[GSM0800_IE_INTER_SYSTEM_INFO] = { TLV_TYPE_TLV },
[GSM0808_IE_SNA_ACCESS_INFO] = { TLV_TYPE_TLV },
[GSM0808_IE_VSTK_RAND_INFO] = { TLV_TYPE_TLV },
+ [GSM0808_IE_VSTK_INFO] = { TLV_TYPE_TLV },
[GSM0808_IE_PAGING_INFO] = { TLV_TYPE_TV },
[GSM0808_IE_IMEI] = { TLV_TYPE_TLV },
[GSM0808_IE_VELOCITY_ESTIMATE] = { TLV_TYPE_TLV },
@@ -1564,6 +2304,11 @@ static const struct tlv_definition bss_att_tlvdef = {
[GSM0808_IE_CN_TO_MS_TRANSP_INFO] = { TLV_TYPE_TLV },
[GSM0808_IE_SELECTED_PLMN_ID] = { TLV_TYPE_FIXED, 3 },
[GSM0808_IE_LAST_USED_EUTRAN_PLMN_ID] = { TLV_TYPE_FIXED, 3 },
+ [GSM0808_IE_OLD_LAI] = { TLV_TYPE_FIXED, 5 },
+ [GSM0808_IE_ATTACH_INDICATOR] = { TLV_TYPE_T },
+ [GSM0808_IE_SELECTED_OPERATOR] = { TLV_TYPE_FIXED, 3 },
+ [GSM0808_IE_PS_REGISTERED_OPERATOR] = { TLV_TYPE_FIXED, 3 },
+ [GSM0808_IE_CS_REGISTERED_OPERATOR] = { TLV_TYPE_FIXED, 3 },
/* Osmocom extensions */
[GSM0808_IE_OSMO_OSMUX_SUPPORT] = { TLV_TYPE_T },
@@ -1689,7 +2434,9 @@ static const struct value_string gsm0808_msgt_names[] = {
{ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST, "VGCS/VBS ASSIGN REQ" },
{ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT, "VGCS/VBS ASSIGN RES" },
{ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE, "VGCS/VBS ASSIGN FAIL" },
+ { BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_STATUS, "VGCS/VBS ASSIGN STATUS" },
{ BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION, "VGCS/VBS QUEUING IND" },
+ { BSS_MAP_MSG_VGCS_VBS_AREA_CELL_INFO, "VGCS/VBS AREA CELL INFO" },
{ BSS_MAP_MSG_UPLINK_RQST, "UPLINK REQ" },
{ BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE, "UPLINK REQ ACK" },
{ BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION, "UPLINK REQ CONF" },
@@ -1698,11 +2445,12 @@ static const struct value_string gsm0808_msgt_names[] = {
{ BSS_MAP_MSG_UPLINK_RELEASE_CMD, "UPLINK REL CMD" },
{ BSS_MAP_MSG_UPLINK_SEIZED_CMD, "UPLINK SEIZED CMD" },
{ BSS_MAP_MSG_VGCS_ADDL_INFO, "VGCS ADDL INFO" },
+ { BSS_MAP_MSG_VGCS_SMS, "VGCS SMS" },
{ BSS_MAP_MSG_NOTIFICATION_DATA, "NOTIF DATA" },
{ BSS_MAP_MSG_UPLINK_APP_DATA, "UPLINK APP DATA" },
{ BSS_MAP_MSG_LCLS_CONNECT_CTRL, "LCLS-CONNECT-CONTROL" },
- { BSS_MAP_MSG_LCLS_CONNECT_CTRL_ACK, "CLS-CONNECT-CONTROL-ACK" },
+ { BSS_MAP_MSG_LCLS_CONNECT_CTRL_ACK, "LCLS-CONNECT-CONTROL-ACK" },
{ BSS_MAP_MSG_LCLS_NOTIFICATION, "LCLS-NOTIFICATION" },
{ 0, NULL }
@@ -1736,6 +2484,7 @@ const struct value_string gsm0808_speech_codec_type_names[] = {
{ GSM0808_SCT_HR3, "HR3" },
{ GSM0808_SCT_HR4, "HR4" },
{ GSM0808_SCT_HR6, "HR6" },
+ { GSM0808_SCT_EXT, "Codec Extension" },
{ GSM0808_SCT_CSD, "CSD" },
{ 0, NULL }
};
@@ -1942,4 +2691,101 @@ const struct value_string gsm0808_lcls_status_names[] = {
{ 0, NULL }
};
+/* Convert one S0-S15 bit to its set of AMR modes, for HR AMR and FR AMR.
+ * This is 3GPP TS 28.062 Table 7.11.3.1.3-2: "Preferred Configurations", with some configurations removed as specified
+ * in 3GPP TS 48.008 3.2.2.103:
+ *
+ * FR_AMR is coded ‘0011’.
+ * S11, S13 and S15 are reserved and coded with zeroes.
+ *
+ * HR_AMR is coded ‘0100’.
+ * S6 - S7 and S11 – S15 are reserved and coded with zeroes.
+ *
+ * Meaning: for FR, exclude all Optimisation Mode configurations.
+ * For HR, exclude all that are not supported by HR AMR -- drop all that include at least one of
+ * 10.2 or 12.2.
+ *
+ * Also, for HR, drop 12.2k from S1.
+ *
+ * The first array dimension is 0 for half rate and 1 for full rate.
+ * The second array dimension is the configuration number (0..15) aka Sn.
+ * The values are bitmask combinations of (1 << GSM0808_AMR_MODE_nnnn).
+ *
+ * For example, accumulate all modes that are possible in a given my_s15_s0:
+ *
+ * uint8_t modes = 0;
+ * for (s_bit = 0; s_bit < 15; s_bit++)
+ * if (my_s15_s0 & (1 << s_bit))
+ * modes |= gsm0808_amr_modes_from_cfg[full_rate ? 1 : 0][s_bit];
+ * for (i = 0; i < 8; i++)
+ * if (modes & (1 << i))
+ * printf(" %s", gsm0808_amr_mode_name(i));
+ */
+const uint8_t gsm0808_amr_modes_from_cfg[2][16] = {
+#define B(X) (1 << (GSM0808_AMR_MODE_##X))
+ /* HR */
+ {
+ /* Sn = modes */
+ [0] = B(4_75),
+ [1] = B(4_75) | B(5_90) | B(7_40),
+ [2] = B(5_90),
+ [3] = B(6_70),
+ [4] = B(7_40),
+ [5] = B(7_95),
+ [6] = 0,
+ [7] = 0,
+
+ [8] = B(4_75) | B(5_90),
+ [9] = B(4_75) | B(5_90) | B(6_70),
+ [10] = B(4_75) | B(5_90) | B(6_70) | B(7_40),
+ [11] = 0,
+ [12] = 0,
+ [13] = 0,
+ [14] = 0,
+ [15] = 0,
+ },
+ /* FR */
+ {
+ /* Sn = modes */
+ [0] = B(4_75),
+ [1] = B(4_75) | B(5_90) | B(7_40) | B(12_2),
+ [2] = B(5_90),
+ [3] = B(6_70),
+ [4] = B(7_40),
+ [5] = B(7_95),
+ [6] = B(10_2),
+ [7] = B(12_2),
+
+ [8] = B(4_75) | B(5_90),
+ [9] = B(4_75) | B(5_90) | B(6_70),
+ [10] = B(4_75) | B(5_90) | B(6_70) | B(7_40),
+ [11] = 0,
+ [12] = B(4_75) | B(5_90) | B(6_70) | B(10_2),
+ [13] = 0,
+ [14] = B(4_75) | B(5_90) | B(7_95) | B(12_2),
+ [15] = 0,
+ }
+};
+
+/* AMR mode names from GSM0808_AMR_MODE_*, for use with gsm0808_amr_modes_from_cfg.
+ *
+ * For example:
+ * printf("S9: ");
+ * uint8_t s9_modes = gsm0808_amr_modes_from_cfg[full_rate ? 1 : 0][9];
+ * for (bit = 0; bit < 8; bit++)
+ * if (s9_modes & (1 << bit))
+ * printf("%s,", gsm0808_amr_mode_name(bit));
+ */
+const struct value_string gsm0808_amr_mode_names[] = {
+ { GSM0808_AMR_MODE_4_75, "4.75" },
+ { GSM0808_AMR_MODE_5_15, "5.15" },
+ { GSM0808_AMR_MODE_5_90, "5.90" },
+ { GSM0808_AMR_MODE_6_70, "6.70" },
+ { GSM0808_AMR_MODE_7_40, "7.40" },
+ { GSM0808_AMR_MODE_7_95, "7.95" },
+ { GSM0808_AMR_MODE_10_2, "10.2" },
+ { GSM0808_AMR_MODE_12_2, "12.2" },
+ {}
+};
+
/*! @} */
diff --git a/src/gsm/gsm0808_utils.c b/src/gsm/gsm0808_utils.c
index 94c9a796..c9f26d35 100644
--- a/src/gsm/gsm0808_utils.c
+++ b/src/gsm/gsm0808_utils.c
@@ -78,7 +78,7 @@ uint8_t gsm0808_enc_cause(struct msgb *msg, uint16_t cause)
/*! Encode TS 08.08 AoIP transport address IE
* \param[out] msg Message Buffer to which to append IE
* \param[in] ss Socket Address to be used in IE
- * \returns number of bytes added to \a msg */
+ * \returns number of bytes added to \a msg; 0 if msg is too small */
uint8_t gsm0808_enc_aoip_trasp_addr(struct msgb *msg,
const struct sockaddr_storage *ss)
{
@@ -87,16 +87,25 @@ uint8_t gsm0808_enc_aoip_trasp_addr(struct msgb *msg,
struct sockaddr_in6 *sin6;
uint16_t port = 0;
uint8_t *ptr;
- uint8_t *old_tail;
- uint8_t *tlv_len;
+ const uint8_t len_tl = 2;
+ uint8_t len_v = sizeof(port);
- OSMO_ASSERT(msg);
- OSMO_ASSERT(ss);
OSMO_ASSERT(ss->ss_family == AF_INET || ss->ss_family == AF_INET6);
+ switch (ss->ss_family) {
+ case AF_INET:
+ len_v += IP_V4_ADDR_LEN;
+ break;
+ case AF_INET6:
+ len_v += IP_V6_ADDR_LEN;
+ break;
+ }
+
+ if (msgb_tailroom(msg) < len_tl + len_v)
+ return 0;
+
msgb_put_u8(msg, GSM0808_IE_AOIP_TRASP_ADDR);
- tlv_len = msgb_put(msg,1);
- old_tail = msg->tail;
+ msgb_put_u8(msg, len_v);
switch (ss->ss_family) {
case AF_INET:
@@ -114,9 +123,7 @@ uint8_t gsm0808_enc_aoip_trasp_addr(struct msgb *msg,
}
msgb_put_u16(msg, port);
-
- *tlv_len = (uint8_t) (msg->tail - old_tail);
- return *tlv_len + 2;
+ return len_tl + len_v;
}
/*! Decode TS 08.08 AoIP transport address IE
@@ -132,7 +139,6 @@ int gsm0808_dec_aoip_trasp_addr(struct sockaddr_storage *ss,
struct sockaddr_in6 sin6;
const uint8_t *old_elem = elem;
- OSMO_ASSERT(ss);
if (!elem)
return -EINVAL;
if (len == 0)
@@ -180,7 +186,6 @@ int gsm0808_dec_aoip_trasp_addr(struct sockaddr_storage *ss,
* \returns number of bytes parsed */
int gsm0808_dec_osmux_cid(uint8_t *cid, const uint8_t *elem, uint8_t len)
{
- OSMO_ASSERT(cid);
if (!elem)
return -EINVAL;
if (len != 1)
@@ -204,21 +209,21 @@ static void decode_lai(const uint8_t *data, struct osmo_location_area_id *decode
gsm48_decode_lai2(&lai, decoded);
}
-/* Helper function for gsm0808_enc_speech_codec()
- * and gsm0808_enc_speech_codec_list() */
-static uint8_t enc_speech_codec(struct msgb *msg,
- const struct gsm0808_speech_codec *sc)
+/* Helper function for gsm0808_enc_speech_codec[_list]().
+ * Returns number of bytes appended; negative on error. */
+static int enc_speech_codec(struct msgb *msg,
+ const struct gsm0808_speech_codec *sc)
{
/* See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
uint8_t header = 0;
uint8_t *old_tail;
- bool type_extended = false;
+ bool type_extended;
/* Note: Extended codec types are codec types that require 8 instead
* of 4 bit to fully specify the selected codec. In the following,
* we check if we work with an extended type or not. We also check
* if the codec type is valid at all. */
- switch(sc->type) {
+ switch (sc->type) {
case GSM0808_SCT_FR1:
case GSM0808_SCT_FR2:
case GSM0808_SCT_FR3:
@@ -235,8 +240,7 @@ static uint8_t enc_speech_codec(struct msgb *msg,
break;
default:
/* Invalid codec type specified */
- OSMO_ASSERT(false);
- break;
+ return -EINVAL;
}
old_tail = msg->tail;
@@ -251,11 +255,10 @@ static uint8_t enc_speech_codec(struct msgb *msg,
header |= (1 << 4);
if (type_extended) {
- header |= 0x0f;
+ header |= GSM0808_SCT_EXT;
msgb_put_u8(msg, header);
msgb_put_u8(msg, sc->type);
} else {
- OSMO_ASSERT(sc->type < 0x0f);
header |= sc->type;
msgb_put_u8(msg, header);
}
@@ -273,11 +276,13 @@ static uint8_t enc_speech_codec(struct msgb *msg,
case GSM0808_SCT_FR5:
case GSM0808_SCT_HR4:
case GSM0808_SCT_CSD:
- OSMO_ASSERT((sc->cfg & 0xff00) == 0);
+ if (sc->cfg >> 8)
+ return -EINVAL;
msgb_put_u8(msg, (uint8_t) sc->cfg & 0xff);
break;
default:
- OSMO_ASSERT(sc->cfg == 0);
+ if (sc->cfg != 0)
+ return -EINVAL;
break;
}
@@ -287,27 +292,38 @@ static uint8_t enc_speech_codec(struct msgb *msg,
/*! Encode TS 08.08 Speech Codec IE
* \param[out] msg Message Buffer to which IE will be appended
* \param[in] sc Speech Codec to be encoded into IE
- * \returns number of bytes appended to \a msg */
-uint8_t gsm0808_enc_speech_codec(struct msgb *msg,
- const struct gsm0808_speech_codec *sc)
+ * \returns number of bytes appended to \a msg; negative on error */
+int gsm0808_enc_speech_codec2(struct msgb *msg,
+ const struct gsm0808_speech_codec *sc)
{
/*! See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
- uint8_t *old_tail;
uint8_t *tlv_len;
-
- OSMO_ASSERT(msg);
- OSMO_ASSERT(sc);
+ int rc;
msgb_put_u8(msg, GSM0808_IE_SPEECH_CODEC);
tlv_len = msgb_put(msg, 1);
- old_tail = msg->tail;
- enc_speech_codec(msg, sc);
+ rc = enc_speech_codec(msg, sc);
+ if (rc < 0)
+ return rc;
- *tlv_len = (uint8_t) (msg->tail - old_tail);
+ *tlv_len = rc;
return *tlv_len + 2;
}
+/*! Deprecated: gsm0808_enc_speech_codec2() wrapper for backwards compatibility.
+ * Mimics the old behavior: crash on missing and/or invalid input. */
+uint8_t gsm0808_enc_speech_codec(struct msgb *msg,
+ const struct gsm0808_speech_codec *sc)
+{
+ int rc;
+
+ rc = gsm0808_enc_speech_codec2(msg, sc);
+ OSMO_ASSERT(rc > 0);
+
+ return rc;
+}
+
/*! Decode TS 08.08 Speech Codec IE
* \param[out] sc Caller-allocated memory for Speech Codec
* \param[in] elem IE value to be decoded
@@ -320,7 +336,6 @@ int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc,
uint8_t header;
const uint8_t *old_elem = elem;
- OSMO_ASSERT(sc);
if (!elem)
return -EINVAL;
if (len == 0)
@@ -332,7 +347,7 @@ int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc,
/* An extended codec type needs at least two fields,
* bail if the input data length is not sufficient. */
- if ((header & 0x0F) == 0x0F && len < 2)
+ if ((header & 0x0F) == GSM0808_SCT_EXT && len < 2)
return -EINVAL;
elem++;
@@ -347,7 +362,7 @@ int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc,
if (header & (1 << 4))
sc->tf = true;
- if ((header & 0x0F) != 0x0F) {
+ if ((header & 0x0F) != GSM0808_SCT_EXT) {
sc->type = (header & 0x0F);
} else {
sc->type = *elem;
@@ -392,35 +407,46 @@ int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc,
/*! Encode TS 08.08 Speech Codec list
* \param[out] msg Message Buffer to which IE is to be appended
* \param[in] scl Speech Codec List to be encoded into IE
- * \returns number of bytes added to \a msg */
-uint8_t gsm0808_enc_speech_codec_list(struct msgb *msg,
- const struct gsm0808_speech_codec_list *scl)
+ * \returns number of bytes added to \a msg; negative on error */
+int gsm0808_enc_speech_codec_list2(struct msgb *msg,
+ const struct gsm0808_speech_codec_list *scl)
{
/*! See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
uint8_t *old_tail;
uint8_t *tlv_len;
unsigned int i;
- uint8_t rc;
unsigned int bytes_used = 0;
- OSMO_ASSERT(msg);
- OSMO_ASSERT(scl);
-
msgb_put_u8(msg, GSM0808_IE_SPEECH_CODEC_LIST);
tlv_len = msgb_put(msg, 1);
old_tail = msg->tail;
for (i = 0; i < scl->len; i++) {
- rc = enc_speech_codec(msg, &scl->codec[i]);
- OSMO_ASSERT(rc >= 1);
+ int rc = enc_speech_codec(msg, &scl->codec[i]);
+ if (rc < 1)
+ return rc;
bytes_used += rc;
- OSMO_ASSERT(bytes_used <= 255);
+ if (bytes_used > 0xff)
+ return -EOVERFLOW;
}
*tlv_len = (uint8_t) (msg->tail - old_tail);
return *tlv_len + 2;
}
+/*! Deprecated: gsm0808_enc_speech_codec_list2() wrapper for backwards compatibility.
+ * Mimics the old behavior: crash on missing and/or invalid input. */
+uint8_t gsm0808_enc_speech_codec_list(struct msgb *msg,
+ const struct gsm0808_speech_codec_list *scl)
+{
+ int rc;
+
+ rc = gsm0808_enc_speech_codec_list2(msg, scl);
+ OSMO_ASSERT(rc > 0);
+
+ return rc;
+}
+
/*! Decode TS 08.08 Speech Codec list IE
* \param[out] scl Caller-provided memory to store codec list
* \param[in] elem IE value to be decoded
@@ -435,7 +461,6 @@ int gsm0808_dec_speech_codec_list(struct gsm0808_speech_codec_list *scl,
int rc;
uint8_t decoded = 0;
- OSMO_ASSERT(scl);
if (!elem)
return -EINVAL;
@@ -472,16 +497,8 @@ uint8_t gsm0808_enc_channel_type(struct msgb *msg,
uint8_t *old_tail;
uint8_t *tlv_len;
- OSMO_ASSERT(msg);
- OSMO_ASSERT(ct);
OSMO_ASSERT(ct->perm_spch_len <= CHANNEL_TYPE_ELEMENT_MAXLEN - 2);
- /* FIXME: Implement encoding support for Data
- * and Speech + CTM Text Telephony */
- if ((ct->ch_indctr & 0x0f) != GSM0808_CHAN_SPEECH
- && (ct->ch_indctr & 0x0f) != GSM0808_CHAN_SIGN)
- OSMO_ASSERT(false);
-
msgb_put_u8(msg, GSM0808_IE_CHANNEL_TYPE);
tlv_len = msgb_put(msg, 1);
old_tail = msg->tail;
@@ -489,12 +506,45 @@ uint8_t gsm0808_enc_channel_type(struct msgb *msg,
msgb_put_u8(msg, ct->ch_indctr & 0x0f);
msgb_put_u8(msg, ct->ch_rate_type);
- for (i = 0; i < ct->perm_spch_len; i++) {
- byte = ct->perm_spch[i];
+ switch (ct->ch_indctr) {
+ case GSM0808_CHAN_DATA:
+ byte = ct->data_rate;
+
+ if (!ct->data_transparent)
+ byte |= 0x40; /* Set T/NT */
+
+ if (ct->data_rate_allowed_is_set) {
+ OSMO_ASSERT(!ct->data_transparent);
+ byte |= 0x80; /* Set ext */
+ msgb_put_u8(msg, byte);
+
+ byte = ct->data_rate_allowed;
+ if (ct->data_asym_pref_is_set) {
+ byte |= 0x80; /* Set ext */
+ msgb_put_u8(msg, byte);
- if (i < ct->perm_spch_len - 1)
- byte |= 0x80;
+ /* Set asymmetry indication, rest is spare */
+ byte = ct->data_asym_pref << 5;
+ }
+ }
msgb_put_u8(msg, byte);
+ break;
+ case GSM0808_CHAN_SPEECH:
+ case GSM0808_CHAN_SPEECH_CTM_TEXT_TELEPHONY:
+ for (i = 0; i < ct->perm_spch_len; i++) {
+ byte = ct->perm_spch[i];
+
+ if (i < ct->perm_spch_len - 1)
+ byte |= 0x80;
+ msgb_put_u8(msg, byte);
+ }
+ break;
+ case GSM0808_CHAN_SIGN:
+ /* Octet 5 is spare */
+ msgb_put_u8(msg, 0);
+ break;
+ default:
+ OSMO_ASSERT(false);
}
*tlv_len = (uint8_t) (msg->tail - old_tail);
@@ -514,7 +564,6 @@ int gsm0808_dec_channel_type(struct gsm0808_channel_type *ct,
uint8_t byte;
const uint8_t *old_elem = elem;
- OSMO_ASSERT(ct);
if (!elem)
return -EINVAL;
if (len < 3 || len > 11)
@@ -524,17 +573,60 @@ int gsm0808_dec_channel_type(struct gsm0808_channel_type *ct,
ct->ch_indctr = (*elem) & 0x0f;
elem++;
- ct->ch_rate_type = (*elem) & 0x0f;
+ ct->ch_rate_type = *elem;
elem++;
- for (i = 0; i < ARRAY_SIZE(ct->perm_spch); i++) {
+ switch (ct->ch_indctr) {
+ case GSM0808_CHAN_DATA:
byte = *elem;
elem++;
- ct->perm_spch[i] = byte & 0x7f;
- if ((byte & 0x80) == 0x00)
- break;
+ ct->data_transparent = !(byte & 0x40); /* T/NT */
+ ct->data_rate = byte & 0x3f;
+
+ /* Optional extension for non-transparent service */
+ if (byte & 0x80) {
+ if (ct->data_transparent)
+ return -EINVAL;
+ if (elem - old_elem >= len)
+ return -EOVERFLOW;
+ byte = *elem;
+ elem++;
+
+ ct->data_rate_allowed_is_set = true;
+ ct->data_rate_allowed = byte & 0x7f;
+
+ /* Optional extension */
+ if (byte & 0x80) {
+ if (elem - old_elem >= len)
+ return -EOVERFLOW;
+ byte = *elem;
+ elem++;
+
+ ct->data_asym_pref_is_set = true;
+ ct->data_asym_pref = byte & 0x60 >> 5;
+ }
+ }
+ break;
+ case GSM0808_CHAN_SPEECH:
+ case GSM0808_CHAN_SPEECH_CTM_TEXT_TELEPHONY:
+ for (i = 0; i < ARRAY_SIZE(ct->perm_spch); i++) {
+ if (elem - old_elem >= len)
+ return -EOVERFLOW;
+
+ byte = *elem;
+ elem++;
+ ct->perm_spch[i] = byte & 0x7f;
+ if ((byte & 0x80) == 0x00)
+ break;
+ }
+ ct->perm_spch_len = i + 1;
+ break;
+ case GSM0808_CHAN_SIGN:
+ /* Octet 5 is spare */
+ break;
+ default:
+ return -ENOTSUP;
}
- ct->perm_spch_len = i + 1;
return (int)(elem - old_elem);
}
@@ -703,8 +795,6 @@ uint8_t gsm0808_enc_encrypt_info(struct msgb *msg,
uint8_t *old_tail;
uint8_t *tlv_len;
- OSMO_ASSERT(msg);
- OSMO_ASSERT(ei);
OSMO_ASSERT(ei->key_len <= ARRAY_SIZE(ei->key));
OSMO_ASSERT(ei->perm_algo_len <= ENCRY_INFO_PERM_ALGO_MAXLEN);
@@ -721,6 +811,8 @@ uint8_t gsm0808_enc_encrypt_info(struct msgb *msg,
}
msgb_put_u8(msg, perm_algo);
+ /* FIXME: 48.008 3.2.2.10 Encryption Information says:
+ * "When present, the key shall be 8 octets long." */
ptr = msgb_put(msg, ei->key_len);
memcpy(ptr, ei->key, ei->key_len);
@@ -741,7 +833,6 @@ int gsm0808_dec_encrypt_info(struct gsm0808_encrypt_info *ei,
unsigned int perm_algo_len = 0;
const uint8_t *old_elem = elem;
- OSMO_ASSERT(ei);
if (!elem)
return -EINVAL;
if (len == 0)
@@ -760,6 +851,8 @@ int gsm0808_dec_encrypt_info(struct gsm0808_encrypt_info *ei,
}
ei->perm_algo_len = perm_algo_len;
+ /* FIXME: 48.008 3.2.2.10 Encryption Information says:
+ * "When present, the key shall be 8 octets long." */
ei->key_len = len - 1;
memcpy(ei->key, elem, ei->key_len);
elem+=ei->key_len;
@@ -767,6 +860,30 @@ int gsm0808_dec_encrypt_info(struct gsm0808_encrypt_info *ei,
return (int)(elem - old_elem);
}
+/*! Encode TS 48.008 Kc128 IE.
+ * \param[out] msg Message Buffer to which IE is to be appended.
+ * \param[in] kc128 Pointer to 16 bytes of Kc128 key data.
+ * \returns number of bytes appended to msg */
+int gsm0808_enc_kc128(struct msgb *msg, const uint8_t *kc128)
+{
+ uint8_t *start = msg->tail;
+ msgb_tv_fixed_put(msg, GSM0808_IE_KC_128, 16, kc128);
+ return msg->tail - start;
+}
+
+/*! Decode TS 48.008 Kc128 IE.
+ * \param[out] kc128 Target buffer for received Kc128 key, 16 bytes long.
+ * \param[in] elem IE value to be decoded (without IE discriminator).
+ * \param[in] len Length of elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_kc128(uint8_t *kc128, const uint8_t *elem, uint8_t len)
+{
+ if (len != 16)
+ return -EINVAL;
+ memcpy(kc128, elem, 16);
+ return len;
+}
+
/* Store individual Cell Identifier information in a CGI, without clearing the remaining ones.
* This is useful to supplement one CGI with information from more than one Cell Identifier,
* which in turn is useful to match Cell Identifiers of differing kinds to each other.
@@ -803,6 +920,10 @@ static void cell_id_to_cgi(struct osmo_cell_global_id *dst,
dst->lai.lac = u->lac;
return;
+ case CELL_IDENT_SAI:
+ dst->lai = u->sai.lai;
+ return;
+
case CELL_IDENT_NO_CELL:
case CELL_IDENT_BSS:
case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
@@ -830,6 +951,8 @@ int gsm0808_cell_id_size(enum CELL_IDENT discr)
case CELL_IDENT_BSS:
case CELL_IDENT_NO_CELL:
return 0;
+ case CELL_IDENT_SAI:
+ return 7;
case CELL_IDENT_WHOLE_GLOBAL_PS:
return 8;
default:
@@ -876,6 +999,12 @@ int gsm0808_decode_cell_id_u(union gsm0808_cell_id_u *out, enum CELL_IDENT discr
case CELL_IDENT_NO_CELL:
/* Does not have any list items */
break;
+ case CELL_IDENT_SAI:
+ if (len < 7)
+ return -EINVAL;
+ decode_lai(buf, &out->sai.lai);
+ out->sai.sac = osmo_load16be(buf + sizeof(struct gsm48_loc_area_id));
+ break;
case CELL_IDENT_WHOLE_GLOBAL_PS:
/* 3GPP TS 48.018 sec 11.3.9 "Cell Identifier" */
if (len < 8)
@@ -925,6 +1054,16 @@ void gsm0808_msgb_put_cell_id_u(struct msgb *msg, enum CELL_IDENT id_discr, cons
case CELL_IDENT_NO_CELL:
/* Does not have any list items */
break;
+
+ case CELL_IDENT_SAI: {
+ const struct osmo_service_area_id *id = &u->sai;
+ struct gsm48_loc_area_id lai;
+ gsm48_generate_lai2(&lai, &id->lai);
+ memcpy(msgb_put(msg, sizeof(lai)), &lai, sizeof(lai));
+ msgb_put_u16(msg, id->sac);
+ break;
+ }
+
case CELL_IDENT_WHOLE_GLOBAL_PS: {
/* 3GPP TS 48.018 sec 11.3.9 "Cell Identifier" */
const struct osmo_cell_global_id_ps *id = &u->global_ps;
@@ -953,9 +1092,6 @@ uint8_t gsm0808_enc_cell_id_list2(struct msgb *msg,
unsigned int i;
uint8_t id_discr;
- OSMO_ASSERT(msg);
- OSMO_ASSERT(cil);
-
msgb_put_u8(msg, GSM0808_IE_CELL_IDENTIFIER_LIST);
tlv_len = msgb_put(msg, 1);
old_tail = msg->tail;
@@ -997,9 +1133,6 @@ uint8_t gsm0808_enc_cell_id_list(struct msgb *msg,
uint8_t *tlv_len;
unsigned int i;
- OSMO_ASSERT(msg);
- OSMO_ASSERT(cil);
-
msgb_put_u8(msg, GSM0808_IE_CELL_IDENTIFIER_LIST);
tlv_len = msgb_put(msg, 1);
old_tail = msg->tail;
@@ -1138,6 +1271,31 @@ static int parse_cell_id_lac_list(struct gsm0808_cell_id_list2 *cil, const uint8
return i;
}
+static int parse_cell_id_sai_list(struct gsm0808_cell_id_list2 *cil, const uint8_t *data, size_t remain, size_t *consumed)
+{
+ struct osmo_service_area_id *id;
+ uint16_t *sac_be;
+ size_t lai_offset;
+ int i = 0;
+ const size_t elemlen = sizeof(struct gsm48_loc_area_id) + sizeof(*sac_be);
+
+ *consumed = 0;
+ while (remain >= elemlen) {
+ if (i >= GSM0808_CELL_ID_LIST2_MAXLEN)
+ return -ENOSPC;
+ id = &cil->id_list[i].sai;
+ lai_offset = i * elemlen;
+ decode_lai(&data[lai_offset], &id->lai);
+ sac_be = (uint16_t *)(&data[lai_offset + sizeof(struct gsm48_loc_area_id)]);
+ id->sac = osmo_load16be(sac_be);
+ *consumed += elemlen;
+ remain -= elemlen;
+ i++;
+ }
+
+ return i;
+}
+
/*! Decode Cell Identifier List IE
* \param[out] cil Caller-provided memory to store Cell ID list
* \param[in] elem IE value to be decoded
@@ -1150,7 +1308,6 @@ int gsm0808_dec_cell_id_list2(struct gsm0808_cell_id_list2 *cil,
size_t bytes_elem = 0;
int list_len = 0;
- OSMO_ASSERT(cil);
if (!elem)
return -EINVAL;
if (len == 0)
@@ -1182,6 +1339,12 @@ int gsm0808_dec_cell_id_list2(struct gsm0808_cell_id_list2 *cil,
case CELL_IDENT_NO_CELL:
/* Does not have any list items */
break;
+ case CELL_IDENT_SAI:
+ list_len = parse_cell_id_sai_list(cil, elem, len, &bytes_elem);
+ break;
+ case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
+ case CELL_IDENT_UTRAN_RNC:
+ case CELL_IDENT_UTRAN_LAC_RNC:
default:
/* Remaining cell identification types are not implemented. */
return -EINVAL;
@@ -1212,7 +1375,6 @@ int gsm0808_dec_cell_id_list(struct gsm0808_cell_id_list *cil,
const uint8_t *old_elem = elem;
unsigned int item_count = 0;
- OSMO_ASSERT(cil);
if (!elem)
return -EINVAL;
if (len == 0)
@@ -1370,9 +1532,6 @@ uint8_t gsm0808_enc_cell_id(struct msgb *msg, const struct gsm0808_cell_id *ci)
.id_list_len = 1,
};
- OSMO_ASSERT(msg);
- OSMO_ASSERT(ci);
-
ie_tag = msg->tail;
rc = gsm0808_enc_cell_id_list2(msg, &cil);
@@ -1675,6 +1834,8 @@ int gsm0808_cell_id_u_name(char *buf, size_t buflen,
return snprintf(buf, buflen, "%s", osmo_cgi_name(&u->global));
case CELL_IDENT_WHOLE_GLOBAL_PS:
return snprintf(buf, buflen, "%s", osmo_cgi_ps_name(&u->global_ps));
+ case CELL_IDENT_SAI:
+ return snprintf(buf, buflen, "%s", osmo_sai_name(&u->sai));
default:
/* For CELL_IDENT_BSS and CELL_IDENT_NO_CELL, just print the discriminator.
* Same for kinds we have no string representation of yet. */
@@ -1831,6 +1992,12 @@ void gsm0808_cell_id_from_cgi(struct gsm0808_cell_id *cid, enum CELL_IDENT id_di
cid->id.lac = cgi->lai.lac;
return;
+ case CELL_IDENT_SAI:
+ cid->id.sai = (struct osmo_service_area_id){
+ .lai = cgi->lai,
+ };
+ return;
+
case CELL_IDENT_NO_CELL:
case CELL_IDENT_BSS:
case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
@@ -1877,6 +2044,10 @@ int gsm0808_cell_id_to_cgi(struct osmo_cell_global_id *cgi, const struct gsm0808
cgi->lai.lac = cid->id.lac;
return OSMO_CGI_PART_LAC;
+ case CELL_IDENT_SAI:
+ cgi->lai = cid->id.sai.lai;
+ return OSMO_CGI_PART_PLMN | OSMO_CGI_PART_LAC;
+
case CELL_IDENT_NO_CELL:
case CELL_IDENT_BSS:
case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
@@ -1899,29 +2070,17 @@ const struct value_string gsm0808_cell_id_discr_names[] = {
{ CELL_IDENT_UTRAN_PLMN_LAC_RNC, "UTRAN-PLMN-LAC-RNC" },
{ CELL_IDENT_UTRAN_RNC, "UTRAN-RNC" },
{ CELL_IDENT_UTRAN_LAC_RNC, "UTRAN-LAC-RNC" },
+ { CELL_IDENT_SAI, "SAI" },
{ CELL_IDENT_WHOLE_GLOBAL_PS, "CGI-PS"},
{ 0, NULL }
};
-#define APPEND_THING(func, args...) do { \
- int remain = buflen - (pos - buf); \
- int l = func(pos, remain, ##args); \
- if (l < 0 || l > remain) \
- pos = buf + buflen; \
- else \
- pos += l; \
- if (l > 0) \
- total_len += l; \
- } while(0)
-#define APPEND_STR(fmt, args...) APPEND_THING(snprintf, fmt, ##args)
-#define APPEND_CELL_ID_U(DISCR, U) APPEND_THING(gsm0808_cell_id_u_name, DISCR, U)
-
char *gsm0808_cell_id_name_buf(char *buf, size_t buflen, const struct gsm0808_cell_id *cid)
{
- char *pos = buf;
- int total_len = 0;
- APPEND_STR("%s:", gsm0808_cell_id_discr_name(cid->id_discr));
- APPEND_CELL_ID_U(cid->id_discr, &cid->id);
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+
+ OSMO_STRBUF_PRINTF(sb, "%s:", gsm0808_cell_id_discr_name(cid->id_discr));
+ OSMO_STRBUF_APPEND(sb, gsm0808_cell_id_u_name, cid->id_discr, &cid->id);
return buf;
}
@@ -1968,30 +2127,31 @@ char *gsm0808_cell_id_name_c(const void *ctx, const struct gsm0808_cell_id *cid)
*/
int gsm0808_cell_id_list_name_buf(char *buf, size_t buflen, const struct gsm0808_cell_id_list2 *cil)
{
- char *pos = buf;
- int total_len = 0;
- int i;
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
- APPEND_STR("%s[%u]", gsm0808_cell_id_discr_name(cil->id_discr), cil->id_list_len);
+ OSMO_STRBUF_PRINTF(sb, "%s[%u]",
+ gsm0808_cell_id_discr_name(cil->id_discr),
+ cil->id_list_len);
switch (cil->id_discr) {
case CELL_IDENT_BSS:
case CELL_IDENT_NO_CELL:
- return total_len;
+ return sb.chars_needed;
default:
break;
}
- APPEND_STR(":{");
+ OSMO_STRBUF_PRINTF(sb, ":{");
- for (i = 0; i < cil->id_list_len; i++) {
+ for (unsigned int i = 0; i < cil->id_list_len; i++) {
if (i)
- APPEND_STR(", ");
- APPEND_CELL_ID_U(cil->id_discr, &cil->id_list[i]);
+ OSMO_STRBUF_PRINTF(sb, ", ");
+ OSMO_STRBUF_APPEND(sb, gsm0808_cell_id_u_name,
+ cil->id_discr, &cil->id_list[i]);
}
- APPEND_STR("}");
- return total_len;
+ OSMO_STRBUF_PRINTF(sb, "}");
+ return sb.chars_needed;
}
/*! Return a human-readable representation of \a cil in a static buffer.
@@ -2013,9 +2173,6 @@ char *gsm0808_cell_id_list_name_c(const void *ctx, const struct gsm0808_cell_id_
return buf;
}
-#undef APPEND_STR
-#undef APPEND_CELL_ID_U
-
char *gsm0808_channel_type_name_buf(char *buf, size_t buf_len, const struct gsm0808_channel_type *ct)
{
snprintf(buf, buf_len, "ch_indctr=0x%x ch_rate_type=0x%x perm_spch=%s",
@@ -2038,4 +2195,367 @@ char *gsm0808_channel_type_name_c(const void *ctx, const struct gsm0808_channel_
return gsm0808_channel_type_name_buf(buf, 128, ct);
}
+/*! Encode Group Call Reference IE (3GPP TS 48.008 3.2.2.55).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] gc Group Call Reference to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_group_callref(struct msgb *msg, const struct gsm0808_group_callref *gc)
+{
+ uint8_t *old_tail;
+ uint8_t *tlv_len;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(gc);
+
+ msgb_put_u8(msg, GSM0808_IE_GROUP_CALL_REFERENCE);
+ tlv_len = msgb_put(msg, 1);
+ old_tail = msg->tail;
+
+ memcpy(msgb_put(msg, sizeof(*gc)), gc, sizeof(*gc));
+
+ *tlv_len = (uint8_t) (msg->tail - old_tail);
+ return *tlv_len + 2;
+}
+
+/*! Decode Group Call Reference IE (3GPP TS 48.008 3.2.2.55).
+ * \param[out] gc Group Call Reference structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_group_callref(struct gsm0808_group_callref *gc, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(gc);
+ OSMO_ASSERT(elem);
+
+ if (len != sizeof(*gc))
+ return -EINVAL;
+
+ memcpy(gc, elem, sizeof(*gc));
+
+ return len;
+}
+
+/*! Encode Priority IE (3GPP TS 48.008 3.2.2.18).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] pri Priority to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_priority(struct msgb *msg, const struct gsm0808_priority *pri)
+{
+ uint8_t *old_tail;
+ uint8_t *tlv_len;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(pri);
+
+ msgb_put_u8(msg, GSM0808_IE_PRIORITY);
+ tlv_len = msgb_put(msg, 1);
+ old_tail = msg->tail;
+
+ memcpy(msgb_put(msg, sizeof(*pri)), pri, sizeof(*pri));
+
+ *tlv_len = (uint8_t) (msg->tail - old_tail);
+ return *tlv_len + 2;
+}
+
+/*! Decode Priority IE (3GPP TS 48.008 3.2.2.18).
+ * \param[out] pri Priority structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_priority(struct gsm0808_priority *pri, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(pri);
+ OSMO_ASSERT(elem);
+
+ if (len != sizeof(*pri))
+ return -EINVAL;
+
+ memcpy(pri, elem, sizeof(*pri));
+
+ return len;
+}
+
+/*! Encode VGCS Feature Flags IE (3GPP TS 48.008 3.2.2.88).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] ff VGCS Feature Flags to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_vgcs_feature_flags(struct msgb *msg, const struct gsm0808_vgcs_feature_flags *ff)
+{
+ uint8_t *old_tail;
+ uint8_t *tlv_len;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(ff);
+
+ msgb_put_u8(msg, GSM0808_IE_VGCS_FEATURE_FLAGS);
+ tlv_len = msgb_put(msg, 1);
+ old_tail = msg->tail;
+
+ memcpy(msgb_put(msg, sizeof(*ff)), ff, sizeof(*ff));
+
+ *tlv_len = (uint8_t) (msg->tail - old_tail);
+ return *tlv_len + 2;
+}
+
+/*! Decode VGCS Feature Flags IE (3GPP TS 48.008 3.2.2.88).
+ * \param[out] ff VGCS Feature Flags structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_vgcs_feature_flags(struct gsm0808_vgcs_feature_flags *ff, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(ff);
+ OSMO_ASSERT(elem);
+
+ if (len != sizeof(*ff))
+ return -EINVAL;
+
+ memcpy(ff, elem, sizeof(*ff));
+
+ return len;
+}
+
+/*! Encode Data Identity IE (3GPP TS 48.008 3.2.2.99).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] di Data Identity to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_data_identity(struct msgb *msg, const struct gsm0808_data_identity *ti)
+{
+ uint8_t *old_tail;
+ uint8_t *tlv_len;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(ti);
+
+ msgb_put_u8(msg, GSM0808_IE_DATA_IDENTITY);
+ tlv_len = msgb_put(msg, 1);
+ old_tail = msg->tail;
+
+ memcpy(msgb_put(msg, sizeof(*ti)), ti, sizeof(*ti));
+
+ *tlv_len = (uint8_t) (msg->tail - old_tail);
+ return *tlv_len + 2;
+}
+
+/*! Decode Data Identity IE (3GPP TS 48.008 3.2.2.99).
+ * \param[out] di Data Identity structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_data_identity(struct gsm0808_data_identity *ti, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(ti);
+ OSMO_ASSERT(elem);
+
+ if (len < sizeof(*ti))
+ return -EINVAL;
+
+ memcpy(ti, elem, sizeof(*ti));
+
+ return len;
+}
+
+/*! Encode MSISDN IE (3GPP TS 48.008 3.2.2.101).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] msisdn MSISDN to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_msisdn(struct msgb *msg, const char *msisdn)
+{
+ uint8_t *tlv_len;
+ uint8_t bcd_lv[11];
+ int rc;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(msisdn);
+
+ rc = gsm48_encode_bcd_number(bcd_lv, sizeof(bcd_lv), 0, msisdn);
+ if (rc < 1)
+ return 0;
+
+ msgb_put_u8(msg, GSM0808_IE_MSISDN);
+ tlv_len = msgb_put(msg, rc);
+
+ memcpy(tlv_len, bcd_lv, rc);
+
+ *tlv_len = rc - 1;
+ return *tlv_len + 2;
+}
+
+/*! Decode MSISDN IE (3GPP TS 48.008 3.2.2.101).
+ * \param[out] msisdn MSISDN structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_msisdn(char *msisdn, const char *elem, uint8_t len)
+{
+ OSMO_ASSERT(msisdn);
+ OSMO_ASSERT(elem);
+
+ if (len < 1)
+ return -EINVAL;
+
+ gsm48_decode_bcd_number(msisdn, MSISDN_MAXLEN + 1, (uint8_t *)(elem - 1), 0);
+
+ return len;
+}
+
+/*! Encode Talker Identity IE (3GPP TS 48.008 3.2.2.91).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] ti Talker Identity to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_talker_identity(struct msgb *msg, const struct gsm0808_talker_identity *ti)
+{
+ uint8_t *old_tail;
+ uint8_t *tlv_len;
+ uint8_t *ptr;
+ unsigned int bytes;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(ti);
+
+ msgb_put_u8(msg, GSM0808_IE_TALKER_IDENTITY);
+ tlv_len = msgb_put(msg, 1);
+ old_tail = msg->tail;
+
+ bytes = (ti->id_bits + 7) >> 3;
+ ptr = msgb_put(msg, 1 + bytes);
+ ptr[0] = -ti->id_bits & 0x7;
+ memcpy(ptr + 1, ti->talker_id, bytes);
+
+ *tlv_len = (uint8_t) (msg->tail - old_tail);
+ return *tlv_len + 2;
+}
+
+/*! Decode Talker Identity IE (3GPP TS 48.008 3.2.2.91).
+ * \param[out] ti Talker Identity structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_talker_identity(struct gsm0808_talker_identity *ti, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(ti);
+ OSMO_ASSERT(elem);
+
+ if (len < 2)
+ return -EINVAL;
+ unsigned int bytes;
+
+ bytes = len - 1;
+ if (bytes > TALKER_IDENTITY_MAXLEN)
+ return -EINVAL;
+ ti->id_bits = (bytes << 3) - (elem[0] & 0x7);
+ memcpy(ti->talker_id, elem + 1, bytes);
+
+ return len;
+}
+
+/*! Encode Assignment Requirements IE (3GPP TS 48.008 3.2.2.52).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] ar Assignment Requirement to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_assign_req(struct msgb *msg, const enum gsm0808_assignment_requirement ar)
+{
+ uint8_t *ptr;
+
+ OSMO_ASSERT(msg);
+
+ msgb_put_u8(msg, GSM0808_IE_ASSIGNMENT_REQUIREMENT);
+
+ ptr = msgb_put(msg, 1);
+ ptr[0] = ar;
+
+ return 2;
+}
+
+/*! Decode Assignment Requirements IE (3GPP TS 48.008 3.2.2.52).
+ * \param[out] ar Assignment Requirements enum to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_assign_req(enum gsm0808_assignment_requirement *ar, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(ar);
+ OSMO_ASSERT(elem);
+
+ if (len != 1)
+ return -EINVAL;
+
+ *ar = elem[0];
+
+ return 1;
+}
+
+/*! Encode Cell Identifier List Segment IE (3GPP TS 48.008 3.2.2.27a).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] ie_type Type of IE to use (5 different lists are specified.)
+ * \param[in] ci Cell Identifier List Segment to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_cell_id_list_segment(struct msgb *msg, uint8_t ie_type,
+ const struct gsm0808_cell_id_list_segment *ci)
+{
+ uint8_t *old_tail;
+ uint8_t *tlv_len;
+ int rc;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(ci);
+
+ msgb_put_u8(msg, ie_type);
+ tlv_len = msgb_put(msg, 1);
+ old_tail = msg->tail;
+
+ msgb_put_u8(msg, (ci->seq_last << 4) | ci->seq_number);
+
+ rc = gsm0808_enc_cell_id_list2(msg, &ci->cil);
+ if (rc <= 0)
+ return rc;
+
+ *tlv_len = (uint8_t) (msg->tail - old_tail);
+ return *tlv_len + 2;
+}
+
+/*! Decode Cell Identifier List Segment IE (3GPP TS 48.008 3.2.2.27a).
+ * \param[out] ci Cell Identifier List Segment structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_cell_id_list_segment(struct gsm0808_cell_id_list_segment *ci, const uint8_t *elem, uint8_t len)
+{
+ int rc;
+
+ OSMO_ASSERT(ci);
+ OSMO_ASSERT(elem);
+
+ if (len < 1)
+ return -EINVAL;
+
+ ci->seq_last = elem[0] >> 4;
+ ci->seq_number = elem[0] & 0x0f;
+
+ rc = gsm0808_dec_cell_id_list2(&ci->cil, elem + 1, len - 1);
+ if (rc < 0)
+ return rc;
+
+ return len;
+}
+
+/*! Decode Call Identifier IE (3GPP TS 48.008 3.2.2.105).
+ * \param[out] ci Call Identifier structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_call_id(uint32_t *ci, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(ci);
+ OSMO_ASSERT(elem);
+
+ if (len != 4)
+ return -EINVAL;
+
+ /* The Call Identifier is stored as little endian. */
+ *ci = osmo_load32le(elem);
+
+ return 4;
+}
+
/*! @} */
diff --git a/src/gsm/gsm23003.c b/src/gsm/gsm23003.c
index c2b3de86..1eed41fe 100644
--- a/src/gsm/gsm23003.c
+++ b/src/gsm/gsm23003.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <ctype.h>
@@ -372,6 +368,51 @@ char *osmo_cgi_ps_name_c(const void *ctx, const struct osmo_cell_global_id_ps *c
return osmo_cgi_ps_name_buf(buf, 32, cgi_ps);
}
+/*! Return MCC-MNC-LAC-SAC as string, in caller-provided output buffer.
+ * \param[out] buf caller-allocated output buffer
+ * \param[in] buf_len size of buf in bytes
+ * \param[in] sai SAI to encode.
+ * \returns buf
+ */
+char *osmo_sai_name_buf(char *buf, size_t buf_len, const struct osmo_service_area_id *sai)
+{
+ snprintf(buf, buf_len, "%s-%u", osmo_lai_name(&sai->lai), sai->sac);
+ return buf;
+}
+
+/*! Return MCC-MNC-LAC-SAC as string, in a static buffer.
+ * \param[in] sai SAI to encode.
+ * \returns Static string buffer.
+ */
+const char *osmo_sai_name(const struct osmo_service_area_id *sai)
+{
+ static __thread char buf[32];
+ return osmo_sai_name_buf(buf, sizeof(buf), sai);
+}
+
+/*! Same as osmo_cgi_name(), but uses a different static buffer.
+ * Useful for printing two distinct CGIs in the same printf format.
+ * \param[in] sai SAI to encode.
+ * \returns Static string buffer.
+ */
+const char *osmo_sai_name2(const struct osmo_service_area_id *sai)
+{
+ static __thread char buf[32];
+ return osmo_sai_name_buf(buf, sizeof(buf), sai);
+}
+
+/*! Return MCC-MNC-LAC-SAC as string, in a talloc-allocated output buffer.
+ * \param[in] ctx talloc context from which to allocate output buffer
+ * \param[in] sai SAI to encode.
+ * \returns string representation of CGI in dynamically-allocated buffer.
+ */
+char *osmo_sai_name_c(const void *ctx, const struct osmo_service_area_id *sai)
+{
+ char *buf = talloc_size(ctx, 32);
+ return osmo_sai_name_buf(buf, 32, sai);
+}
+
+
static void to_bcd(uint8_t *bcd, uint16_t val)
{
bcd[2] = val % 10;
@@ -449,14 +490,12 @@ void osmo_plmn_to_bcd(uint8_t *bcd_dst, const struct osmo_plmn_id *plmn)
}
}
-/* Convert given 3-byte BCD buffer to integers and write results to *mcc and
- * *mnc. The first three BCD digits result in the MCC and the remaining ones in
- * the MNC. Return mnc_3_digits as false if the MNC's most significant digit is encoded as 0xF, true
- * otherwise; i.e. true if MNC > 99 or if it is represented with leading zeros instead of 0xF.
+/* Convert given 3-byte BCD buffer to integers and write results to plmn->mcc and plmn->mnc. The first three BCD digits
+ * result in the MCC and the remaining ones in the MNC. Set plmn->mnc_3_digits as false if the MNC's most significant
+ * digit is encoded as 0xF, true otherwise; i.e. true if MNC > 99 or if it is represented with leading zeros instead of
+ * 0xF.
* \param[in] bcd_src 3-byte BCD buffer containing MCC+MNC representations.
- * \param[out] mcc MCC result buffer, or NULL.
- * \param[out] mnc MNC result buffer, or NULL.
- * \param[out] mnc_3_digits Result buffer for 3-digit flag, or NULL.
+ * \param[out] plmn user provided memory to store the result.
*/
void osmo_plmn_from_bcd(const uint8_t *bcd_src, struct osmo_plmn_id *plmn)
{
@@ -487,22 +526,23 @@ void osmo_plmn_from_bcd(const uint8_t *bcd_src, struct osmo_plmn_id *plmn)
*/
int osmo_mnc_from_str(const char *mnc_str, uint16_t *mnc, bool *mnc_3_digits)
{
- long int _mnc = 0;
+ int _mnc = 0;
bool _mnc_3_digits = false;
- char *endptr;
int rc = 0;
if (!mnc_str || !isdigit((unsigned char)mnc_str[0]) || strlen(mnc_str) > 3)
return -EINVAL;
- errno = 0;
- _mnc = strtol(mnc_str, &endptr, 10);
- if (errno)
- rc = -errno;
- else if (*endptr)
+ rc = osmo_str_to_int(&_mnc, mnc_str, 10, 0, 999);
+ /* Heed the API definition to return -EINVAL in case of surplus chars */
+ if (rc == -E2BIG)
return -EINVAL;
- if (_mnc < 0 || _mnc > 999)
- return -ERANGE;
+ /* Heed the API definition to always return negative errno */
+ if (rc > 0)
+ return -rc;
+ if (rc < 0)
+ return rc;
+
_mnc_3_digits = strlen(mnc_str) > 2;
if (mnc)
diff --git a/src/gsm/gsm23236.c b/src/gsm/gsm23236.c
index 01d0eb36..4a83ec82 100644
--- a/src/gsm/gsm23236.c
+++ b/src/gsm/gsm23236.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
@@ -436,27 +432,13 @@ char *osmo_nri_ranges_to_str_c(void *ctx, const struct osmo_nri_ranges *nri_rang
*/
static int osmo_nri_parse(int16_t *dst, const char *str)
{
- char *endp;
- int64_t val;
+ int val;
int base = 10;
-
- if (osmo_str_startswith(str, "0x")) {
- str += 2;
+ if (osmo_str_startswith(str, "0x"))
base = 16;
- }
-
- if (!str || !str[0])
+ if (osmo_str_to_int(&val, str, base, 0, INT16_MAX))
return -1;
-
- errno = 0;
- val = strtoull(str, &endp, base);
- if (errno || *endp != '\0')
- return -1;
-
- if (val < 0 || val > INT16_MAX)
- return -1;
-
- *dst = val;
+ *dst = (int16_t)val;
return 0;
}
diff --git a/src/gsm/gsm29118.c b/src/gsm/gsm29118.c
index 2c02b2fd..0ca5b52e 100644
--- a/src/gsm/gsm29118.c
+++ b/src/gsm/gsm29118.c
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <osmocom/core/utils.h>
diff --git a/src/gsm/gsm29205.c b/src/gsm/gsm29205.c
index 6ceb8ee4..8fed0206 100644
--- a/src/gsm/gsm29205.c
+++ b/src/gsm/gsm29205.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include "config.h"
diff --git a/src/gsm/gsm44021.c b/src/gsm/gsm44021.c
new file mode 100644
index 00000000..5e9d5ab8
--- /dev/null
+++ b/src/gsm/gsm44021.c
@@ -0,0 +1,303 @@
+/*************************************************************************
+ * GSM CSD modified V.110 frame decoding/encoding (ubits <-> struct with D/S/X/E bits)
+ *************************************************************************/
+
+/* (C) 2022 by Harald Welte <laforge@osmocom.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <errno.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/isdn/v110.h>
+
+/*! Decode a 60-bit GSM 12kbit/s CSD frame present as 60 ubits into a struct osmo_v110_decoded_frame.
+ * \param[out] caller-allocated output data structure, filled by this function
+ * \param[in] ra_bits One V.110 frame as 60 unpacked bits. */
+int osmo_csd_12k_6k_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits)
+{
+ /* 3GPP TS 44.021 Section 8.1.2 / 8.1.3
+ D1 D2 D3 D4 D5 D6 S1
+ D7 D8 D9 D10 D11 D12 X
+ D13 D14 D15 D16 D17 D18 S3
+ D19 D20 D21 D22 D23 D24 S4
+ E4 E5 E6 E7 D25 D26 D27
+ D28 D29 D30 S6 D31 D32 D33
+ D34 D35 D36 X D37 D38 D39
+ D40 D41 D42 S8 D43 D44 D45
+ D46 D47 D48 S9 */
+
+ if (n_bits < 60)
+ return -EINVAL;
+
+ /* X1 .. X2 */
+ fr->x_bits[0] = ra_bits[1 * 7 + 6];
+ fr->x_bits[1] = ra_bits[6 * 7 + 3];
+
+ /* S1, S3, S4, S6, S8, S9 */
+ fr->s_bits[0] = ra_bits[0 * 7 + 6];
+ fr->s_bits[2] = ra_bits[2 * 7 + 6];
+ fr->s_bits[3] = ra_bits[3 * 7 + 6];
+ fr->s_bits[5] = ra_bits[5 * 7 + 3];
+ fr->s_bits[7] = ra_bits[7 * 7 + 3];
+ fr->s_bits[8] = ra_bits[8 * 7 + 3];
+
+ /* E1 .. E3 must be set by out-of-band knowledge! */
+
+ /* E4 .. E7 */
+ memcpy(fr->e_bits+3, ra_bits + 4 * 7 + 0, 4);
+
+ /* D-bits */
+ memcpy(fr->d_bits + 0 * 6 + 0, ra_bits + 0 * 7 + 0, 6);
+ memcpy(fr->d_bits + 1 * 6 + 0, ra_bits + 1 * 7 + 0, 6);
+ memcpy(fr->d_bits + 2 * 6 + 0, ra_bits + 2 * 7 + 0, 6);
+ memcpy(fr->d_bits + 3 * 6 + 0, ra_bits + 3 * 7 + 0, 6);
+ memcpy(fr->d_bits + 4 * 6 + 0, ra_bits + 4 * 7 + 4, 3);
+ memcpy(fr->d_bits + 4 * 6 + 3, ra_bits + 5 * 7 + 0, 3);
+ memcpy(fr->d_bits + 5 * 6 + 0, ra_bits + 5 * 7 + 4, 3);
+ memcpy(fr->d_bits + 5 * 6 + 3, ra_bits + 6 * 7 + 0, 3);
+ memcpy(fr->d_bits + 6 * 6 + 0, ra_bits + 6 * 7 + 4, 3);
+ memcpy(fr->d_bits + 6 * 6 + 3, ra_bits + 7 * 7 + 0, 3);
+ memcpy(fr->d_bits + 7 * 6 + 0, ra_bits + 7 * 7 + 4, 3);
+ memcpy(fr->d_bits + 7 * 6 + 3, ra_bits + 8 * 7 + 0, 3);
+
+ return 0;
+}
+
+int osmo_csd_12k_6k_encode_frame(ubit_t *ra_bits, size_t ra_bits_size, const struct osmo_v110_decoded_frame *fr)
+{
+ if (ra_bits_size < 60)
+ return -EINVAL;
+
+ /* X1 .. X2 */
+ ra_bits[1 * 7 + 6] = fr->x_bits[0];
+ ra_bits[6 * 7 + 3] = fr->x_bits[1];
+
+ /* S1, S3, S4, S6, S8, S9 */
+ ra_bits[0 * 7 + 6] = fr->s_bits[0];
+ ra_bits[2 * 7 + 6] = fr->s_bits[2];
+ ra_bits[3 * 7 + 6] = fr->s_bits[3];
+ ra_bits[5 * 7 + 3] = fr->s_bits[5];
+ ra_bits[7 * 7 + 3] = fr->s_bits[7];
+ ra_bits[8 * 7 + 3] = fr->s_bits[8];
+
+ /* E1 .. E3 are dropped */
+
+ /* E4 .. E7 */
+ memcpy(ra_bits + 4 * 7 + 0, fr->e_bits+3, 4);
+
+ /* D-bits */
+ memcpy(ra_bits + 0 * 7 + 0, fr->d_bits + 0 * 6 + 0, 6);
+ memcpy(ra_bits + 1 * 7 + 0, fr->d_bits + 1 * 6 + 0, 6);
+ memcpy(ra_bits + 2 * 7 + 0, fr->d_bits + 2 * 6 + 0, 6);
+ memcpy(ra_bits + 3 * 7 + 0, fr->d_bits + 3 * 6 + 0, 6);
+ memcpy(ra_bits + 4 * 7 + 4, fr->d_bits + 4 * 6 + 0, 3);
+ memcpy(ra_bits + 5 * 7 + 0, fr->d_bits + 4 * 6 + 3, 3);
+ memcpy(ra_bits + 5 * 7 + 4, fr->d_bits + 5 * 6 + 0, 3);
+ memcpy(ra_bits + 6 * 7 + 0, fr->d_bits + 5 * 6 + 3, 3);
+ memcpy(ra_bits + 6 * 7 + 4, fr->d_bits + 6 * 6 + 0, 3);
+ memcpy(ra_bits + 7 * 7 + 0, fr->d_bits + 6 * 6 + 3, 3);
+ memcpy(ra_bits + 7 * 7 + 4, fr->d_bits + 7 * 6 + 0, 3);
+ memcpy(ra_bits + 8 * 7 + 0, fr->d_bits + 7 * 6 + 3, 3);
+
+ return 60;
+}
+
+/*! Decode a 36-bit GSM 3k6kbit/s CSD frame present as 36 ubits into a struct osmo_v110_decoded_frame.
+ * \param[out] caller-allocated output data structure, filled by this function
+ * \param[in] ra_bits One V.110 frame as 36 unpacked bits. */
+int osmo_csd_3k6_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits)
+{
+
+ /* 3GPP TS 44.021 Section 8.1.4
+ D1 D2 D3 S1 D4 D5 D6 X
+ D7 D8 D9 S3 D10 D11 D12 S4
+ E4 E5 E6 E7 D13 D14 D15 S6
+ D16 D17 D18 X D19 D20 D21 S8
+ D22 D23 D24 S9
+ */
+
+ if (n_bits < 36)
+ return -EINVAL;
+
+ /* X1 .. X2 */
+ fr->x_bits[0] = ra_bits[0 * 8 + 7];
+ fr->x_bits[1] = ra_bits[3 * 8 + 3];
+
+ /* S1, S3, S4, S6, S8, S9 */
+ fr->s_bits[0] = ra_bits[0 * 8 + 3];
+ fr->s_bits[2] = ra_bits[1 * 8 + 3];
+ fr->s_bits[3] = ra_bits[1 * 8 + 7];
+ fr->s_bits[5] = ra_bits[2 * 8 + 7];
+ fr->s_bits[7] = ra_bits[3 * 8 + 7];
+ fr->s_bits[8] = ra_bits[4 * 8 + 3];
+
+ /* E1 .. E3 must be set by out-of-band knowledge! */
+
+ /* E4 .. E7 */
+ memcpy(fr->e_bits+3, ra_bits + 2 * 8 + 0, 4);
+
+ /* D-bits */
+ unsigned int d_idx = 0;
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 0]; /* D1 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 0]; /* D1 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 1]; /* D2 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 1]; /* D2 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 2]; /* D3 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 2]; /* D3 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 4]; /* D4 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 4]; /* D4 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 5]; /* D5 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 5]; /* D5 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 6]; /* D6 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 6]; /* D6 */
+
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 0]; /* D7 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 0]; /* D7 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 1]; /* D8 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 1]; /* D8 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 2]; /* D9 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 2]; /* D9 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 4]; /* D10 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 4]; /* D10 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 5]; /* D11 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 5]; /* D11 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 6]; /* D12 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 6]; /* D12 */
+
+ fr->d_bits[d_idx++] = ra_bits[2 * 8 + 4]; /* D13 */
+ fr->d_bits[d_idx++] = ra_bits[2 * 8 + 4]; /* D13 */
+ fr->d_bits[d_idx++] = ra_bits[2 * 8 + 5]; /* D14 */
+ fr->d_bits[d_idx++] = ra_bits[2 * 8 + 5]; /* D14 */
+ fr->d_bits[d_idx++] = ra_bits[2 * 8 + 6]; /* D15 */
+ fr->d_bits[d_idx++] = ra_bits[2 * 8 + 6]; /* D15 */
+
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 0]; /* D16 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 0]; /* D16 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 1]; /* D17 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 1]; /* D17 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 2]; /* D18 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 2]; /* D18 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 4]; /* D19 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 4]; /* D19 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 5]; /* D20 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 5]; /* D20 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 6]; /* D21 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 6]; /* D21 */
+
+ fr->d_bits[d_idx++] = ra_bits[4 * 8 + 0]; /* D22 */
+ fr->d_bits[d_idx++] = ra_bits[4 * 8 + 0]; /* D22 */
+ fr->d_bits[d_idx++] = ra_bits[4 * 8 + 1]; /* D23 */
+ fr->d_bits[d_idx++] = ra_bits[4 * 8 + 1]; /* D23 */
+ fr->d_bits[d_idx++] = ra_bits[4 * 8 + 2]; /* D24 */
+ fr->d_bits[d_idx++] = ra_bits[4 * 8 + 2]; /* D24 */
+
+ OSMO_ASSERT(d_idx == 48);
+
+ return 0;
+}
+
+int osmo_csd_3k6_encode_frame(ubit_t *ra_bits, size_t ra_bits_size, const struct osmo_v110_decoded_frame *fr)
+{
+ if (ra_bits_size < 36)
+ return -EINVAL;
+
+ /* X1 .. X2 */
+ ra_bits[0 * 8 + 7] = fr->x_bits[0];
+ ra_bits[3 * 8 + 3] = fr->x_bits[1];
+
+ /* S1, S3, S4, S6, S8, S9 */
+ ra_bits[0 * 8 + 3] = fr->s_bits[0];
+ ra_bits[1 * 8 + 3] = fr->s_bits[2];
+ ra_bits[1 * 8 + 7] = fr->s_bits[3];
+ ra_bits[2 * 8 + 7] = fr->s_bits[5];
+ ra_bits[3 * 8 + 7] = fr->s_bits[7];
+ ra_bits[4 * 8 + 3] = fr->s_bits[8];
+
+ /* E1 .. E3 are ignored */
+
+ /* E4 .. E7 */
+ memcpy(ra_bits + 2 * 8 + 0, fr->e_bits+3, 4);
+
+ /* D-bits */
+ unsigned int d_idx = 0;
+ ra_bits[0 * 8 + 0] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[0 * 8 + 1] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[0 * 8 + 2] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[0 * 8 + 4] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[0 * 8 + 5] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[0 * 8 + 6] = fr->d_bits[d_idx]; d_idx += 2;
+
+ ra_bits[1 * 8 + 0] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[1 * 8 + 1] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[1 * 8 + 2] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[1 * 8 + 4] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[1 * 8 + 5] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[1 * 8 + 6] = fr->d_bits[d_idx]; d_idx += 2;
+
+ ra_bits[2 * 8 + 4] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[2 * 8 + 5] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[2 * 8 + 6] = fr->d_bits[d_idx]; d_idx += 2;
+
+ ra_bits[3 * 8 + 0] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[3 * 8 + 1] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[3 * 8 + 2] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[3 * 8 + 4] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[3 * 8 + 5] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[3 * 8 + 6] = fr->d_bits[d_idx]; d_idx += 2;
+
+ ra_bits[4 * 8 + 0] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[4 * 8 + 1] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[4 * 8 + 2] = fr->d_bits[d_idx]; d_idx += 2;
+
+ OSMO_ASSERT(d_idx == 48);
+
+ return 36;
+}
+
+/*! Print a encoded "CSD modififed V.110" frame in the same table-like structure as the spec.
+ * \param outf output FILE stream to which to dump
+ * \param[in] fr unpacked bits to dump
+ * \param[in] in_len length of unpacked bits available at fr. */
+void osmo_csd_ubit_dump(FILE *outf, const ubit_t *fr, size_t in_len)
+{
+ switch (in_len) {
+ case 60:
+ for (unsigned int septet = 0; septet < 9; septet++) {
+ if (septet < 8) {
+ fprintf(outf, "%d\t%d\t%d\t%d\t%d\t%d\t%d\n", fr[septet * 7 + 0],
+ fr[septet * 7 + 1], fr[septet * 7 + 2], fr[septet * 7 + 3],
+ fr[septet * 7 + 4], fr[septet * 7 + 5], fr[septet*7 + 6]);
+ } else {
+ fprintf(outf, "%d\t%d\t%d\t%d\n", fr[septet * 7 + 0],
+ fr[septet * 7 + 1], fr[septet * 7 + 2], fr[septet * 7 + 3]);
+ }
+ }
+ break;
+ case 36:
+ for (unsigned int octet = 0; octet < 5; octet++) {
+ if (octet < 4) {
+ fprintf(outf, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ fr[octet * 8 + 0], fr[octet * 8 + 1], fr[octet * 8 + 2],
+ fr[octet * 8 + 3], fr[octet * 8 + 4], fr[octet * 8 + 5],
+ fr[octet * 8 + 6], fr[octet * 8 + 7]);
+ } else {
+ fprintf(outf, "%d\t%d\t%d\t%d\n", fr[octet * 8 + 0],
+ fr[octet * 8 + 1], fr[octet * 8 + 2], fr[octet * 8 + 3]);
+ }
+ }
+ break;
+ default:
+ fprintf(outf, "invalid input data length: %zu\n", in_len);
+ }
+}
diff --git a/src/gsm/gsm44068.c b/src/gsm/gsm44068.c
new file mode 100644
index 00000000..2556b128
--- /dev/null
+++ b/src/gsm/gsm44068.c
@@ -0,0 +1,121 @@
+/* Group Call Control (GCC) is an ETSI/3GPP standard protocol used between
+ * MS (Mobile Station) and MSC (Mobile Switchting Center) in 2G/GSM-R network.
+ * It is specified in 3GPP TS 44.068.
+ *
+ * (C) 2023 by Sysmocom s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Andreas Eversberg
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <stddef.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/protocol/gsm_44_068.h>
+
+/***********************************************************************
+ * Protocol Definitions
+ ***********************************************************************/
+
+const struct value_string osmo_gsm44068_msg_type_names[] = {
+ { OSMO_GSM44068_MSGT_IMMEDIATE_SETUP, "IMMEDIATE SETUP" },
+ { OSMO_GSM44068_MSGT_SETUP, "SETUP" },
+ { OSMO_GSM44068_MSGT_CONNECT, "CONNECT" },
+ { OSMO_GSM44068_MSGT_TERMINATION, "TERMINATION" },
+ { OSMO_GSM44068_MSGT_TERMINATION_REQUEST, "TERMINATION REQUEST" },
+ { OSMO_GSM44068_MSGT_TERMINATION_REJECT, "TERMINATION REJECT" },
+ { OSMO_GSM44068_MSGT_STATUS, "STATUS" },
+ { OSMO_GSM44068_MSGT_GET_STATUS, "GET STATUS" },
+ { OSMO_GSM44068_MSGT_SET_PARAMETER, "SET PARAMETER" },
+ { OSMO_GSM44068_MSGT_IMMEDIATE_SETUP_2, "IMMEDIATE SETUP 2" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_gsm44068_priority_level_names[] = {
+ { OSMO_GSM44068_PRIO_LEVEL_4, "priority level 4" },
+ { OSMO_GSM44068_PRIO_LEVEL_3, "priority level 3" },
+ { OSMO_GSM44068_PRIO_LEVEL_2, "priority level 2" },
+ { OSMO_GSM44068_PRIO_LEVEL_1, "priority level 1" },
+ { OSMO_GSM44068_PRIO_LEVEL_0, "priority level 0" },
+ { OSMO_GSM44068_PRIO_LEVEL_B, "priority level B" },
+ { OSMO_GSM44068_PRIO_LEVEL_A, "priority level A" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_gsm44068_cause_names[] = {
+ { OSMO_GSM44068_CAUSE_ILLEGAL_MS, "Illegal MS" },
+ { OSMO_GSM44068_CAUSE_IMEI_NOT_ACCEPTED, "IMEI not accepted" },
+ { OSMO_GSM44068_CAUSE_ILLEGAL_ME, "Illegal ME" },
+ { OSMO_GSM44068_CAUSE_SERVICE_NOT_AUTHORIZED, "Service not authorized" },
+ { OSMO_GSM44068_CAUSE_APP_NOT_SUPPORTED_ON_PROTO, "Application not supported on the protocol" },
+ { OSMO_GSM44068_CAUSE_RR_CONNECTION_ABORTED, "RR connection aborted" },
+ { OSMO_GSM44068_CAUSE_NORMAL_CALL_CLEARING, "Normal call clearing" },
+ { OSMO_GSM44068_CAUSE_NETWORK_FAILURE, "Network failure" },
+ { OSMO_GSM44068_CAUSE_BUSY, "Busy" },
+ { OSMO_GSM44068_CAUSE_CONGESTION, "Congestion" },
+ { OSMO_GSM44068_CAUSE_USER_NOT_ORIGINATOR, "User not originator of call" },
+ { OSMO_GSM44068_CAUSE_NET_WANTS_TO_MAINTAIN_CALL, "Network wants to maintain call" },
+ { OSMO_GSM44068_CAUSE_RESPONSE_TO_GET_STATUS, "Response to GET STATUS" },
+ { OSMO_GSM44068_CAUSE_SERVICE_OPTION_NOT_SUBSCR, "Service option not supported" },
+ { OSMO_GSM44068_CAUSE_REQUESTED_SERVICE_NOT_SUB, "Requested service option not subscribed" },
+ { OSMO_GSM44068_CAUSE_SERVICE_OPTION_OOO, "Service option temporarily out of order" },
+ { OSMO_GSM44068_CAUSE_CALL_CANNOT_BE_IDENTIFIED, "Call cannot be identified" },
+ { OSMO_GSM44068_CAUSE_RETRY_UPON_ENTRY_NEW_CALL, "retry upon entry into a new cell" },
+ { OSMO_GSM44068_CAUSE_INVALID_TRANSACTION_ID, "Invalid transaction identifier value" },
+ { OSMO_GSM44068_CAUSE_SEMANTICALLY_INCORRECT_MSG, "Semantically incorrect message" },
+ { OSMO_GSM44068_CAUSE_INVALID_MANDATORY_INFO, "Invalid mandatory information" },
+ { OSMO_GSM44068_CAUSE_MESSAGE_TYPE_NON_EXISTENT, "Message type non-existent or not implemented" },
+ { OSMO_GSM44068_CAUSE_MESSAGE_TYPE_NOT_COMPAT, "Message type not compatible with the protocol state" },
+ { OSMO_GSM44068_CAUSE_IE_NON_EXISTENT, "Information element non-existent or not implemented" },
+ { OSMO_GSM44068_CAUSE_IE_NOT_COMPAT, "Message type not compatible with the protocol state" },
+ { OSMO_GSM44068_CAUSE_PROTOCOL_ERROR, "Protocol error, unspecified" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_gsm44068_call_state_names[] = {
+ { OSMO_GSM44068_CSTATE_U0, "U0" },
+ { OSMO_GSM44068_CSTATE_U1, "U1" },
+ { OSMO_GSM44068_CSTATE_U2sl_U2, "U2sl/U2" },
+ { OSMO_GSM44068_CSTATE_U3, "U3" },
+ { OSMO_GSM44068_CSTATE_U4, "U4" },
+ { OSMO_GSM44068_CSTATE_U5, "U5" },
+ { OSMO_GSM44068_CSTATE_U0p, "U0.p" },
+ { OSMO_GSM44068_CSTATE_U2wr_U6, "U2wr/U6" },
+ { OSMO_GSM44068_CSTATE_U2r, "U2r" },
+ { OSMO_GSM44068_CSTATE_U2ws, "U2ws" },
+ { OSMO_GSM44068_CSTATE_U2sr, "U2sr" },
+ { OSMO_GSM44068_CSTATE_U2nc, "U2nc" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_gsm44068_talker_priority_names[] = {
+ { OSMO_GSM44068_PRIO_NORMAL, "Normal" },
+ { OSMO_GSM44068_PRIO_PRIVILEGED, "Privileged" },
+ { OSMO_GSM44068_PRIO_EMERGENCY, "Emergency" },
+ { 0, NULL }
+};
+
+const struct tlv_definition osmo_gsm44068_att_tlvdef = {
+ .def = {
+ [OSMO_GSM44068_IEI_MOBILE_IDENTITY] = { TLV_TYPE_TLV },
+ [OSMO_GSM44068_IEI_USER_USER] = { TLV_TYPE_TLV },
+ [OSMO_GSM44068_IEI_CALL_STATE] = { TLV_TYPE_SINGLE_TV },
+ [OSMO_GSM44068_IEI_STATE_ATTRIBUTES] = { TLV_TYPE_SINGLE_TV },
+ [OSMO_GSM44068_IEI_TALKER_PRIORITY] = { TLV_TYPE_SINGLE_TV },
+ [OSMO_GSM44068_IEI_SMS_INDICATIONS] = { TLV_TYPE_SINGLE_TV },
+ },
+};
diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c
index 02489c96..64b17658 100644
--- a/src/gsm/gsm48.c
+++ b/src/gsm/gsm48.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
@@ -46,6 +42,7 @@
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
+#include <osmocom/gsm/protocol/gsm_44_068.h>
/*! \addtogroup gsm0408
* @{
@@ -133,10 +130,10 @@ const struct tlv_definition gsm48_rr_att_tlvdef = {
[GSM48_IE_REALTIME_DIFF] = { TLV_TYPE_TLV },
[GSM48_IE_START_TIME] = { TLV_TYPE_FIXED, 2 },
[GSM48_IE_TIMING_ADVANCE] = { TLV_TYPE_TV },
- [GSM48_IE_GROUP_CIP_SEQ] = { TLV_TYPE_SINGLE_TV },
- [GSM48_IE_CIP_MODE_SET] = { TLV_TYPE_SINGLE_TV },
- [GSM48_IE_GPRS_RESUMPT] = { TLV_TYPE_SINGLE_TV },
- [GSM48_IE_SYNC_IND] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_GROUP_CIP_SEQ_HO] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_CIP_MODE_SET_HO] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_GPRS_RESUMPT_HO] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_SYNC_IND_HO] = { TLV_TYPE_SINGLE_TV },
},
};
@@ -152,7 +149,7 @@ const struct tlv_definition gsm48_mm_att_tlvdef = {
[GSM48_IE_NET_DST] = { TLV_TYPE_TLV },
[GSM48_IE_LOCATION_AREA] = { TLV_TYPE_FIXED, 5 },
- [GSM48_IE_PRIORITY_LEV] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_PRIORITY_LEV_HO] = { TLV_TYPE_SINGLE_TV },
[GSM48_IE_FOLLOW_ON_PROC] = { TLV_TYPE_T },
[GSM48_IE_CTS_PERMISSION] = { TLV_TYPE_T },
},
@@ -423,16 +420,54 @@ const char *gsm48_rr_msg_name(uint8_t msgtype)
return get_value_string(rr_msg_names, msgtype);
}
+/* 3GPP TS 44.018 Table 10.4.2 */
+static const struct value_string rr_msg_type_short_names[] = {
+ { GSM48_MT_RR_SH_SI10, "System Information Type 10" },
+ { GSM48_MT_RR_SH_FACCH, "Notification/FACCH" },
+ { GSM48_MT_RR_SH_UL_FREE, "Uplink Free" },
+ { GSM48_MT_RR_SH_MEAS_REP, "Enhanced Measurement Report (uplink)" },
+ { GSM48_MT_RR_SH_MEAS_INFO, "Measurement Information (downlink)" },
+ { GSM48_MT_RR_SH_VGCS_RECON, "VBS/VGCS Reconfigure" },
+ { GSM48_MT_RR_SH_VGCS_RECON2, "VBS/VGCS Reconfigure2" },
+ { GSM48_MT_RR_SH_VGCS_INFO, "VGCS Additional Information" },
+ { GSM48_MT_RR_SH_VGCS_SMS, "VGCS SMS Information" },
+ { GSM48_MT_RR_SH_SI10bis, "System Information Type 10bis" },
+ { GSM48_MT_RR_SH_SI10ter, "System Information Type 10ter" },
+ { GSM48_MT_RR_SH_VGCS_NEIGH, "VGCS Neighbour Cell Information" },
+ { GSM48_MT_RR_SH_APP_DATA, "Notify Application Data" },
+ { 0, NULL }
+};
+
+/*! return string representation of RR Message Type using the RR short protocol discriminator */
+const char *gsm48_rr_short_pd_msg_name(uint8_t msgtype)
+{
+ return get_value_string(rr_msg_type_short_names, msgtype);
+}
const struct value_string gsm48_chan_mode_names[] = {
{ GSM48_CMODE_SIGN, "SIGNALLING" },
{ GSM48_CMODE_SPEECH_V1, "SPEECH_V1" },
{ GSM48_CMODE_SPEECH_EFR, "SPEECH_EFR" },
{ GSM48_CMODE_SPEECH_AMR, "SPEECH_AMR" },
+ { GSM48_CMODE_SPEECH_V4, "SPEECH_V4" },
+ { GSM48_CMODE_SPEECH_V5, "SPEECH_V5" },
+ { GSM48_CMODE_SPEECH_V6, "SPEECH_V6" },
+
+ { GSM48_CMODE_DATA_43k5_14k5, "DATA_43k5_14k5" },
+ { GSM48_CMODE_DATA_29k0_14k5, "DATA_29k0_14k5" },
+ { GSM48_CMODE_DATA_43k5_29k0, "DATA_43k5_29k0" },
+ { GSM48_CMODE_DATA_14k5_43k5, "DATA_14k5_43k5" },
+ { GSM48_CMODE_DATA_14k5_29k0, "DATA_14k5_29k0" },
+ { GSM48_CMODE_DATA_29k0_43k5, "DATA_29k0_43k5" },
+
+ { GSM48_CMODE_DATA_43k5, "DATA_43k5" },
+ { GSM48_CMODE_DATA_32k0, "DATA_32k0" },
+ { GSM48_CMODE_DATA_29k0, "DATA_29k0" },
{ GSM48_CMODE_DATA_14k5, "DATA_14k5" },
{ GSM48_CMODE_DATA_12k0, "DATA_12k0" },
{ GSM48_CMODE_DATA_6k0, "DATA_6k0" },
{ GSM48_CMODE_DATA_3k6, "DATA_3k6" },
+
{ GSM48_CMODE_SPEECH_V1_VAMOS, "SPEECH_V1_VAMOS" },
{ GSM48_CMODE_SPEECH_V2_VAMOS, "SPEECH_V2_VAMOS" },
{ GSM48_CMODE_SPEECH_V3_VAMOS, "SPEECH_V3_VAMOS" },
@@ -463,22 +498,21 @@ enum gsm48_chan_mode gsm48_chan_mode_to_vamos(enum gsm48_chan_mode mode)
}
/*! Translate GSM48_CMODE_SPEECH_*_VAMOS to its corresponding GSM48_CMODE_SPEECH_* non-vamos mode.
- * If the mode has no equivalent non-VAMOS mode, return a negative value.
+ * If the mode is not a VAMOS mode, return the unchanged mode.
*/
enum gsm48_chan_mode gsm48_chan_mode_to_non_vamos(enum gsm48_chan_mode mode)
{
switch (mode) {
case GSM48_CMODE_SPEECH_V1_VAMOS:
- case GSM48_CMODE_SPEECH_V1:
return GSM48_CMODE_SPEECH_V1;
case GSM48_CMODE_SPEECH_V2_VAMOS:
- case GSM48_CMODE_SPEECH_EFR:
return GSM48_CMODE_SPEECH_EFR;
case GSM48_CMODE_SPEECH_V3_VAMOS:
- case GSM48_CMODE_SPEECH_AMR:
return GSM48_CMODE_SPEECH_AMR;
+ case GSM48_CMODE_SPEECH_V5_VAMOS:
+ return GSM48_CMODE_SPEECH_V5;
default:
- return -1;
+ return mode;
}
}
@@ -604,11 +638,13 @@ int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t *
{
int rc;
int nibbles_len;
- char *str;
- size_t str_size;
+ char *str = NULL; /* initialize to avoid uninitialized false warnings on some gcc versions (11.1.0) */
+ size_t str_size = 0; /* initialize to avoid uninitialized false warnings on some gcc versions (11.1.0) */
- if (!mi_data || mi_len < 1)
- return -EBADMSG;
+ if (!mi_data || mi_len < 1) {
+ rc = -EBADMSG;
+ goto return_error;
+ }
nibbles_len = (mi_len - 1) * 2 + ((mi_data[0] & GSM_MI_ODD) ? 1 : 0);
@@ -680,8 +716,12 @@ int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t *
goto return_error;
}
rc = osmo_bcd2str(str, str_size, mi_data, 1, 1 + nibbles_len, allow_hex);
- /* rc checked below */
- break;
+ /* check mi->str printing rc */
+ if (rc < 1 || rc >= str_size) {
+ rc = -EBADMSG;
+ goto return_error;
+ }
+ return 0;
default:
/* Already handled above, but as future bug paranoia: */
@@ -689,13 +729,6 @@ int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t *
goto return_error;
}
- /* check mi->str printing rc */
- if (rc < 1 || rc >= str_size) {
- rc = -EBADMSG;
- goto return_error;
- }
- return 0;
-
return_error:
*mi = (struct osmo_mobile_identity){
.type = GSM_MI_TYPE_NONE,
@@ -833,11 +866,13 @@ int osmo_mobile_identity_encode_msgb(struct msgb *msg, const struct osmo_mobile_
* osmo_mobile_identity.
*
* \param[out] mi Return buffer for decoded Mobile Identity.
- * \param[in] msg The Complete Layer 3 message to extract from (LU, CM Service Req or Paging Resp).
+ * \param[in] l3_data The Complete Layer 3 message to extract from (LU, CM Service Req or Paging Resp).
+ * \param[in] l3_len Length of l3_data in bytes.
* \returns 0 on success, negative on error: return codes as defined in osmo_mobile_identity_decode(), or
* -ENOTSUP = not a Complete Layer 3 message,
*/
-int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct msgb *msg, bool allow_hex)
+int osmo_mobile_identity_decode_from_l3_buf(struct osmo_mobile_identity *mi, const uint8_t *l3_data, size_t l3_len,
+ bool allow_hex)
{
const struct gsm48_hdr *gh;
int8_t pdisc = 0;
@@ -856,10 +891,10 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct
.tmsi = GSM_RESERVED_TMSI,
};
- if (msgb_l3len(msg) < sizeof(*gh))
+ if (l3_len < sizeof(*gh))
return -EBADMSG;
- gh = msgb_l3(msg);
+ gh = (void *)l3_data;
pdisc = gsm48_hdr_pdisc(gh);
mtype = gsm48_hdr_msg_type(gh);
@@ -869,12 +904,12 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct
switch (mtype) {
case GSM48_MT_MM_LOC_UPD_REQUEST:
/* First make sure that lu-> can be dereferenced */
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu))
+ if (l3_len < sizeof(*gh) + sizeof(*lu))
return -EBADMSG;
/* Now we know there is enough msgb data to read a lu->mi_len, so also check that */
lu = (struct gsm48_loc_upd_req*)gh->data;
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu) + lu->mi_len)
+ if (l3_len < sizeof(*gh) + sizeof(*lu) + lu->mi_len)
return -EBADMSG;
mi_data = lu->mi;
mi_len = lu->mi_len;
@@ -884,7 +919,7 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct
case GSM48_MT_MM_CM_REEST_REQ:
/* Unfortunately in Phase1 the Classmark2 length is variable, so we cannot
* just use gsm48_service_request struct, and need to parse it manually. */
- if (msgb_l3len(msg) < sizeof(*gh) + 2)
+ if (l3_len < sizeof(*gh) + 2)
return -EBADMSG;
cm2_len = gh->data[1];
@@ -892,7 +927,7 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct
goto got_cm2;
case GSM48_MT_MM_IMSI_DETACH_IND:
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*idi))
+ if (l3_len < sizeof(*gh) + sizeof(*idi))
return -EBADMSG;
idi = (struct gsm48_imsi_detach_ind*) gh->data;
mi_data = idi->mi;
@@ -900,7 +935,7 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct
goto got_mi;
case GSM48_MT_MM_ID_RESP:
- if (msgb_l3len(msg) < sizeof(*gh) + 2)
+ if (l3_len < sizeof(*gh) + 2)
return -EBADMSG;
mi_data = gh->data+1;
mi_len = gh->data[0];
@@ -915,13 +950,24 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct
switch (mtype) {
case GSM48_MT_RR_PAG_RESP:
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*paging_response))
+ if (l3_len < sizeof(*gh) + sizeof(*paging_response))
return -EBADMSG;
paging_response = (struct gsm48_pag_resp*)gh->data;
cm2_len = paging_response->cm2_len;
cm2_buf = (uint8_t*)&paging_response->cm2;
goto got_cm2;
+ case GSM48_MT_RR_TALKER_IND:
+ /* Check minimum size: Header + CM2 LV + minimum MI LV */
+ if (l3_len < sizeof(*gh) + 4 + 2)
+ return -EBADMSG;
+ /* CM2 shall be always 3 bytes in length */
+ if (gh->data[0] != 3)
+ return -EBADMSG;
+ cm2_len = gh->data[0];
+ cm2_buf = gh->data + 1;
+ goto got_cm2;
+
default:
break;
}
@@ -934,7 +980,7 @@ got_cm2:
/* MI (Mobile Identity) LV follows the Classmark2 */
/* There must be at least a mi_len byte after the CM2 */
- if (cm2_buf + cm2_len + 1 > msg->tail)
+ if (cm2_buf + cm2_len + 1 > l3_data + l3_len)
return -EBADMSG;
mi_start = cm2_buf + cm2_len;
@@ -943,12 +989,27 @@ got_cm2:
got_mi:
/* mi_data points at the start of the Mobile Identity coding of mi_len bytes */
- if (mi_data + mi_len > msg->tail)
+ if (mi_data + mi_len > l3_data + l3_len)
return -EBADMSG;
return osmo_mobile_identity_decode(mi, mi_data, mi_len, allow_hex);
}
+/*! Extract Mobile Identity from a Complete Layer 3 message.
+ *
+ * Determine the Mobile Identity data and call osmo_mobile_identity_decode() to return a decoded struct
+ * osmo_mobile_identity.
+ *
+ * \param[out] mi Return buffer for decoded Mobile Identity.
+ * \param[in] msg The Complete Layer 3 message to extract from (LU, CM Service Req or Paging Resp).
+ * \returns 0 on success, negative on error: return codes as defined in osmo_mobile_identity_decode(), or
+ * -ENOTSUP = not a Complete Layer 3 message,
+ */
+int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct msgb *msg, bool allow_hex)
+{
+ return osmo_mobile_identity_decode_from_l3_buf(mi, msgb_l3(msg), msgb_l3len(msg), allow_hex);
+}
+
/*! Return a human readable representation of a struct osmo_mobile_identity.
* Write a string like "IMSI-1234567", "TMSI-0x1234ABCD" or "NONE", "NULL".
* \param[out] buf String buffer to write to.
@@ -1287,7 +1348,64 @@ int gsm48_mi_to_string(char *string, int str_len, const uint8_t *mi, int mi_len)
return 1;
}
-/*! Parse TS 04.08 Routing Area Identifier
+/*! Decode to struct osmo_routing_area_id from a 3GPP TS 24.008 § 10.5.5.15 Routing area identification.
+ * \param[out] dst Store the decoded result here.
+ * \param[in] ra_data The start of a Routing Area ID in encoded form, to be decoded.
+ * \param[in] ra_data_len Buffer size available to read from at *ra_data.
+ * \return the number of decoded bytes on success, or negative on error (if the input buffer is too small).
+ */
+int osmo_routing_area_id_decode(struct osmo_routing_area_id *dst, const uint8_t *ra_data, size_t ra_data_len)
+{
+ const struct gsm48_ra_id *ra_id;
+ if (ra_data_len < sizeof(*ra_id))
+ return -ENOSPC;
+
+ gsm48_decode_lai2((void *)ra_data, &dst->lac);
+
+ ra_id = (void *)ra_data;
+ dst->rac = ra_id->rac;
+
+ return sizeof(*ra_id);
+}
+
+/*! Encode struct osmo_routing_area_id to a 3GPP TS 24.008 § 10.5.5.15 Routing area identification: write to a buffer.
+ * \param[out] buf Return buffer for encoded Mobile Identity.
+ * \param[in] buflen sizeof(buf).
+ * \param[in] src RA to encode.
+ * \return Amount of bytes written to buf, or negative on error.
+ */
+int osmo_routing_area_id_encode_buf(uint8_t *buf, size_t buflen, const struct osmo_routing_area_id *src)
+{
+ struct gsm48_ra_id *ra_id;
+ if (buflen < sizeof(*ra_id))
+ return -ENOSPC;
+
+ gsm48_generate_lai2((void *)buf, &src->lac);
+
+ ra_id = (void *)buf;
+ ra_id->rac = src->rac;
+
+ return sizeof(*ra_id);
+}
+
+/*! Encode struct osmo_routing_area_id to a 3GPP TS 24.008 § 10.5.5.15 Routing area identification: append to msgb.
+ * To succeed, the msgb must have tailroom >= sizeof(struct gsm48_ra_id).
+ * \param[out] msg Append to this msgb.
+ * \param[in] src Encode this Routing Area ID.
+ * \return Number of bytes appended to msgb, or negative on error.
+ */
+int osmo_routing_area_id_encode_msgb(struct msgb *msg, const struct osmo_routing_area_id *src)
+{
+ int rc = osmo_routing_area_id_encode_buf(msg->tail, msgb_tailroom(msg), src);
+ if (rc <= 0)
+ return rc;
+ msgb_put(msg, rc);
+ return rc;
+}
+
+/*! Parse TS 04.08 Routing Area Identifier.
+ * Preferably use osmo_routing_area_id_decode() instead: struct osmo_routing_area_id is better integrated with other API
+ * like osmo_plmn_cmp().
* \param[out] Caller-provided memory for decoded RA ID
* \param[in] buf Input buffer pointing to RAI IE value */
void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf)
@@ -1371,7 +1489,7 @@ bool gsm48_ra_equal(const struct gprs_ra_id *raid1, const struct gprs_ra_id *rai
* Uses From Table 10.5.33 of GSM 04.08 to determine the number of
* paging sub-channels in the given control channel configuration
*/
-int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc)
+int gsm48_number_of_paging_subchannels(const struct gsm48_control_channel_descr *chan_desc)
{
unsigned int n_pag_blocks = gsm0502_get_n_pag_blocks(chan_desc);
@@ -1651,6 +1769,10 @@ char *gsm48_pdisc_msgtype_name_buf(char *buf, size_t buf_len, uint8_t pdisc, uin
case GSM48_PDISC_CC:
msgt_names = gsm48_cc_msgtype_names;
break;
+ case GSM48_PDISC_GROUP_CC:
+ case GSM48_PDISC_BCAST_CC:
+ msgt_names = osmo_gsm44068_msg_type_names;
+ break;
case GSM48_PDISC_NC_SS:
msgt_names = gsm48_nc_ss_msgtype_names;
break;
diff --git a/src/gsm/gsm48_arfcn_range_encode.c b/src/gsm/gsm48_arfcn_range_encode.c
index 6423a9a8..afe552d8 100644
--- a/src/gsm/gsm48_arfcn_range_encode.c
+++ b/src/gsm/gsm48_arfcn_range_encode.c
@@ -166,6 +166,10 @@ int osmo_gsm48_range_enc_determine_range(const int *arfcns, const int size, int
{
int max = 0;
+ /* don't dereference arfcns[] array if size is 0 */
+ if (size == 0)
+ return OSMO_GSM48_ARFCN_RANGE_128;
+
/*
* Go for the easiest. And pick arfcns[0] == f0.
*/
diff --git a/src/gsm/gsm48_ie.c b/src/gsm/gsm48_ie.c
index 6070e5cc..b95609fe 100644
--- a/src/gsm/gsm48_ie.c
+++ b/src/gsm/gsm48_ie.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -48,10 +44,11 @@ static const char bcd_num_digits[] = {
};
/*! Like gsm48_decode_bcd_number2() but with less airtight bounds checking.
- * \param[out] Caller-provided output buffer
+ * \param[out] output Caller-provided output buffer
+ * \param[in] output_len sizeof(output)
* \param[in] bcd_lv Length-Value portion of to-be-decoded IE
* \param[in] h_len Length of an optional heder between L and V portion
- * \returns - in case of success; negative on error */
+ * \returns 0 in case of success; negative on error */
int gsm48_decode_bcd_number(char *output, int output_len,
const uint8_t *bcd_lv, int h_len)
{
@@ -143,7 +140,7 @@ static int asc_to_bcd(const char asc)
* \param[in] max_len Maximum Length of \a bcd_lv
* \param[in] h_len Length of an optional heder between L and V portion
* \param[in] input phone number as 0-terminated ASCII
- * \returns number of bytes used in \a bcd_lv
+ * \returns number of bytes used in \a bcd_lv; negative on error
*
* Depending on a context (e.g. called or calling party BCD number), the
* optional header between L and V parts can contain TON (Type Of Number),
@@ -183,8 +180,8 @@ int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len,
}
/*! Decode TS 04.08 Bearer Capability IE (10.5.4.5)
- * \param[out] Caller-provided memory for decoded output
- * \[aram[in] LV portion of TS 04.08 Bearer Capability
+ * \param[out] bcap Caller-provided memory for decoded output
+ * \param[in] lv LV portion of TS 04.08 Bearer Capability
* \returns 0 on success; negative on error */
int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
const uint8_t *lv)
@@ -207,7 +204,24 @@ int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
case GSM_MNCC_BCAP_SPEECH:
i = 1;
s = 0;
- while(!(lv[i] & 0x80)) {
+ if ((lv[1] & 0x80) != 0) { /* octet 3a is absent */
+ switch (bcap->radio) {
+ case GSM48_BCAP_RRQ_FR_ONLY:
+ bcap->speech_ver[s++] = GSM48_BCAP_SV_FR;
+ break;
+ case GSM48_BCAP_RRQ_DUAL_HR:
+ bcap->speech_ver[s++] = GSM48_BCAP_SV_HR;
+ bcap->speech_ver[s++] = GSM48_BCAP_SV_FR;
+ break;
+ case GSM48_BCAP_RRQ_DUAL_FR:
+ bcap->speech_ver[s++] = GSM48_BCAP_SV_FR;
+ bcap->speech_ver[s++] = GSM48_BCAP_SV_HR;
+ break;
+ }
+ bcap->speech_ver[s] = -1; /* end of list */
+ return 0;
+ }
+ while (!(lv[i] & 0x80)) {
i++; /* octet 3a etc */
if (in_len < i)
return 0;
@@ -220,9 +234,10 @@ int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
}
break;
case GSM_MNCC_BCAP_UNR_DIG:
+ case GSM_MNCC_BCAP_AUDIO:
case GSM_MNCC_BCAP_FAX_G3:
i = 1;
- while(!(lv[i] & 0x80)) {
+ while (!(lv[i] & 0x80)) {
i++; /* octet 3a etc */
if (in_len < i)
return 0;
@@ -236,7 +251,7 @@ int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
return 0;
bcap->data.rate_adaption = (lv[i] >> 3) & 3;
bcap->data.sig_access = lv[i] & 7;
- while(!(lv[i] & 0x80)) {
+ while (!(lv[i] & 0x80)) {
i++; /* octet 5a etc */
if (in_len < i)
return 0;
@@ -321,10 +336,11 @@ int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
lv[i] |= 0x80; /* last IE of octet 3 etc */
break;
case GSM48_BCAP_ITCAP_UNR_DIG_INF:
+ case GSM48_BCAP_ITCAP_3k1_AUDIO:
case GSM48_BCAP_ITCAP_FAX_G3:
lv[i++] |= 0x80; /* last IE of octet 3 etc */
/* octet 4 */
- lv[i++] = 0xb8;
+ lv[i++] = 0x88;
/* octet 5 */
lv[i++] = 0x80 | ((bcap->data.rate_adaption & 3) << 3)
| (bcap->data.sig_access & 7);
@@ -338,7 +354,9 @@ int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
lv[i++] = (bcap->data.parity & 7) |
((bcap->data.interm_rate & 3) << 5);
/* octet 6c */
- lv[i] = 0x80 | (bcap->data.modem_type & 0x1f);
+ lv[i] = 0x80 |
+ ((bcap->data.transp & 3) << 5) |
+ (bcap->data.modem_type & 0x1f);
break;
default:
return -EINVAL;
@@ -354,9 +372,9 @@ int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
}
/*! Decode TS 04.08 Call Control Capabilities IE (10.5.4.5a)
- * \param[out] Caller-provided memory for decoded CC capabilities
+ * \param[out] ccap Caller-provided memory for decoded CC capabilities
* \param[in] lv Length-Value of IE
- * \retursns 0 on success; negative on error */
+ * \returns 0 on success; negative on error */
int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv)
{
uint8_t in_len = lv[0];
@@ -440,7 +458,7 @@ int gsm48_encode_called(struct msgb *msg,
}
/*! Decode TS 04.08 Caller ID
- * \param[out] called Caller-provided memory for decoded number
+ * \param[out] callerid Caller-provided memory for decoded number
* \param[in] lv Length-Value portion of IE
* \returns 0 on success; negative on error */
int gsm48_decode_callerid(struct gsm_mncc_number *callerid,
@@ -812,7 +830,7 @@ int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv,
{
uint8_t in_len = lv[0];
- if (in_len < 1 || in_len < sizeof(ssv->info))
+ if (in_len < 1 || in_len > sizeof(ssv->info))
return -EINVAL;
memcpy(ssv->info, lv + 1, in_len);
@@ -867,8 +885,9 @@ static int32_t smod(int32_t n, int32_t m)
* \param[in] cd Cell Channel Description IE
* \param[in] len Length of \a cd in bytes
* \returns 0 on success; negative on error */
-int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
- uint8_t len, uint8_t mask, uint8_t frqt)
+int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f,
+ const uint8_t *cd, uint8_t len,
+ uint8_t mask, uint8_t frqt)
{
int i;
diff --git a/src/gsm/gsm48_rest_octets.c b/src/gsm/gsm48_rest_octets.c
index 77fd349b..57c2c99a 100644
--- a/src/gsm/gsm48_rest_octets.c
+++ b/src/gsm/gsm48_rest_octets.c
@@ -59,6 +59,82 @@ int osmo_gsm48_rest_octets_si1_encode(uint8_t *data, uint8_t *nch_pos, int is180
return bv.data_len;
}
+struct nch_pos {
+ uint8_t num_blocks;
+ uint8_t first_block;
+};
+
+/* 3GPP TS 44.010 Table 10.5.2.32.1b */
+static const struct nch_pos si1ro_nch_positions[] = {
+ [0x00] = {1, 0},
+ [0x01] = {1, 1},
+ [0x02] = {1, 2},
+ [0x03] = {1, 3},
+ [0x04] = {1, 4},
+ [0x05] = {1, 5},
+ [0x06] = {1, 6},
+
+ [0x07] = {2, 0},
+ [0x08] = {2, 1},
+ [0x09] = {2, 2},
+ [0x0a] = {2, 3},
+ [0x0b] = {2, 4},
+ [0x0c] = {2, 5},
+
+ [0x0d] = {3, 0},
+ [0x0e] = {3, 1},
+ [0x0f] = {3, 2},
+ [0x10] = {3, 3},
+ [0x11] = {3, 4},
+
+ [0x12] = {4, 0},
+ [0x13] = {4, 1},
+ [0x14] = {4, 2},
+ [0x15] = {4, 3},
+
+ [0x16] = {5, 0},
+ [0x17] = {5, 1},
+ [0x18] = {5, 2},
+
+ [0x19] = {6, 0},
+ [0x1a] = {6, 1},
+
+ [0x1b] = {7, 0},
+};
+
+/*! Decode the 5-bit 'NCH position' field within SI1 Rest Octets.
+ * \param[in] value 5-bit value from SI1 rest octets
+ * \param[out] num_blocks Number of CCCH used for NCH
+ * \param[out] first_block First CCCH block used for NCH
+ * \returns 0 on success; negative in case of error */
+int osmo_gsm48_si1ro_nch_pos_decode(uint8_t value, uint8_t *num_blocks, uint8_t *first_block)
+{
+ if (value >= ARRAY_SIZE(si1ro_nch_positions))
+ return -EINVAL;
+
+ *num_blocks = si1ro_nch_positions[value].num_blocks;
+ *first_block = si1ro_nch_positions[value].first_block;
+
+ return 0;
+}
+
+/*! Encode the 5-bit 'NCH position' field within SI1 Rest Octets.
+ * \param[in] num_blocks Number of CCCH used for NCH
+ * \param[in] first_block First CCCH block used for NCH
+ * \returns 5-bit value for SI1 rest octets on success; negative in case of error */
+int osmo_gsm48_si1ro_nch_pos_encode(uint8_t num_blocks, uint8_t first_block)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(si1ro_nch_positions); i++) {
+ if (si1ro_nch_positions[i].num_blocks == num_blocks &&
+ si1ro_nch_positions[i].first_block == first_block) {
+ return i;
+ }
+ }
+ return -EINVAL;
+}
+
/* Append Repeated E-UTRAN Neighbour Cell to bitvec: see 3GPP TS 44.018 Table 10.5.2.33b.1 */
static inline bool append_eutran_neib_cell(struct bitvec *bv, const struct osmo_earfcn_si2q *e, size_t *e_offset,
uint8_t budget)
diff --git a/src/gsm/gsm_utils.c b/src/gsm/gsm_utils.c
index 07e082d3..bb403392 100644
--- a/src/gsm/gsm_utils.c
+++ b/src/gsm/gsm_utils.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \mainpage libosmogsm Documentation
@@ -84,6 +80,7 @@
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/meas_rep.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm0502.h>
#include <stdlib.h>
#include <stdint.h>
@@ -96,7 +93,7 @@
#include <time.h>
#include <unistd.h>
-#include "../../config.h"
+#include "config.h"
#if (!EMBEDDED)
/* FIXME: this can be removed once we bump glibc requirements to 2.25: */
@@ -329,12 +326,13 @@ int gsm_septet_pack(uint8_t *result, const uint8_t *rdata, size_t septet_len, ui
int i = 0, z = 0;
uint8_t cb, nb;
int shift = 0;
- uint8_t *data = calloc(septet_len + 1, sizeof(uint8_t));
+ uint8_t *data = malloc(septet_len + 1);
if (padding) {
shift = 7 - padding;
/* the first zero is needed for padding */
memcpy(data + 1, rdata, septet_len);
+ data[0] = 0x00;
septet_len++;
} else
memcpy(data, rdata, septet_len);
@@ -388,7 +386,7 @@ int gsm_7bit_encode_n(uint8_t *result, size_t n, const char *data, int *octets)
size_t max_septets = n * 8 / 7;
/* prepare for the worst case, every character expanding to two bytes */
- uint8_t *rdata = calloc(strlen(data) * 2, sizeof(uint8_t));
+ uint8_t *rdata = malloc(strlen(data) * 2);
y = gsm_septet_encode(rdata, data);
if (y > max_septets) {
@@ -493,7 +491,7 @@ int osmo_get_rand_id(uint8_t *out, size_t len)
* \param[out] buf Pre-allocated bufer for storing IE
* \returns Number of bytes filled in buf
*/
-size_t gsm0858_rsl_ul_meas_enc(struct gsm_meas_rep_unidir *mru, bool dtxd_used,
+size_t gsm0858_rsl_ul_meas_enc(const struct gsm_meas_rep_unidir *mru, bool dtxd_used,
uint8_t *buf)
{
buf[0] = dtxd_used ? (1 << 6) : 0;
@@ -888,10 +886,21 @@ char *gsm_fn_as_gsmtime_str(uint32_t fn)
/*! Encode decoded \ref gsm_time to Frame Number
* \param[in] time GSM Time in decoded structure
* \returns GSM Frame Number */
-uint32_t gsm_gsmtime2fn(struct gsm_time *time)
+uint32_t gsm_gsmtime2fn(const struct gsm_time *time)
{
- /* TS 05.02 Chapter 4.3.3 TDMA frame number */
- return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1));
+ uint32_t fn;
+
+ /* See also:
+ * 3GPP TS 44.018, section 10.5.2.38, 3GPP TS 45.002 section 4.3.3, and
+ * 3GPP TS 48.058, section 9.3.8 */
+ fn = 51 * OSMO_MOD_FLR((time->t3-time->t2), 26) + time->t3 + 51 * 26 * time->t1;
+
+ /* Note: Corrupted input values may cause a resulting frame number
+ * larger then the maximum permitted value of GSM_MAX_FN. Even though
+ * the caller is expected to check the input values beforehand we must
+ * make sure that the result cannot exceed the value range of a valid
+ * GSM frame number. */
+ return fn % GSM_MAX_FN;
}
char *osmo_dump_gsmtime_buf(char *buf, size_t buf_len, const struct gsm_time *tm)
@@ -916,6 +925,45 @@ char *osmo_dump_gsmtime_c(const void *ctx, const struct gsm_time *tm)
return osmo_dump_gsmtime_buf(buf, 64, tm);
}
+#define GSM_RFN_THRESHOLD (GSM_RFN_MODULUS / 2)
+uint32_t gsm_rfn2fn(uint16_t rfn, uint32_t curr_fn)
+{
+ uint32_t curr_rfn;
+ uint32_t fn_rounded;
+ const uint32_t rfn32 = rfn; /* used as 32bit for calculations */
+
+ /* Ensure that all following calculations are performed with the
+ * relative frame number */
+ OSMO_ASSERT(rfn32 < GSM_RFN_MODULUS);
+
+ /* Compute an internal relative frame number from the full internal
+ frame number */
+ curr_rfn = gsm_fn2rfn(curr_fn);
+
+ /* Compute a "rounded" version of the internal frame number, which
+ * exactly fits in the RFN_MODULUS raster */
+ fn_rounded = GSM_TDMA_FN_SUB(curr_fn, curr_rfn);
+
+ /* If the delta between the internal and the external relative frame
+ * number exceeds a certain limit, we need to assume that the incoming
+ * request belongs to a the previous rfn period. To correct this,
+ * we roll back the rounded frame number by one RFN_MODULUS */
+ if (GSM_TDMA_FN_DIFF(rfn32, curr_rfn) > GSM_RFN_THRESHOLD) {
+ /* Race condition between rfn and curr_fn detected: rfn belongs
+ to the previous RFN_MODULUS cycle, wrapping... */
+ if (fn_rounded < GSM_RFN_MODULUS) {
+ /* Corner case detected: wrapping crosses GSM_MAX_FN border */
+ fn_rounded = GSM_TDMA_FN_SUB(GSM_MAX_FN, (GSM_TDMA_FN_SUB(GSM_RFN_MODULUS, fn_rounded)));
+ } else {
+ fn_rounded = GSM_TDMA_FN_SUB(fn_rounded, GSM_RFN_MODULUS);
+ }
+ }
+
+ /* The real frame number is the sum of the rounded frame number and the
+ * relative framenumber computed via RACH */
+ return GSM_TDMA_FN_SUM(fn_rounded, rfn32);
+}
+
/*! append range1024 encoded data to bit vector
* \param[out] bv Caller-provided output bit-vector
* \param[in] r Input Range1024 sructure */
diff --git a/src/gsm/gsup.c b/src/gsm/gsup.c
index ef33ed08..4f0a1b5f 100644
--- a/src/gsm/gsup.c
+++ b/src/gsm/gsup.c
@@ -103,6 +103,10 @@ const struct value_string osmo_gsup_message_type_names[] = {
OSMO_VALUE_STRING(OSMO_GSUP_MSGT_ROUTING_ERROR),
+ OSMO_VALUE_STRING(OSMO_GSUP_MSGT_EPDG_TUNNEL_REQUEST),
+ OSMO_VALUE_STRING(OSMO_GSUP_MSGT_EPDG_TUNNEL_RESULT),
+ OSMO_VALUE_STRING(OSMO_GSUP_MSGT_EPDG_TUNNEL_ERROR),
+
{ 0, NULL }
};
@@ -122,6 +126,62 @@ int osmo_gsup_get_err_msg_type(enum osmo_gsup_message_type type_in)
return OSMO_GSUP_TO_MSGT_ERROR(type_in);
}
+static int decode_pdp_address(const uint8_t *data, size_t data_len, struct osmo_gsup_pdp_info *pdp_info)
+{
+ const struct gsm48_pdp_address *pdp_addr = (const struct gsm48_pdp_address *)data;
+ /* Explicitly pre-nitialize them to AF_UNSPEC to signal they are empty: */
+ pdp_info->pdp_address[0].u.sa.sa_family = AF_UNSPEC;
+ pdp_info->pdp_address[1].u.sa.sa_family = AF_UNSPEC;
+
+ if (data_len < 2)
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+
+ pdp_info->pdp_type_org = pdp_addr->organization;
+ pdp_info->pdp_type_nr = pdp_addr->type;
+
+ if (pdp_info->pdp_type_org != PDP_TYPE_ORG_IETF)
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+
+ /* Skip type-org + type-nr for easy calculations below: */
+ data_len -= 2;
+
+ switch (pdp_info->pdp_type_nr) {
+ case PDP_TYPE_N_IETF_IPv4:
+ if (data_len == 0)
+ return 0; /* empty, marked as AF_UNSET. */
+ if (data_len != sizeof(pdp_addr->ietf.v4))
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+ pdp_info->pdp_address[0].u.sa.sa_family = AF_INET;
+ pdp_info->pdp_address[0].u.sin.sin_addr.s_addr = pdp_addr->ietf.v4;
+ return 0;
+ case PDP_TYPE_N_IETF_IPv6:
+ if (data_len == 0)
+ return 0; /* empty, marked as AF_UNSET. */
+ if (data_len != sizeof(pdp_addr->ietf.v6))
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+ pdp_info->pdp_address[0].u.sa.sa_family = AF_INET6;
+ memcpy(&pdp_info->pdp_address[0].u.sin6.sin6_addr,
+ pdp_addr->ietf.v6,
+ sizeof(pdp_addr->ietf.v6));
+ return 0;
+ case PDP_TYPE_N_IETF_IPv4v6:
+ if (data_len == 0)
+ return 0; /* empty, marked as AF_UNSET. */
+ if (data_len != sizeof(pdp_addr->ietf.v4v6))
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+ pdp_info->pdp_address[0].u.sa.sa_family = AF_INET;
+ pdp_info->pdp_address[0].u.sin.sin_addr.s_addr = pdp_addr->ietf.v4v6.v4;
+ pdp_info->pdp_address[1].u.sa.sa_family = AF_INET6;
+ memcpy(&pdp_info->pdp_address[1].u.sin6.sin6_addr,
+ pdp_addr->ietf.v4v6.v6,
+ sizeof(pdp_addr->ietf.v4v6.v6));
+ return 0;
+ default:
+ /* reserved, both pdp_info->pdp_address are preinitialied to AF_UNSET. */
+ return 0;
+ }
+}
+
static int decode_pdp_info(uint8_t *data, size_t data_len,
struct osmo_gsup_pdp_info *pdp_info)
{
@@ -145,9 +205,9 @@ static int decode_pdp_info(uint8_t *data, size_t data_len,
pdp_info->context_id = osmo_decode_big_endian(value, value_len);
break;
- case OSMO_GSUP_PDP_TYPE_IE:
- pdp_info->pdp_type =
- osmo_decode_big_endian(value, value_len) & 0x0fff;
+ case OSMO_GSUP_PDP_ADDRESS_IE:
+ if ((rc = decode_pdp_address(value, value_len, pdp_info)) < 0)
+ return rc;
break;
case OSMO_GSUP_ACCESS_POINT_NAME_IE:
@@ -262,7 +322,7 @@ static int decode_auth_info(uint8_t *data, size_t data_len,
parse_error:
LOGP(DLGSUP, LOGL_ERROR,
- "GSUP IE type %d, length %zu invalid in PDP info\n", iei, value_len);
+ "GSUP IE type %d, length %zu invalid in auth info\n", iei, value_len);
return -1;
}
@@ -353,7 +413,7 @@ int osmo_gsup_decode(const uint8_t *const_data, size_t data_len,
switch (iei) {
case OSMO_GSUP_IMSI_IE:
- case OSMO_GSUP_PDP_TYPE_IE:
+ case OSMO_GSUP_PDP_ADDRESS_IE:
case OSMO_GSUP_ACCESS_POINT_NAME_IE:
case OSMO_GSUP_SRES_IE:
case OSMO_GSUP_KC_IE:
@@ -446,6 +506,11 @@ int osmo_gsup_decode(const uint8_t *const_data, size_t data_len,
gsup_msg->rand = value;
break;
+ case OSMO_GSUP_PCO_IE:
+ gsup_msg->pco = value;
+ gsup_msg->pco_len = value_len;
+ break;
+
case OSMO_GSUP_MSISDN_IE:
gsup_msg->msisdn_enc = value;
gsup_msg->msisdn_enc_len = value_len;
@@ -597,11 +662,45 @@ static void encode_pdp_info(struct msgb *msg, enum osmo_gsup_iei iei,
u8 = pdp_info->context_id;
msgb_tlv_put(msg, OSMO_GSUP_PDP_CONTEXT_ID_IE, sizeof(u8), &u8);
- if (pdp_info->pdp_type) {
- msgb_tlv_put(msg, OSMO_GSUP_PDP_TYPE_IE,
- OSMO_GSUP_PDP_TYPE_SIZE,
- osmo_encode_big_endian(pdp_info->pdp_type | 0xf000,
- OSMO_GSUP_PDP_TYPE_SIZE));
+ if (pdp_info->pdp_type_org == PDP_TYPE_ORG_IETF) {
+ struct gsm48_pdp_address pdp_addr;
+ uint8_t pdp_addr_len = 2;
+ pdp_addr.spare = 0x0f;
+ pdp_addr.organization = pdp_info->pdp_type_org;
+ pdp_addr.type = pdp_info->pdp_type_nr;
+
+ switch (pdp_info->pdp_type_nr) {
+ case PDP_TYPE_N_IETF_IPv4:
+ if (pdp_info->pdp_address[0].u.sa.sa_family == AF_INET) {
+ pdp_addr.ietf.v4 = pdp_info->pdp_address[0].u.sin.sin_addr.s_addr;
+ pdp_addr_len += sizeof(pdp_addr.ietf.v4);
+ }
+ break;
+ case PDP_TYPE_N_IETF_IPv6:
+ if (pdp_info->pdp_address[0].u.sa.sa_family == AF_INET6) {
+ memcpy(pdp_addr.ietf.v6,
+ &pdp_info->pdp_address[0].u.sin6.sin6_addr,
+ sizeof(pdp_addr.ietf.v6));
+ pdp_addr_len += sizeof(pdp_addr.ietf.v6);
+ }
+ break;
+ case PDP_TYPE_N_IETF_IPv4v6:
+ if (pdp_info->pdp_address[0].u.sa.sa_family == AF_INET) {
+ pdp_addr.ietf.v4v6.v4 = pdp_info->pdp_address[0].u.sin.sin_addr.s_addr;
+ pdp_addr_len += sizeof(pdp_addr.ietf.v4v6.v4);
+ }
+ if (pdp_info->pdp_address[0].u.sa.sa_family == AF_INET6) {
+ memcpy(pdp_addr.ietf.v4v6.v6,
+ &pdp_info->pdp_address[1].u.sin6.sin6_addr,
+ sizeof(pdp_addr.ietf.v4v6.v6));
+ pdp_addr_len += sizeof(pdp_addr.ietf.v4v6.v6);
+ }
+ break;
+ }
+
+ msgb_tlv_put(msg, OSMO_GSUP_PDP_ADDRESS_IE,
+ pdp_addr_len,
+ (const uint8_t *)&pdp_addr);
}
if (pdp_info->apn_enc) {
@@ -778,6 +877,11 @@ int osmo_gsup_encode(struct msgb *msg, const struct osmo_gsup_message *gsup_msg)
if (gsup_msg->rand)
msgb_tlv_put(msg, OSMO_GSUP_RAND_IE, 16, gsup_msg->rand);
+ if (gsup_msg->pco && gsup_msg->pco_len > 0) {
+ if (gsup_msg->pco_len > OSMO_GSUP_MAX_PCO_LEN)
+ return -EINVAL;
+ msgb_tlv_put(msg, OSMO_GSUP_PCO_IE, gsup_msg->pco_len, gsup_msg->pco);
+ }
if (gsup_msg->cn_domain) {
uint8_t dn = gsup_msg->cn_domain;
msgb_tlv_put(msg, OSMO_GSUP_CN_DOMAIN_IE, 1, &dn);
@@ -911,6 +1015,7 @@ const struct value_string osmo_gsup_message_class_names[] = {
{ OSMO_GSUP_MESSAGE_CLASS_SMS, "SMS" },
{ OSMO_GSUP_MESSAGE_CLASS_USSD, "USSD" },
{ OSMO_GSUP_MESSAGE_CLASS_INTER_MSC, "Inter-MSC" },
+ { OSMO_GSUP_MESSAGE_CLASS_IPSEC_EPDG, "IPsec-ePDG" },
{}
};
diff --git a/src/gsm/ipa.c b/src/gsm/ipa.c
index 447e8e3d..6e41fd98 100644
--- a/src/gsm/ipa.c
+++ b/src/gsm/ipa.c
@@ -267,34 +267,43 @@ int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
return 0;
}
+/*! Fill ud struct from tp structure.
+ * \param[in,out] ud ipaccess_unit to fill
+ * \param[in] tp the decoded TLV structure from eg. ID_RESP message
+ * \returns zero on success, negative on error
+ *
+ * This function expects parameter ud's fields to be initialized to zero if not yet set.
+ * Existing incoming string pointer fields are expected to be allocated using
+ * talloc and will be deallocated as such if replaced with the content of tp.
+ **/
int ipa_ccm_tlv_to_unitdata(struct ipaccess_unit *ud,
const struct tlv_parsed *tp)
{
int rc = 0;
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SERNR, 1))
- ud->serno = talloc_strdup(ud, (char *)
- TLVP_VAL(tp, IPAC_IDTAG_SERNR));
+ osmo_talloc_replace_string(ud, &ud->serno,
+ (char *)TLVP_VAL(tp, IPAC_IDTAG_SERNR));
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNITNAME, 1))
- ud->unit_name = talloc_strdup(ud, (char *)
- TLVP_VAL(tp, IPAC_IDTAG_UNITNAME));
+ osmo_talloc_replace_string(ud, &ud->unit_name,
+ (char *)TLVP_VAL(tp, IPAC_IDTAG_UNITNAME));
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION1, 1))
- ud->location1 = talloc_strdup(ud, (char *)
- TLVP_VAL(tp, IPAC_IDTAG_LOCATION1));
+ osmo_talloc_replace_string(ud, &ud->location1,
+ (char *)TLVP_VAL(tp, IPAC_IDTAG_LOCATION1));
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION2, 1))
- ud->location2 = talloc_strdup(ud, (char *)
- TLVP_VAL(tp, IPAC_IDTAG_LOCATION2));
+ osmo_talloc_replace_string(ud, &ud->location2,
+ (char *)TLVP_VAL(tp, IPAC_IDTAG_LOCATION2));
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_EQUIPVERS, 1))
- ud->equipvers = talloc_strdup(ud, (char *)
- TLVP_VAL(tp, IPAC_IDTAG_EQUIPVERS));
+ osmo_talloc_replace_string(ud, &ud->equipvers,
+ (char *)TLVP_VAL(tp, IPAC_IDTAG_EQUIPVERS));
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SWVERSION, 1))
- ud->swversion = talloc_strdup(ud, (char *)
- TLVP_VAL(tp, IPAC_IDTAG_SWVERSION));
+ osmo_talloc_replace_string(ud, &ud->swversion,
+ (char *)TLVP_VAL(tp, IPAC_IDTAG_SWVERSION));
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_MACADDR, 17)) {
rc = osmo_macaddr_parse(ud->mac_addr, (char *)
@@ -478,7 +487,7 @@ int ipa_ccm_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd)
case IPAC_MSGT_PING:
ret = ipa_ccm_send_pong(bfd->fd);
if (ret < 0) {
- LOGP(DLINP, LOGL_ERROR, "Cannot send PING "
+ LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
"message. Reason: %s\n", strerror(errno));
break;
}
diff --git a/src/gsm/iuup.c b/src/gsm/iuup.c
new file mode 100644
index 00000000..16a6f5e0
--- /dev/null
+++ b/src/gsm/iuup.c
@@ -0,0 +1,1064 @@
+/*! \file iu_up.c
+ * IuUP (Iu User Plane) according to 3GPP TS 25.415 */
+/*
+ * (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+
+#include <osmocom/core/crc8gen.h>
+#include <osmocom/core/crc16gen.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gsm/prim.h>
+#include <osmocom/gsm/protocol/gsm_25_415.h>
+#include <osmocom/gsm/iuup.h>
+
+/***********************************************************************
+ * CRC Calculation
+ ***********************************************************************/
+
+/* Section 6.6.3.8 Header CRC */
+const struct osmo_crc8gen_code iuup_hdr_crc_code = {
+ .bits = 6,
+ .poly = 47,
+ .init = 0,
+ .remainder = 0,
+};
+
+/* Section 6.6.3.9 Payload CRC */
+const struct osmo_crc16gen_code iuup_data_crc_code = {
+ .bits = 10,
+ .poly = 563,
+ .init = 0,
+ .remainder = 0,
+};
+
+static int iuup_get_payload_offset(const uint8_t *iuup_pdu)
+{
+ uint8_t pdu_type = iuup_pdu[0] >> 4;
+ switch (pdu_type) {
+ case 0:
+ case 14:
+ return 4;
+ case 1:
+ return 3;
+ default:
+ return -1;
+ }
+}
+
+int osmo_iuup_compute_payload_crc(const uint8_t *iuup_pdu, unsigned int pdu_len)
+{
+ ubit_t buf[1024*8];
+ uint8_t pdu_type;
+ int offset, payload_len_bytes;
+
+ if (pdu_len < 1)
+ return -1;
+
+ pdu_type = iuup_pdu[0] >> 4;
+
+ /* Type 1 has no CRC */
+ if (pdu_type == 1)
+ return 0;
+
+ offset = iuup_get_payload_offset(iuup_pdu);
+ if (offset < 0)
+ return offset;
+
+ if (pdu_len < offset)
+ return -1;
+
+ payload_len_bytes = pdu_len - offset;
+ osmo_pbit2ubit(buf, iuup_pdu+offset, payload_len_bytes*8);
+ return osmo_crc16gen_compute_bits(&iuup_data_crc_code, buf, payload_len_bytes*8);
+}
+
+int osmo_iuup_compute_header_crc(const uint8_t *iuup_pdu, unsigned int pdu_len)
+{
+ ubit_t buf[2*8];
+
+ if (pdu_len < 2)
+ return -1;
+
+ osmo_pbit2ubit(buf, iuup_pdu, 2*8);
+ return osmo_crc8gen_compute_bits(&iuup_hdr_crc_code, buf, 2*8);
+}
+
+/***********************************************************************
+ * Internal State / FSM (Annex B)
+ ***********************************************************************/
+
+#define S(x) (1 << (x))
+
+#define IUUP_TIMER_INIT 1
+#define IUUP_TIMER_TA 2
+#define IUUP_TIMER_RC 3
+
+struct osmo_timer_nt {
+ uint32_t n; /* number of repetitions */
+ struct osmo_iuup_tnl_prim *retrans_itp;
+ struct osmo_timer_list timer;
+};
+
+struct osmo_iuup_instance {
+ struct osmo_iuup_rnl_config config;
+ struct osmo_fsm_inst *fi;
+
+ uint8_t mode_version;
+
+ struct {
+ struct osmo_timer_nt init;
+ struct osmo_timer_nt ta;
+ struct osmo_timer_nt rc;
+ } timer;
+ /* call-back function to pass primitives up to the user */
+ osmo_prim_cb user_prim_cb;
+ void *user_prim_priv;
+ osmo_prim_cb transport_prim_cb;
+ void *transport_prim_priv;
+ uint8_t type14_fn; /* 2 bits */
+};
+
+enum iuup_fsm_state {
+ IUUP_FSM_ST_NULL,
+ IUUP_FSM_ST_INIT,
+ IUUP_FSM_ST_TrM_DATA_XFER_READY,
+ IUUP_FSM_ST_SMpSDU_DATA_XFER_READY,
+};
+
+enum iuup_fsm_event {
+ IUUP_FSM_EVT_IUUP_CONFIG_REQ,
+ IUUP_FSM_EVT_IUUP_DATA_REQ,
+ IUUP_FSM_EVT_IUUP_DATA_IND,
+ IUUP_FSM_EVT_IUUP_STATUS_REQ,
+ IUUP_FSM_EVT_IUUP_STATUS_IND,
+ IUUP_FSM_EVT_SSASAR_UNITDATA_REQ,
+ IUUP_FSM_EVT_SSASAR_UNITDATA_IND,
+ IUUP_FSM_EVT_IUUP_UNITDATA_REQ,
+ IUUP_FSM_EVT_IUUP_UNITDATA_IND,
+ IUUP_FSM_EVT_INIT,
+ IUUP_FSM_EVT_LAST_INIT_ACK,
+ IUUP_FSM_EVT_INIT_NACK,
+};
+
+static const struct value_string iuup_fsm_event_names[] = {
+ { IUUP_FSM_EVT_IUUP_CONFIG_REQ, "IuUP-CONFIG-req" },
+ { IUUP_FSM_EVT_IUUP_DATA_REQ, "IuUP-DATA-req" },
+ { IUUP_FSM_EVT_IUUP_DATA_IND, "IuUP-DATA-ind" },
+ { IUUP_FSM_EVT_IUUP_STATUS_REQ, "IuUP-STATUS-req" },
+ { IUUP_FSM_EVT_IUUP_STATUS_IND, "IuUP-STATUS-ind" },
+ { IUUP_FSM_EVT_SSASAR_UNITDATA_REQ, "SSSAR-UNITDATA-req" },
+ { IUUP_FSM_EVT_SSASAR_UNITDATA_IND, "SSSAR-UNITDATA-ind" },
+ { IUUP_FSM_EVT_IUUP_UNITDATA_REQ, "IuUP-UNITDATA-req" },
+ { IUUP_FSM_EVT_IUUP_UNITDATA_IND, "IuUP-UNITDATA-ind" },
+ { IUUP_FSM_EVT_INIT, "INIT" },
+ { IUUP_FSM_EVT_LAST_INIT_ACK, "LAST_INIT_ACK" },
+ { IUUP_FSM_EVT_INIT_NACK, "INIT_NACK" },
+ { 0, NULL }
+};
+
+static inline uint8_t iuup_get_pdu_type(const uint8_t *data)
+{
+ return data[0] >> 4;
+}
+
+static inline uint8_t iuup_get_hdr_crc(const uint8_t *data)
+{
+ return data[2] >> 2;
+}
+
+/* Helper functions to store non-packed structs in msgb so that pointers are properly aligned: */
+#define IUUP_MSGB_SIZE 4096
+#define PTR_ALIGNMENT_BYTES 8
+#define IUUP_MSGB_HEADROOM_MIN_REQUIRED (OSMO_MAX(sizeof(struct osmo_iuup_tnl_prim), sizeof(struct osmo_iuup_rnl_prim)) + (PTR_ALIGNMENT_BYTES - 1))
+static inline struct msgb *osmo_iuup_msgb_alloc_c(void *ctx, size_t size)
+{
+ OSMO_ASSERT(size > IUUP_MSGB_HEADROOM_MIN_REQUIRED);
+ return msgb_alloc_headroom_c(ctx, size, IUUP_MSGB_HEADROOM_MIN_REQUIRED, "iuup-msgb");
+}
+
+/* push data so that the resulting pointer to write to is aligned to 8 byte */
+static inline __attribute__((assume_aligned(PTR_ALIGNMENT_BYTES)))
+unsigned char *aligned_msgb_push(struct msgb *msg, unsigned int len)
+{
+ uint8_t *ptr = (msgb_data(msg) - len);
+ size_t extra_size = ((uintptr_t)ptr & (PTR_ALIGNMENT_BYTES - 1));
+
+ return msgb_push(msg, len + extra_size);
+}
+
+struct osmo_iuup_rnl_prim *osmo_iuup_rnl_prim_alloc(void *ctx, unsigned int primitive, unsigned int operation, unsigned int size)
+{
+ struct msgb *msg;
+ struct osmo_iuup_rnl_prim *irp;
+
+ msg = osmo_iuup_msgb_alloc_c(ctx, size);
+ irp = (struct osmo_iuup_rnl_prim *)aligned_msgb_push(msg, sizeof(*irp));
+ osmo_prim_init(&irp->oph, SAP_IUUP_RNL, primitive, operation, msg);
+ return irp;
+}
+
+struct osmo_iuup_tnl_prim *osmo_iuup_tnl_prim_alloc(void *ctx, unsigned int primitive, unsigned int operation, unsigned int size)
+{
+ struct msgb *msg;
+ struct osmo_iuup_tnl_prim *itp;
+
+ msg = osmo_iuup_msgb_alloc_c(ctx, size);
+ itp = (struct osmo_iuup_tnl_prim *) aligned_msgb_push(msg, sizeof(*itp));
+ osmo_prim_init(&itp->oph, SAP_IUUP_TNL, primitive, operation, msg);
+ return itp;
+}
+
+/* 6.6.2.3.2 */
+static struct osmo_iuup_tnl_prim *itp_ctrl_ack_alloc(struct osmo_iuup_instance *iui, enum iuup_procedure proc_ind, uint8_t fn)
+{
+ struct osmo_iuup_tnl_prim *itp;
+ struct iuup_ctrl_ack *ack;
+ itp = osmo_iuup_tnl_prim_alloc(iui, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ itp->oph.msg->l2h = msgb_put(itp->oph.msg, sizeof(struct iuup_ctrl_ack));
+ ack = (struct iuup_ctrl_ack *) msgb_l2(itp->oph.msg);
+ *ack = (struct iuup_ctrl_ack){
+ .hdr = {
+ .frame_nr = fn,
+ .ack_nack = IUUP_AN_ACK,
+ .pdu_type = IUUP_PDU_T_CONTROL,
+ .proc_ind = proc_ind,
+ .mode_version = iui->mode_version,
+ .payload_crc_hi = 0,
+ .header_crc = 0,
+ .payload_crc_lo = 0,
+ },
+ };
+ ack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(itp->oph.msg), msgb_l2len(itp->oph.msg));
+ return itp;
+}
+
+/* 6.6.2.3.3 */
+static struct osmo_iuup_tnl_prim *tnp_ctrl_nack_alloc(struct osmo_iuup_instance *iui, enum iuup_procedure proc_ind, enum iuup_error_cause error_cause, uint8_t fn)
+{
+ struct osmo_iuup_tnl_prim *itp;
+ struct iuup_ctrl_nack *nack;
+ itp = osmo_iuup_tnl_prim_alloc(iui, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ itp->oph.msg->l2h = msgb_put(itp->oph.msg, sizeof(struct iuup_ctrl_nack));
+ nack = (struct iuup_ctrl_nack *) msgb_l2(itp->oph.msg);
+ *nack = (struct iuup_ctrl_nack){
+ .hdr = {
+ .frame_nr = fn,
+ .ack_nack = IUUP_AN_NACK,
+ .pdu_type = IUUP_PDU_T_CONTROL,
+ .proc_ind = proc_ind,
+ .mode_version = iui->mode_version,
+ .payload_crc_hi = 0,
+ .header_crc = 0,
+ .payload_crc_lo = 0,
+ },
+ .spare = 0,
+ .error_cause = error_cause,
+ };
+ nack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(itp->oph.msg), msgb_l2len(itp->oph.msg));
+ return itp;
+}
+
+/* 6.6.2.3.4.1 */
+static struct osmo_iuup_tnl_prim *tnp_ctrl_init_alloc(struct osmo_iuup_instance *iui)
+{
+ struct osmo_iuup_tnl_prim *itp;
+ struct iuup_pdutype14_hdr *hdr;
+ struct iuup_ctrl_init_hdr *ihdr;
+ struct iuup_ctrl_init_rfci_hdr *ihdr_rfci;
+ struct iuup_ctrl_init_tail *itail;
+ unsigned int i, j;
+ uint16_t payload_crc;
+ uint8_t rfci_cnt;
+ struct msgb *msg;
+
+ itp = osmo_iuup_tnl_prim_alloc(iui, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ msg = itp->oph.msg;
+
+ msg->l2h = msgb_put(msg, sizeof(*hdr));
+ hdr = (struct iuup_pdutype14_hdr *)msgb_l2(msg);
+ hdr->frame_nr = iui->type14_fn++;
+ hdr->ack_nack = IUUP_AN_PROCEDURE;
+ hdr->pdu_type = IUUP_PDU_T_CONTROL;
+ hdr->proc_ind = IUUP_PROC_INIT;
+ hdr->mode_version = 0; /* Use here the minimum version required to negotiate */
+ hdr->header_crc = osmo_iuup_compute_header_crc(msgb_l2(msg), msgb_l2len(msg));
+
+ ihdr = (struct iuup_ctrl_init_hdr *)msgb_put(msg, sizeof(*ihdr));
+ ihdr->chain_ind = 0; /* this frame is the last frame for the procedure. TODO: support several */
+ ihdr->num_subflows_per_rfci = iui->config.num_subflows;
+ ihdr->ti = iui->config.IPTIs_present ? 1 : 0;
+ ihdr->spare = 0;
+
+ /* RFCI + subflow size part: */
+ rfci_cnt = 0;
+ for (i = 0; i < ARRAY_SIZE(iui->config.rfci); i++) {
+ bool last;
+ uint8_t len_size;
+ struct osmo_iuup_rfci *rfci = &iui->config.rfci[i];
+ if (!rfci->used)
+ continue;
+ rfci_cnt++;
+ last = (rfci_cnt == iui->config.num_rfci);
+
+ len_size = 1;
+ for (j = 0; j < iui->config.num_subflows; j++) {
+ if (rfci->subflow_sizes[j] > UINT8_MAX)
+ len_size = 2;
+ }
+
+ ihdr_rfci = (struct iuup_ctrl_init_rfci_hdr *)msgb_put(msg, sizeof(*ihdr_rfci) + len_size * iui->config.num_subflows);
+ ihdr_rfci->rfci = rfci->id;
+ ihdr_rfci->li = len_size - 1;
+ ihdr_rfci->lri = last;
+ if (len_size == 2) {
+ uint16_t *buf = (uint16_t *)&ihdr_rfci->subflow_length[0];
+ for (j = 0; j < iui->config.num_subflows; j++)
+ osmo_store16be(rfci->subflow_sizes[j], buf++);
+ } else {
+ for (j = 0; j < iui->config.num_subflows; j++)
+ ihdr_rfci->subflow_length[j] = rfci->subflow_sizes[j];
+ }
+ /* early loop termination: */
+ if (last)
+ break;
+ }
+ /* Sanity check: */
+ if (rfci_cnt != iui->config.num_rfci) {
+ LOGP(DLIUUP, LOGL_ERROR, "rfci_cnt %u != num_rfci %u\n",
+ rfci_cnt, iui->config.num_rfci);
+ msgb_free(msg);
+ return NULL;
+ }
+
+ if (iui->config.IPTIs_present) {
+ uint8_t num_bytes = (iui->config.num_rfci + 1) / 2;
+ uint8_t *buf = msgb_put(msg, num_bytes);
+ rfci_cnt = 0;
+ for (i = 0; i < ARRAY_SIZE(iui->config.rfci); i++) {
+ struct osmo_iuup_rfci *rfci = &iui->config.rfci[i];
+ if (!rfci->used)
+ continue;
+ if (!(rfci_cnt & 0x01)) /* is even: */
+ buf[rfci_cnt / 2] = (((uint8_t)rfci->IPTI) << 4);
+ else
+ buf[rfci_cnt / 2] |= (rfci->IPTI & 0x0F);
+ rfci_cnt++;
+ /* early loop termination: */
+ if (rfci_cnt == iui->config.num_rfci)
+ break;
+ }
+ }
+
+ itail = (struct iuup_ctrl_init_tail *)msgb_put(msg, sizeof(*itail));
+ osmo_store16be(iui->config.supported_versions_mask, &itail->versions_supported);
+ itail->spare = 0;
+ itail->data_pdu_type = iui->config.data_pdu_type;
+
+ payload_crc = osmo_iuup_compute_payload_crc(msgb_l2(msg), msgb_l2len(msg));
+ hdr->payload_crc_hi = (payload_crc >> 8) & 0x03;
+ hdr->payload_crc_lo = payload_crc & 0xff;
+
+
+ return itp;
+}
+
+static struct osmo_iuup_rnl_prim *irp_init_ind_alloc(struct osmo_iuup_instance *iui)
+{
+ struct osmo_iuup_rnl_prim *irp;
+
+ irp = osmo_iuup_rnl_prim_alloc(iui, OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ irp->u.status.procedure = IUUP_PROC_INIT;
+ irp->u.status.u.initialization.mode_version = iui->mode_version;
+ irp->u.status.u.initialization.data_pdu_type = iui->config.data_pdu_type;
+ irp->u.status.u.initialization.num_subflows = iui->config.num_subflows;
+ irp->u.status.u.initialization.num_rfci = iui->config.num_rfci;
+ irp->u.status.u.initialization.IPTIs_present = iui->config.IPTIs_present;
+ memcpy(irp->u.status.u.initialization.rfci, iui->config.rfci, sizeof(iui->config.rfci));
+ return irp;
+}
+
+/* transform a RNL data primitive into a TNL data primitive (down the stack) */
+static struct osmo_iuup_tnl_prim *rnl_to_tnl_data(struct osmo_iuup_instance *iui,
+ struct osmo_iuup_rnl_prim *irp)
+{
+ struct osmo_iuup_tnl_prim *itp;
+ struct osmo_iuup_rnl_data dt;
+ struct msgb *msg;
+ uint16_t payload_crc;
+ struct iuup_pdutype0_hdr *h0;
+ struct iuup_pdutype1_hdr *h1;
+
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST));
+
+ msg = irp->oph.msg;
+ dt = irp->u.data;
+
+ /* pull up to the IuUP payload and push a new primitive header in front */
+ msgb_pull_to_l3(msg);
+
+ /* push the PDU TYPE 0 / 1 header in front of the payload */
+ switch (iui->config.data_pdu_type) {
+ case 0:
+ msg->l2h = msgb_push(msg, sizeof(*h0));
+ h0 = (struct iuup_pdutype0_hdr *)msg->l2h;
+ h0->frame_nr = dt.frame_nr;
+ h0->pdu_type = IUUP_PDU_T_DATA_CRC;
+ h0->rfci = dt.rfci;
+ h0->fqc = dt.fqc;
+ h0->header_crc = osmo_iuup_compute_header_crc(msgb_l2(msg), msgb_l2len(msg));
+ payload_crc = osmo_iuup_compute_payload_crc(msgb_l2(msg), msgb_l2len(msg));
+ h0->payload_crc_hi = (payload_crc >> 8) & 0x03;
+ h0->payload_crc_lo = payload_crc & 0xff;
+ break;
+ case 1:
+ msg->l2h = msgb_push(msg, sizeof(*h1));
+ h1 = (struct iuup_pdutype1_hdr *)msg->l2h;
+ h1->frame_nr = dt.frame_nr;
+ h1->pdu_type = IUUP_PDU_T_DATA_NOCRC;
+ h1->rfci = dt.rfci;
+ h1->fqc = dt.fqc;
+ h1->header_crc = osmo_iuup_compute_header_crc(msgb_l2(msg), msgb_l2len(msg));
+ h1->spare = 0;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ /* Avoid allocating irp out of 8byte-aligned address, Asan is not happy with it */
+ itp = (struct osmo_iuup_tnl_prim *) aligned_msgb_push(msg, sizeof(*itp));
+ osmo_prim_init(&itp->oph, SAP_IUUP_TNL, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST, msg);
+
+ return itp;
+}
+
+/* transform a TNL primitive into a RNL primitive (up the stack) */
+static struct osmo_iuup_rnl_prim *tnl_to_rnl_data(struct osmo_iuup_tnl_prim *itp)
+{
+ struct msgb *msg;
+ struct iuup_pdutype0_hdr *h0;
+ struct iuup_pdutype1_hdr *h1;
+ struct osmo_iuup_rnl_data dt;
+ struct osmo_iuup_rnl_prim *irp;
+
+ msg = itp->oph.msg;
+
+ OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION));
+
+ switch (iuup_get_pdu_type(msgb_l2(msg))) {
+ case IUUP_PDU_T_DATA_CRC:
+ h0 = (struct iuup_pdutype0_hdr *) msgb_l2(msg);
+ dt.rfci = h0->rfci;
+ dt.frame_nr = h0->frame_nr;
+ dt.fqc = h0->fqc;
+ break;
+ case IUUP_PDU_T_DATA_NOCRC:
+ h1 = (struct iuup_pdutype1_hdr *) msgb_l2(msg);
+ dt.rfci = h1->rfci;
+ dt.frame_nr = h1->frame_nr;
+ dt.fqc = h1->fqc;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ /* pull up to the IuUP payload and push a new primitive header in front */
+ msgb_pull_to_l3(msg);
+
+ /* Avoid allocating irp out of 8byte-aligned address, Asan is not happy with it */
+ irp = (struct osmo_iuup_rnl_prim *) aligned_msgb_push(msg, sizeof(*irp));
+ osmo_prim_init(&irp->oph, SAP_IUUP_RNL, OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION, msg);
+ irp->u.data = dt;
+
+ return irp;
+}
+
+static struct osmo_iuup_rnl_prim *irp_error_event_alloc_c(void *ctx, enum iuup_error_cause cause, enum iuup_error_distance distance)
+{
+ struct osmo_iuup_rnl_prim *irp;
+ struct msgb *msg;
+ msg = msgb_alloc_c(ctx, sizeof(*irp), "iuup-tx");
+ irp = (struct osmo_iuup_rnl_prim *) msgb_put(msg, sizeof(*irp));
+ osmo_prim_init(&irp->oph, SAP_IUUP_RNL, OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION, msg);
+ irp->u.status.procedure = IUUP_PROC_ERR_EVENT;
+ irp->u.status.u.error_event.cause = cause;
+ irp->u.status.u.error_event.distance = distance;
+ return irp;
+}
+
+static struct osmo_iuup_tnl_prim *itp_copy_c(void *ctx, const struct osmo_iuup_tnl_prim *src_itp)
+{
+ struct msgb *msg;
+ struct osmo_iuup_tnl_prim *dst_itp;
+
+ msg = msgb_copy_c(ctx, src_itp->oph.msg, "iuup-tx-retrans");
+ dst_itp = (struct osmo_iuup_tnl_prim *)msgb_data(msg);
+ dst_itp->oph.msg = msg;
+ return dst_itp;
+}
+
+static void retransmit_initialization(struct osmo_iuup_instance *iui)
+{
+ struct osmo_iuup_tnl_prim *itp;
+ iui->fi->T = IUUP_TIMER_INIT;
+ osmo_timer_schedule(&iui->fi->timer, iui->config.t_init.t_ms / 1000, (iui->config.t_init.t_ms % 1000) * 1000);
+ itp = itp_copy_c(iui, iui->timer.init.retrans_itp);
+ iui->transport_prim_cb(&itp->oph, iui->transport_prim_priv);
+}
+
+/* return: whether the last Init was Acked correctly and hence can transition to next state */
+static bool iuup_rx_initialization(struct osmo_iuup_instance *iui, struct osmo_iuup_tnl_prim *itp)
+{
+ struct iuup_pdutype14_hdr *hdr;
+ struct iuup_ctrl_init_hdr *ihdr;
+ struct iuup_ctrl_init_rfci_hdr *ihdr_rfci;
+ struct iuup_ctrl_init_tail *itail;
+ enum iuup_error_cause err_cause;
+ uint8_t num_rfci = 0;
+ int i;
+ bool is_last;
+ uint16_t remote_mask, match_mask;
+ struct osmo_iuup_rnl_prim *irp;
+ struct osmo_iuup_tnl_prim *resp;
+
+ /* TODO: whenever we check message boundaries, length, etc. and we fail, send NACK */
+
+ hdr = (struct iuup_pdutype14_hdr *)msgb_l2(itp->oph.msg);
+ ihdr = (struct iuup_ctrl_init_hdr *)hdr->payload;
+ if (ihdr->num_subflows_per_rfci == 0) {
+ LOGPFSML(iui->fi, LOGL_NOTICE, "Initialization: Unexpected num_subflows=0 received\n");
+ err_cause = IUUP_ERR_CAUSE_UNEXPECTED_VALUE;
+ goto send_nack;
+ }
+ ihdr_rfci = (struct iuup_ctrl_init_rfci_hdr *)ihdr->rfci_data;
+
+ do {
+ struct osmo_iuup_rfci *rfci = &iui->config.rfci[num_rfci];
+ uint8_t l_size_bytes = ihdr_rfci->li + 1;
+ is_last = ihdr_rfci->lri;
+ if (num_rfci >= IUUP_MAX_RFCIS) {
+ LOGPFSML(iui->fi, LOGL_NOTICE, "Initialization: Too many RFCIs received (%u)\n",
+ num_rfci);
+ err_cause = IUUP_ERR_CAUSE_UNEXPECTED_RFCI;
+ goto send_nack;
+ }
+ rfci->used = 1;
+ rfci->id = ihdr_rfci->rfci;
+ if (l_size_bytes == 2) {
+ uint16_t *subflow_size = (uint16_t *)ihdr_rfci->subflow_length;
+ for (i = 0; i < ihdr->num_subflows_per_rfci; i++) {
+ rfci->subflow_sizes[i] = osmo_load16be(subflow_size);
+ subflow_size++;
+ }
+ } else {
+ uint8_t *subflow_size = ihdr_rfci->subflow_length;
+ for (i = 0; i < ihdr->num_subflows_per_rfci; i++) {
+ rfci->subflow_sizes[i] = *subflow_size;
+ subflow_size++;
+ }
+ }
+ num_rfci++;
+ ihdr_rfci++;
+ ihdr_rfci = (struct iuup_ctrl_init_rfci_hdr *)(((uint8_t *)ihdr_rfci) + ihdr->num_subflows_per_rfci * l_size_bytes);
+ } while (!is_last);
+
+ if (ihdr->ti) { /* Timing information present */
+ uint8_t *buf = (uint8_t *)ihdr_rfci;
+ uint8_t num_bytes = (num_rfci + 1) / 2;
+ iui->config.IPTIs_present = true;
+ for (i = 0; i < num_bytes - 1; i++) {
+ iui->config.rfci[i*2].IPTI = *buf >> 4;
+ iui->config.rfci[i*2 + 1].IPTI = *buf & 0x0f;
+ buf++;
+ }
+ iui->config.rfci[i*2].IPTI = *buf >> 4;
+ if (!(num_rfci & 0x01)) /* is even: */
+ iui->config.rfci[i*2 + 1].IPTI = *buf & 0x0f;
+ buf++;
+ itail = (struct iuup_ctrl_init_tail *)buf;
+ } else {
+ iui->config.IPTIs_present = false;
+ itail = (struct iuup_ctrl_init_tail *)ihdr_rfci;
+ }
+ if (itail->data_pdu_type > 1) {
+ LOGPFSML(iui->fi, LOGL_NOTICE, "Initialization: Unexpected Data PDU Type %u received\n", itail->data_pdu_type);
+ err_cause = IUUP_ERR_CAUSE_UNEXPECTED_VALUE;
+ goto send_nack;
+ }
+
+ remote_mask = osmo_load16be(&itail->versions_supported);
+ match_mask = (remote_mask & iui->config.supported_versions_mask);
+ if (match_mask == 0x0000) {
+ LOGPFSML(iui->fi, LOGL_NOTICE,
+ "Initialization: No match in supported versions local=0x%04x vs remote=0x%04x\n",
+ iui->config.supported_versions_mask, remote_mask);
+ err_cause = IUUP_ERR_CAUSE_UNEXPECTED_VALUE;
+ goto send_nack;
+ }
+ for (i = 15; i >= 0; i--) {
+ if (match_mask & (1<<i)) {
+ iui->mode_version = i;
+ break;
+ }
+ }
+
+ iui->config.num_rfci = num_rfci;
+ iui->config.num_subflows = ihdr->num_subflows_per_rfci;
+ iui->config.data_pdu_type = itail->data_pdu_type;
+
+ irp = irp_init_ind_alloc(iui);
+ iui->user_prim_cb(&irp->oph, iui->user_prim_priv);
+
+ LOGPFSML(iui->fi, LOGL_DEBUG, "Tx Initialization ACK\n");
+ resp = itp_ctrl_ack_alloc(iui, IUUP_PROC_INIT, hdr->frame_nr);
+ iui->transport_prim_cb(&resp->oph, iui->transport_prim_priv);
+ return ihdr->chain_ind == 0;
+send_nack:
+ LOGPFSML(iui->fi, LOGL_NOTICE, "Tx Initialization NACK cause=%u orig_message=%s\n",
+ err_cause, osmo_hexdump((const unsigned char *) msgb_l2(itp->oph.msg), msgb_l2len(itp->oph.msg)));
+ resp = tnp_ctrl_nack_alloc(iui, IUUP_PROC_INIT, err_cause, hdr->frame_nr);
+ iui->transport_prim_cb(&resp->oph, iui->transport_prim_priv);
+ return false;
+}
+
+/**********************
+ * FSM STATE FUNCTIONS
+ **********************/
+static void iuup_fsm_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_iuup_instance *iui = fi->priv;
+ struct osmo_iuup_rnl_prim *user_prim = NULL;
+
+ switch (event) {
+ case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
+ user_prim = data;
+ iui->config = user_prim->u.config;
+ iui->config.supported_versions_mask &= 0x0003; /* We only support versions 1 and 2 ourselves */
+ //TODO: if supported_versions_mask == 0x0000,no supported versions, send error to upper layers
+
+ if (iui->config.transparent)
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_TrM_DATA_XFER_READY, 0, 0);
+ else {
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_INIT, 0, 0);
+ }
+ break;
+ }
+}
+
+/* transparent mode data transfer */
+static void iuup_fsm_trm_data(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ //struct osmo_iuup_instance *iui = fi->priv;
+
+ switch (event) {
+ case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_NULL, 0, 0);
+ break;
+ case IUUP_FSM_EVT_IUUP_DATA_REQ:
+ /* Data coming down from RNL (user) towards TNL (transport) */
+ break;
+ case IUUP_FSM_EVT_IUUP_DATA_IND:
+ /* Data coming up from TNL (transport) towards RNL (user) */
+ break;
+ case IUUP_FSM_EVT_IUUP_UNITDATA_REQ:
+ case IUUP_FSM_EVT_IUUP_UNITDATA_IND:
+ case IUUP_FSM_EVT_SSASAR_UNITDATA_REQ:
+ case IUUP_FSM_EVT_SSASAR_UNITDATA_IND:
+ /* no state change */
+ break;
+ }
+}
+
+static void iuup_fsm_init_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmo_iuup_instance *iui = fi->priv;
+
+ iui->type14_fn = 0;
+ if (iui->config.active) {
+ iui->timer.init.n = 0;
+ iui->timer.init.retrans_itp = tnp_ctrl_init_alloc(iui);
+ retransmit_initialization(iui);
+ }
+}
+
+static void iuup_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_iuup_instance *iui = fi->priv;
+ struct osmo_iuup_rnl_prim *irp;
+ struct osmo_iuup_tnl_prim *itp;
+
+ switch (event) {
+ case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
+ /* the only permitted 'config req' type is the request to release the instance */
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_NULL, 0, 0);
+ break;
+ case IUUP_FSM_EVT_INIT:
+ itp = data;
+ if (iuup_rx_initialization(iui, itp))
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_SMpSDU_DATA_XFER_READY, 0, 0);
+ break;
+ case IUUP_FSM_EVT_LAST_INIT_ACK:
+ /* last INIT ACK was received, transition to DATA_XFER_READY state */
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_SMpSDU_DATA_XFER_READY, 0, 0);
+ break;
+ case IUUP_FSM_EVT_INIT_NACK:
+ LOGPFSML(fi, LOGL_NOTICE, "Rx Initialization NACK N=%" PRIu32 "/%" PRIu32 "\n",
+ iui->timer.init.n, iui->config.t_init.n_max);
+ osmo_timer_del(&fi->timer);
+ if (iui->timer.init.n == iui->config.t_init.n_max) {
+ irp = irp_error_event_alloc_c(iui, IUUP_ERR_CAUSE_INIT_FAILURE_REP_NACK, IUUP_ERR_DIST_SECOND_FWD);
+ iui->user_prim_cb(&irp->oph, iui->user_prim_priv);
+ return;
+ }
+ iui->timer.init.n++;
+ retransmit_initialization(iui);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+/* 3GPP TS 25.415 B.2.3 "Support Mode Data Transfer Ready State" */
+static void iuup_fsm_smpsdu_data(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_iuup_instance *iui = fi->priv;
+ struct osmo_iuup_rnl_prim *irp = NULL;
+ struct osmo_iuup_tnl_prim *itp = NULL;
+
+ switch (event) {
+ case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
+ irp = data;
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_NULL, 0, 0);
+ break;
+ case IUUP_FSM_EVT_INIT:
+ /* "In case of handover or relocation, Initialisation procedures
+ * may have to be performed and Iu UP instance may have to enter
+ * the initialisation state." */
+ itp = data;
+ if (!iuup_rx_initialization(iui, itp))
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_INIT, 0, 0);
+ break;
+ case IUUP_FSM_EVT_IUUP_DATA_REQ:
+ /* Data coming down from RNL (user) towards TNL (transport) */
+ irp = data;
+ itp = rnl_to_tnl_data(iui, irp);
+ iui->transport_prim_cb(&itp->oph, iui->transport_prim_priv);
+ break;
+ case IUUP_FSM_EVT_IUUP_DATA_IND:
+ /* Data coming up from TNL (transport) towards RNL (user) */
+ itp = data;
+ irp = tnl_to_rnl_data(itp);
+ iui->user_prim_cb(&irp->oph, iui->user_prim_priv);
+ break;
+ }
+}
+
+static int iuup_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct osmo_iuup_instance *iui = fi->priv;
+ struct osmo_iuup_rnl_prim *irp;
+
+ switch (fi->T) {
+ case IUUP_TIMER_INIT:
+ OSMO_ASSERT(fi->state == IUUP_FSM_ST_INIT);
+ if (iui->timer.init.n == iui->config.t_init.n_max) {
+ irp = irp_error_event_alloc_c(iui, IUUP_ERR_CAUSE_INIT_FAILURE_NET_TMR, IUUP_ERR_DIST_LOCAL);
+ iui->user_prim_cb(&irp->oph, iui->user_prim_priv);
+ return 0;
+ }
+ iui->timer.init.n++;
+ retransmit_initialization(iui);
+ break;
+ case IUUP_TIMER_TA:
+ break;
+ case IUUP_TIMER_RC:
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ return 0;
+}
+
+
+static const struct osmo_fsm_state iuup_fsm_states[] = {
+ [IUUP_FSM_ST_NULL] = {
+ .in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ),
+ .out_state_mask = S(IUUP_FSM_ST_INIT) |
+ S(IUUP_FSM_ST_TrM_DATA_XFER_READY),
+ .name = "NULL",
+ .action = iuup_fsm_null,
+ },
+ [IUUP_FSM_ST_TrM_DATA_XFER_READY] = {
+ .in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ) |
+ S(IUUP_FSM_EVT_IUUP_STATUS_REQ) |
+ S(IUUP_FSM_EVT_IUUP_DATA_REQ) |
+ S(IUUP_FSM_EVT_IUUP_DATA_IND) |
+ S(IUUP_FSM_EVT_IUUP_UNITDATA_REQ) |
+ S(IUUP_FSM_EVT_IUUP_UNITDATA_IND) |
+ S(IUUP_FSM_EVT_SSASAR_UNITDATA_REQ) |
+ S(IUUP_FSM_EVT_SSASAR_UNITDATA_IND),
+ .out_state_mask = S(IUUP_FSM_ST_NULL),
+ .name = "TrM_Data_Transfer_Ready",
+ .action = iuup_fsm_trm_data,
+ },
+ [IUUP_FSM_ST_INIT] = {
+ .in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ) |
+ S(IUUP_FSM_EVT_INIT) |
+ S(IUUP_FSM_EVT_LAST_INIT_ACK) |
+ S(IUUP_FSM_EVT_INIT_NACK),
+ .out_state_mask = S(IUUP_FSM_ST_NULL) |
+ S(IUUP_FSM_ST_SMpSDU_DATA_XFER_READY),
+ .name = "Initialisation",
+ .onenter = iuup_fsm_init_on_enter,
+ .action = iuup_fsm_init,
+ },
+ [IUUP_FSM_ST_SMpSDU_DATA_XFER_READY] = {
+ .in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ) |
+ S(IUUP_FSM_EVT_INIT) |
+ S(IUUP_FSM_EVT_IUUP_DATA_REQ) |
+ S(IUUP_FSM_EVT_IUUP_DATA_IND),
+ .out_state_mask = S(IUUP_FSM_ST_NULL) |
+ S(IUUP_FSM_ST_INIT),
+ .name = "SMpSDU_Data_Transfer_Ready",
+ .action = iuup_fsm_smpsdu_data,
+ },
+};
+
+static struct osmo_fsm iuup_fsm = {
+ .name = "IuUP",
+ .states = iuup_fsm_states,
+ .num_states = ARRAY_SIZE(iuup_fsm_states),
+ .timer_cb = iuup_fsm_timer_cb,
+ .log_subsys = DLIUUP,
+ .event_names = iuup_fsm_event_names,
+};
+
+static int iuup_verify_pdu(const uint8_t *data, unsigned int len)
+{
+ int header_crc_computed, payload_crc_computed;
+ uint16_t payload_crc;
+ uint8_t pdu_type = iuup_get_pdu_type(data);
+ struct iuup_pdutype0_hdr *t0h;
+ struct iuup_pdutype14_hdr *t14h;
+
+ if (len < 3)
+ return -EINVAL;
+
+ header_crc_computed = osmo_iuup_compute_header_crc(data, len);
+ if (iuup_get_hdr_crc(data) != header_crc_computed) {
+ LOGP(DLIUUP, LOGL_NOTICE, "Header Checksum error: rx 0x%02x vs exp 0x%02x\n",
+ iuup_get_hdr_crc(data), header_crc_computed);
+ return -EIO;
+ }
+ switch (pdu_type) {
+ case IUUP_PDU_T_DATA_NOCRC:
+ if (len < 4)
+ return -EINVAL;
+ break;
+ case IUUP_PDU_T_DATA_CRC:
+ t0h = (struct iuup_pdutype0_hdr *) data;
+ payload_crc = ((uint16_t)t0h->payload_crc_hi << 8) | t0h->payload_crc_lo;
+ payload_crc_computed = osmo_iuup_compute_payload_crc(data, len);
+ if (payload_crc != payload_crc_computed)
+ goto payload_crc_err;
+ break;
+ case IUUP_PDU_T_CONTROL:
+ t14h = (struct iuup_pdutype14_hdr *) data;
+ if (t14h->ack_nack == IUUP_AN_PROCEDURE) {
+ payload_crc = ((uint16_t)t14h->payload_crc_hi << 8) | t14h->payload_crc_lo;
+ payload_crc_computed = osmo_iuup_compute_payload_crc(data, len);
+ if (payload_crc != payload_crc_computed)
+ goto payload_crc_err;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+
+payload_crc_err:
+ LOGP(DLIUUP, LOGL_NOTICE, "Payload Checksum error (pdu type %u): rx 0x%02x vs exp 0x%02x\n",
+ pdu_type, payload_crc, payload_crc_computed);
+ return -EIO;
+}
+
+/* A IuUP TNL SAP primitive from transport (lower layer) */
+int osmo_iuup_tnl_prim_up(struct osmo_iuup_instance *inst, struct osmo_iuup_tnl_prim *itp)
+{
+ struct osmo_prim_hdr *oph = &itp->oph;
+ struct iuup_pdutype14_hdr *t14h;
+ int rc = 0;
+
+ OSMO_ASSERT(oph->sap == SAP_IUUP_TNL);
+
+ switch (OSMO_PRIM_HDR(oph)) {
+ case OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION):
+ if (iuup_verify_pdu(msgb_l2(oph->msg), msgb_l2len(oph->msg)) < 0) {
+ LOGPFSML(inst->fi, LOGL_NOTICE, "Discarding invalid IuUP PDU: %s\n",
+ osmo_hexdump((const unsigned char *) msgb_l2(oph->msg), msgb_l2len(oph->msg)));
+ /* don't return error as the caller is not responsible for the PDU which
+ * was transmitted from some remote peer */
+ return 0;
+ }
+ switch (iuup_get_pdu_type(msgb_l2(oph->msg))) {
+ case IUUP_PDU_T_DATA_CRC:
+ oph->msg->l3h = msgb_l2(oph->msg) + sizeof(struct iuup_pdutype0_hdr);
+ rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_DATA_IND, itp);
+ break;
+ case IUUP_PDU_T_DATA_NOCRC:
+ oph->msg->l3h = msgb_l2(oph->msg) + sizeof(struct iuup_pdutype1_hdr);
+ rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_DATA_IND, itp);
+ break;
+ case IUUP_PDU_T_CONTROL:
+ t14h = (struct iuup_pdutype14_hdr *) msgb_l2(oph->msg);
+ switch (t14h->ack_nack) {
+ case IUUP_AN_PROCEDURE:
+ switch (t14h->proc_ind) {
+ case IUUP_PROC_INIT:
+ rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_INIT, itp);
+ break;
+ case IUUP_PROC_RATE_CTRL:
+ case IUUP_PROC_TIME_ALIGN:
+ case IUUP_PROC_ERR_EVENT:
+ LOGPFSML(inst->fi, LOGL_NOTICE, "Received Request for "
+ "unsupported IuUP procedure %u\n", t14h->proc_ind);
+ break;
+ default:
+ LOGPFSML(inst->fi, LOGL_NOTICE, "Received Request for "
+ "unknown IuUP procedure %u\n", t14h->proc_ind);
+ break;
+ }
+ break;
+ case IUUP_AN_ACK:
+ switch (t14h->proc_ind) {
+ case IUUP_PROC_INIT:
+ rc = osmo_fsm_inst_dispatch(inst->fi,
+ IUUP_FSM_EVT_LAST_INIT_ACK, itp);
+ break;
+ default:
+ LOGPFSML(inst->fi, LOGL_ERROR, "Received ACK for "
+ "unknown IuUP procedure %u\n", t14h->proc_ind);
+ break;
+ }
+ break;
+ case IUUP_AN_NACK:
+ switch (t14h->proc_ind) {
+ case IUUP_PROC_INIT:
+ rc = osmo_fsm_inst_dispatch(inst->fi,
+ IUUP_FSM_EVT_INIT_NACK, itp);
+ break;
+ default:
+ LOGPFSML(inst->fi, LOGL_ERROR, "Received NACK for "
+ "unknown IuUP procedure %u\n", t14h->proc_ind);
+ break;
+ }
+ break;
+ default:
+ LOGPFSML(inst->fi, LOGL_ERROR, "Received unknown IuUP ACK/NACK\n");
+ break;
+ }
+ break;
+ default:
+ LOGPFSML(inst->fi, LOGL_NOTICE, "Received unknown IuUP PDU type %u\n",
+ iuup_get_pdu_type(msgb_l2(oph->msg)));
+ break;
+ }
+ break;
+ default:
+ /* exception: return an error code due to a wrong primitive */
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+/* A IuUP RNL SAP primitive from user (higher layer) */
+int osmo_iuup_rnl_prim_down(struct osmo_iuup_instance *inst, struct osmo_iuup_rnl_prim *irp)
+{
+ struct osmo_prim_hdr *oph = &irp->oph;
+ int rc;
+
+ OSMO_ASSERT(oph->sap == SAP_IUUP_RNL);
+
+ switch (OSMO_PRIM_HDR(oph)) {
+ case OSMO_PRIM(OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST):
+ rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_CONFIG_REQ, irp);
+ msgb_free(irp->oph.msg);
+ break;
+ case OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST):
+ rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_DATA_REQ, irp);
+ if (rc != 0)
+ msgb_free(irp->oph.msg);
+ break;
+ case OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_REQUEST):
+ rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_STATUS_REQ, irp);
+ msgb_free(irp->oph.msg);
+ break;
+ default:
+ rc = -EINVAL;
+ msgb_free(irp->oph.msg);
+ }
+ return rc;
+}
+
+struct osmo_iuup_instance *osmo_iuup_instance_alloc(void *ctx, const char *id)
+{
+ struct osmo_iuup_instance *iui;
+ iui = talloc_zero(ctx, struct osmo_iuup_instance);
+ if (!iui)
+ return NULL;
+
+ iui->fi = osmo_fsm_inst_alloc(&iuup_fsm, NULL, iui, LOGL_DEBUG, id);
+ if (!iui->fi)
+ goto free_ret;
+
+ return iui;
+free_ret:
+ talloc_free(iui);
+ return NULL;
+}
+
+void osmo_iuup_instance_free(struct osmo_iuup_instance *iui)
+{
+ if (!iui)
+ return;
+
+ if (iui->fi)
+ osmo_fsm_inst_free(iui->fi);
+ iui->fi = NULL;
+ talloc_free(iui);
+}
+
+void osmo_iuup_instance_set_user_prim_cb(struct osmo_iuup_instance *iui, osmo_prim_cb func, void *priv)
+{
+ iui->user_prim_cb = func;
+ iui->user_prim_priv = priv;
+}
+void osmo_iuup_instance_set_transport_prim_cb(struct osmo_iuup_instance *iui, osmo_prim_cb func, void *priv)
+{
+ iui->transport_prim_cb = func;
+ iui->transport_prim_priv = priv;
+}
+
+static __attribute__((constructor)) void on_dso_load_iuup_fsm(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&iuup_fsm) == 0);
+}
diff --git a/src/gsm/kasumi.c b/src/gsm/kasumi.c
index f93c002b..78885bdd 100644
--- a/src/gsm/kasumi.c
+++ b/src/gsm/kasumi.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
diff --git a/src/gsm/kdf.c b/src/gsm/kdf.c
new file mode 100644
index 00000000..4113aada
--- /dev/null
+++ b/src/gsm/kdf.c
@@ -0,0 +1,163 @@
+/*
+ * (C) 2021 by sysmocom s.f.m.c. GmbH
+ *
+ * Author: Eric Wild <ewild@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "config.h"
+#if (USE_GNUTLS)
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#define HMAC_FUNC(k,lk,s,sl,out) gnutls_hmac_fast(GNUTLS_MAC_SHA256,k,lk,s,sl,out)
+#else
+#include <osmocom/crypt/kdf.h>
+#define HMAC_FUNC(k,lk,s,sl,out) hmac_sha256(k,lk,s,sl,out)
+#endif
+
+#include <osmocom/core/bit32gen.h>
+#include <osmocom/crypt/kdf.h>
+
+#include "kdf/common.h"
+#include "kdf/sha256.h"
+
+
+#if (USE_GNUTLS)
+/* gnutls < 3.3.0 requires global init.
+ * gnutls >= 3.3.0 does it automatic.
+ * It doesn't hurt calling it twice,
+ * as long it's not done at the same time (threads).
+ */
+__attribute__((constructor))
+static void on_dso_load_gnutls(void)
+{
+ if (!gnutls_check_version("3.3.0"))
+ gnutls_global_init();
+}
+
+__attribute__((destructor))
+static void on_dso_unload_gnutls(void)
+{
+ if (!gnutls_check_version("3.3.0"))
+ gnutls_global_deinit();
+}
+#endif
+
+/*
+ * This file uses the generic key derivation function defined in 3GPP TS 33.220 Annex B
+ *
+ * The S parameter always consists of concatenated values FC | P0 | L0 | Pi | Li | ...
+ * with Pi = Parameter number i and Li = Length of Pi (two octets)
+ *
+ * FC is either a single octet or two octets 0xff | FC
+ * FC values ranges depend on the specification parts that use the KDF,
+ * they are defined in 3GPP TS 33.220 Annex B.2.2
+ *
+ */
+
+/*! \addtogroup kdf
+ * @{
+ * key derivation functions
+ *
+ * \file kdf.c */
+
+/* 3GPP TS 33.102 B.5 */
+void osmo_kdf_kc128(const uint8_t* ck, const uint8_t* ik, uint8_t* kc128) {
+ uint8_t k[16*2];
+ uint8_t s[1];
+ uint8_t out_tmp256[32];
+ memcpy (&k[0], ck, 16);
+ memcpy (&k[16], ik, 16);
+
+ s[0] = 0x32; // yeah, really just one FC byte..
+
+ HMAC_FUNC(k, 32, s, 1, out_tmp256);
+ memcpy(kc128, out_tmp256, 16);
+}
+
+/* 3GPP TS 33.401 A.2 */
+void osmo_kdf_kasme(const uint8_t *ck, const uint8_t *ik, const uint8_t* plmn_id,
+ const uint8_t *sqn, const uint8_t *ak, uint8_t *kasme)
+{
+ uint8_t s[14];
+ uint8_t k[16*2];
+ int i;
+
+ memcpy(&k[0], ck, 16);
+ memcpy(&k[16], ik, 16);
+
+ s[0] = 0x10;
+ memcpy(&s[1], plmn_id, 3);
+ s[4] = 0x00;
+ s[5] = 0x03;
+
+ for (i = 0; i < 6; i++)
+ s[6+i] = sqn[i] ^ ak[i];
+ s[12] = 0x00;
+ s[13] = 0x06;
+
+ HMAC_FUNC(k, 32, s, 14, kasme);
+}
+
+/* 3GPP TS 33.401 A.3 */
+void osmo_kdf_enb(const uint8_t *kasme, uint32_t ul_count, uint8_t *kenb)
+{
+ uint8_t s[7];
+
+ s[0] = 0x11;
+ osmo_store32be(ul_count, &s[1]);
+ s[5] = 0x00;
+ s[6] = 0x04;
+
+ HMAC_FUNC(kasme, 32, s, 7, kenb);
+}
+
+/* 3GPP TS 33.401 A.4 */
+void osmo_kdf_nh(const uint8_t *kasme, const uint8_t *sync_input, uint8_t *nh)
+{
+ uint8_t s[35];
+
+ s[0] = 0x12;
+ memcpy(s+1, sync_input, 32);
+ s[33] = 0x00;
+ s[34] = 0x20;
+
+ HMAC_FUNC(kasme, 32, s, 35, nh);
+}
+
+/* 3GPP TS 33.401 A.7 */
+void osmo_kdf_nas(uint8_t algo_type, uint8_t algo_id, const uint8_t *kasme, uint8_t *knas)
+{
+ uint8_t s[7];
+ uint8_t out[32];
+
+ s[0] = 0x15;
+ s[1] = algo_type;
+ s[2] = 0x00;
+ s[3] = 0x01;
+ s[4] = algo_id;
+ s[5] = 0x00;
+ s[6] = 0x01;
+
+ HMAC_FUNC(kasme, 32, s, 7, out);
+ memcpy(knas, out+16, 16);
+}
+
+/*! @} */
diff --git a/src/gsm/kdf/common.h b/src/gsm/kdf/common.h
new file mode 100644
index 00000000..c1ef17ef
--- /dev/null
+++ b/src/gsm/kdf/common.h
@@ -0,0 +1,101 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define CONFIG_CRYPTO_INTERNAL
+#define TEST_FAIL() 0
+
+#define MSG_DEBUG
+#define wpa_hexdump(x, args...)
+#define wpa_hexdump_key(x, args...)
+#define wpa_printf(x, args...)
+
+#define os_memcpy(x, y, z) memcpy(x, y, z)
+#define os_memcmp(x, y, z) memcmp(x, y, z)
+#define os_memset(x, y, z) memset(x, y, z)
+#define os_malloc(x) malloc(x)
+#define os_free(x) free(x)
+#define os_strlen(x) strlen(x)
+
+#define forced_memzero(ptr, len) memset(ptr, 0, len);
+
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int64_t s64;
+typedef int32_t s32;
+typedef int16_t s16;
+typedef int8_t s8;
+
+
+/* Macros for handling unaligned memory accesses */
+
+#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1]))
+#define WPA_PUT_BE16(a, val) \
+ do { \
+ (a)[0] = ((u16) (val)) >> 8; \
+ (a)[1] = ((u16) (val)) & 0xff; \
+ } while (0)
+
+#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0]))
+#define WPA_PUT_LE16(a, val) \
+ do { \
+ (a)[1] = ((u16) (val)) >> 8; \
+ (a)[0] = ((u16) (val)) & 0xff; \
+ } while (0)
+
+#define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \
+ ((u32) (a)[2]))
+#define WPA_PUT_BE24(a, val) \
+ do { \
+ (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff); \
+ (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \
+ (a)[2] = (u8) (((u32) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \
+ (((u32) (a)[2]) << 8) | ((u32) (a)[3]))
+#define WPA_PUT_BE32(a, val) \
+ do { \
+ (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \
+ (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \
+ (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \
+ (a)[3] = (u8) (((u32) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \
+ (((u32) (a)[1]) << 8) | ((u32) (a)[0]))
+#define WPA_PUT_LE32(a, val) \
+ do { \
+ (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff); \
+ (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff); \
+ (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \
+ (a)[0] = (u8) (((u32) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \
+ (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \
+ (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \
+ (((u64) (a)[6]) << 8) | ((u64) (a)[7]))
+#define WPA_PUT_BE64(a, val) \
+ do { \
+ (a)[0] = (u8) (((u64) (val)) >> 56); \
+ (a)[1] = (u8) (((u64) (val)) >> 48); \
+ (a)[2] = (u8) (((u64) (val)) >> 40); \
+ (a)[3] = (u8) (((u64) (val)) >> 32); \
+ (a)[4] = (u8) (((u64) (val)) >> 24); \
+ (a)[5] = (u8) (((u64) (val)) >> 16); \
+ (a)[6] = (u8) (((u64) (val)) >> 8); \
+ (a)[7] = (u8) (((u64) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \
+ (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \
+ (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \
+ (((u64) (a)[1]) << 8) | ((u64) (a)[0]))
+
+
+#define __must_check
diff --git a/src/gsm/kdf/crypto.h b/src/gsm/kdf/crypto.h
new file mode 100644
index 00000000..6dca191c
--- /dev/null
+++ b/src/gsm/kdf/crypto.h
@@ -0,0 +1,470 @@
+/*
+ * WPA Supplicant / wrapper functions for crypto libraries
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file defines the cryptographic functions that need to be implemented
+ * for wpa_supplicant and hostapd. When TLS is not used, internal
+ * implementation of MD5, SHA1, and AES is used and no external libraries are
+ * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the
+ * crypto library used by the TLS implementation is expected to be used for
+ * non-TLS needs, too, in order to save space by not implementing these
+ * functions twice.
+ *
+ * Wrapper code for using each crypto library is in its own file (crypto*.c)
+ * and one of these files is build and linked in to provide the functions
+ * defined here.
+ */
+
+#ifndef CRYPTO_H
+#define CRYPTO_H
+
+/**
+ * md4_vector - MD4 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+#ifdef CONFIG_FIPS
+/**
+ * md5_vector_non_fips_allow - MD5 hash for data vector (non-FIPS use allowed)
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[],
+ const size_t *len, u8 *mac);
+#else /* CONFIG_FIPS */
+#define md5_vector_non_fips_allow md5_vector
+#endif /* CONFIG_FIPS */
+
+
+/**
+ * sha1_vector - SHA-1 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+
+/**
+ * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF
+ * @seed: Seed/key for the PRF
+ * @seed_len: Seed length in bytes
+ * @x: Buffer for PRF output
+ * @xlen: Output length in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function implements random number generation specified in NIST FIPS
+ * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to
+ * SHA-1, but has different message padding.
+ */
+int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x,
+ size_t xlen);
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+
+/**
+ * des_encrypt - Encrypt one block with DES
+ * @clear: 8 octets (in)
+ * @key: 7 octets (in) (no parity bits included)
+ * @cypher: 8 octets (out)
+ */
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
+
+/**
+ * aes_encrypt_init - Initialize AES for encryption
+ * @key: Encryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_encrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_encrypt - Encrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @plain: Plaintext data to be encrypted (16 bytes)
+ * @crypt: Buffer for the encrypted data (16 bytes)
+ */
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+
+/**
+ * aes_encrypt_deinit - Deinitialize AES encryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_encrypt_deinit(void *ctx);
+
+/**
+ * aes_decrypt_init - Initialize AES for decryption
+ * @key: Decryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_decrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_decrypt - Decrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @crypt: Encrypted data (16 bytes)
+ * @plain: Buffer for the decrypted data (16 bytes)
+ */
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+
+/**
+ * aes_decrypt_deinit - Deinitialize AES decryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_decrypt_deinit(void *ctx);
+
+
+enum crypto_hash_alg {
+ CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
+ CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1,
+ CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256
+};
+
+struct crypto_hash;
+
+/**
+ * crypto_hash_init - Initialize hash/HMAC function
+ * @alg: Hash algorithm
+ * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed
+ * @key_len: Length of the key in bytes
+ * Returns: Pointer to hash context to use with other hash functions or %NULL
+ * on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+ size_t key_len);
+
+/**
+ * crypto_hash_update - Add data to hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @data: Data buffer to add
+ * @len: Length of the buffer
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len);
+
+/**
+ * crypto_hash_finish - Complete hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @hash: Buffer for hash value or %NULL if caller is just freeing the hash
+ * context
+ * @len: Pointer to length of the buffer or %NULL if caller is just freeing the
+ * hash context; on return, this is set to the actual length of the hash value
+ * Returns: 0 on success, -1 if buffer is too small (len set to needed length),
+ * or -2 on other failures (including failed crypto_hash_update() operations)
+ *
+ * This function calculates the hash value and frees the context buffer that
+ * was used for hash calculation.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len);
+
+
+enum crypto_cipher_alg {
+ CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES,
+ CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4
+};
+
+struct crypto_cipher;
+
+/**
+ * crypto_cipher_init - Initialize block/stream cipher function
+ * @alg: Cipher algorithm
+ * @iv: Initialization vector for block ciphers or %NULL for stream ciphers
+ * @key: Cipher key
+ * @key_len: Length of key in bytes
+ * Returns: Pointer to cipher context to use with other cipher functions or
+ * %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len);
+
+/**
+ * crypto_cipher_encrypt - Cipher encrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @plain: Plaintext to cipher
+ * @crypt: Resulting ciphertext
+ * @len: Length of the plaintext
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx,
+ const u8 *plain, u8 *crypt, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Cipher decrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @crypt: Ciphertext to decrypt
+ * @plain: Resulting plaintext
+ * @len: Length of the cipher text
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx,
+ const u8 *crypt, u8 *plain, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Free cipher context
+ * @ctx: Context pointer from crypto_cipher_init()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_cipher_deinit(struct crypto_cipher *ctx);
+
+
+struct crypto_public_key;
+struct crypto_private_key;
+
+/**
+ * crypto_public_key_import - Import an RSA public key
+ * @key: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library supports X.509
+ * parsing. In that case, crypto_public_key_from_cert() is used to import the
+ * public key from a certificate.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len);
+
+/**
+ * crypto_private_key_import - Import an RSA private key
+ * @key: Key buffer (DER encoded RSA private key)
+ * @len: Key buffer length in bytes
+ * @passwd: Key encryption password or %NULL if key is not encrypted
+ * Returns: Pointer to the private key or %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+ size_t len,
+ const char *passwd);
+
+/**
+ * crypto_public_key_from_cert - Import an RSA public key from a certificate
+ * @buf: DER encoded X.509 certificate
+ * @len: Certificate buffer length in bytes
+ * Returns: Pointer to public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library does not support
+ * X.509 parsing. In that case, internal code will be used to parse the
+ * certificate and public key is imported using crypto_public_key_import().
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+ size_t len);
+
+/**
+ * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5)
+ * @key: Public key
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_public_key_encrypt_pkcs1_v15(
+ struct crypto_public_key *key, const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5)
+ * @key: Private key
+ * @in: Encrypted buffer
+ * @inlen: Length of encrypted buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_decrypt_pkcs1_v15(
+ struct crypto_private_key *key, const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1)
+ * @key: Private key from crypto_private_key_import()
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted (signed) data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_public_key_free - Free public key
+ * @key: Public key
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_public_key_free(struct crypto_public_key *key);
+
+/**
+ * crypto_private_key_free - Free private key
+ * @key: Private key from crypto_private_key_import()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_private_key_free(struct crypto_private_key *key);
+
+/**
+ * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature
+ * @key: Public key
+ * @crypt: Encrypted signature data (using the private key)
+ * @crypt_len: Encrypted signature data length
+ * @plain: Buffer for plaintext (at least crypt_len bytes)
+ * @plain_len: Plaintext length (max buffer size on input, real len on output);
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check crypto_public_key_decrypt_pkcs1(
+ struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
+ u8 *plain, size_t *plain_len);
+
+/**
+ * crypto_global_init - Initialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_global_init(void);
+
+/**
+ * crypto_global_deinit - Deinitialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_global_deinit(void);
+
+/**
+ * crypto_mod_exp - Modular exponentiation of large integers
+ * @base: Base integer (big endian byte array)
+ * @base_len: Length of base integer in bytes
+ * @power: Power integer (big endian byte array)
+ * @power_len: Length of power integer in bytes
+ * @modulus: Modulus integer (big endian byte array)
+ * @modulus_len: Length of modulus integer in bytes
+ * @result: Buffer for the result
+ * @result_len: Result length (max buffer size on input, real len on output)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function calculates result = base ^ power mod modulus. modules_len is
+ * used as the maximum size of modulus buffer. It is set to the used size on
+ * success.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len);
+
+/**
+ * rc4_skip - XOR RC4 stream to given data with skip-stream-start
+ * @key: RC4 key
+ * @keylen: RC4 key length
+ * @skip: number of bytes to skip from the beginning of the RC4 stream
+ * @data: data to be XOR'ed with RC4 stream
+ * @data_len: buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Generate RC4 pseudo random stream for the given key, skip beginning of the
+ * stream, and XOR the end result with the data buffer to perform RC4
+ * encryption/decryption.
+ */
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+ u8 *data, size_t data_len);
+
+#endif /* CRYPTO_H */
diff --git a/src/gsm/kdf/sha1-internal.c b/src/gsm/kdf/sha1-internal.c
new file mode 100644
index 00000000..2216b439
--- /dev/null
+++ b/src/gsm/kdf/sha1-internal.c
@@ -0,0 +1,307 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+
+#include "common.h"
+#include "sha1.h"
+#include "sha1_i.h"
+//#include "md5.h"
+#include "crypto.h"
+
+typedef struct SHA1Context SHA1_CTX;
+
+void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
+
+
+/**
+ * sha1_vector - SHA-1 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ SHA1_CTX ctx;
+ size_t i;
+
+ SHA1Init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ SHA1Update(&ctx, addr[i], len[i]);
+ SHA1Final(mac, &ctx);
+ return 0;
+}
+
+
+/* ===== start - public domain SHA1 implementation ===== */
+
+/*
+SHA-1 in C
+By Steve Reid <sreid@sea-to-sky.net>
+100% Public Domain
+
+-----------------
+Modified 7/98
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+Corrected a problem which generated improper hash values on 16 bit machines
+Routine SHA1Update changed from
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
+len)
+to
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
+long len)
+
+The 'len' parameter was declared an int which works fine on 32 bit machines.
+However, on 16 bit machines an int is too small for the shifts being done
+against
+it. This caused the hash function to generate incorrect values if len was
+greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or larger would
+be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+"a"s).
+
+I also changed the declaration of variables i & j in SHA1Update to
+unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit implementations since
+an
+int and a long are the same size in those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments containing 'JHB'
+-----------------
+Modified 8/98
+By Steve Reid <sreid@sea-to-sky.net>
+Still 100% public domain
+
+1- Removed #include <process.h> and used return() instead of exit()
+2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
+
+-----------------
+Modified 4/01
+By Saul Kravitz <Saul.Kravitz@celera.com>
+Still 100% PD
+Modified to run on Compaq Alpha hardware.
+
+-----------------
+Modified 4/01
+By Jouni Malinen <j@w1.fi>
+Minor changes to match the coding style used in Dynamics.
+
+Modified September 24, 2004
+By Jouni Malinen <j@w1.fi>
+Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined.
+
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+#define SHA1HANDSOFF
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef WORDS_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \
+ (rol(block->l[i], 8) & 0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \
+ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) \
+ z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R1(v,w,x,y,z,i) \
+ z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R2(v,w,x,y,z,i) \
+ z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30);
+#define R3(v,w,x,y,z,i) \
+ z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+ w = rol(w, 30);
+#define R4(v,w,x,y,z,i) \
+ z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
+ w=rol(w, 30);
+
+
+#ifdef VERBOSE /* SAK */
+void SHAPrintContext(SHA1_CTX *context, char *msg)
+{
+ printf("%s (%d,%d) %x %x %x %x %x\n",
+ msg,
+ context->count[0], context->count[1],
+ context->state[0],
+ context->state[1],
+ context->state[2],
+ context->state[3],
+ context->state[4]);
+}
+#endif
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(u32 state[5], const unsigned char buffer[64])
+{
+ u32 a, b, c, d, e;
+ typedef union {
+ unsigned char c[64];
+ u32 l[16];
+ } CHAR64LONG16;
+ CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+ CHAR64LONG16 workspace;
+ block = &workspace;
+ os_memcpy(block, buffer, 64);
+#else
+ block = (CHAR64LONG16 *) buffer;
+#endif
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+#ifdef SHA1HANDSOFF
+ os_memset(block, 0, 64);
+#endif
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const void *_data, u32 len)
+{
+ u32 i, j;
+ const unsigned char *data = _data;
+
+#ifdef VERBOSE
+ SHAPrintContext(context, "before");
+#endif
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3))
+ context->count[1]++;
+ context->count[1] += (len >> 29);
+ if ((j + len) > 63) {
+ os_memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ os_memcpy(&context->buffer[j], &data[i], len - i);
+#ifdef VERBOSE
+ SHAPrintContext(context, "after ");
+#endif
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+{
+ u32 i;
+ unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)
+ ((context->count[(i >= 4 ? 0 : 1)] >>
+ ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+ SHA1Update(context, (unsigned char *) "\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ SHA1Update(context, (unsigned char *) "\0", 1);
+ }
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform()
+ */
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) &
+ 255);
+ }
+ /* Wipe variables */
+ i = 0;
+ os_memset(context->buffer, 0, 64);
+ os_memset(context->state, 0, 20);
+ os_memset(context->count, 0, 8);
+ os_memset(finalcount, 0, 8);
+}
+
+/* ===== end - public domain SHA1 implementation ===== */
diff --git a/src/gsm/kdf/sha1.c b/src/gsm/kdf/sha1.c
new file mode 100644
index 00000000..0d39f77b
--- /dev/null
+++ b/src/gsm/kdf/sha1.c
@@ -0,0 +1,162 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+
+#include "common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
+ unsigned char tk[20];
+ const u8 *_addr[6];
+ size_t _len[6], i;
+
+ if (num_elem > 5) {
+ /*
+ * Fixed limit on the number of fragments to avoid having to
+ * allocate memory (which could fail).
+ */
+ return -1;
+ }
+
+ /* if key is longer than 64 bytes reset it to key = SHA1(key) */
+ if (key_len > 64) {
+ if (sha1_vector(1, &key, &key_len, tk))
+ return -1;
+ key = tk;
+ key_len = 20;
+ }
+
+ /* the HMAC_SHA1 transform looks like:
+ *
+ * SHA1(K XOR opad, SHA1(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected */
+
+ /* start out by storing key in ipad */
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with ipad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x36;
+
+ /* perform inner SHA1 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ for (i = 0; i < num_elem; i++) {
+ _addr[i + 1] = addr[i];
+ _len[i + 1] = len[i];
+ }
+ if (sha1_vector(1 + num_elem, _addr, _len, mac))
+ return -1;
+
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with opad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x5c;
+
+ /* perform outer SHA1 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ _addr[1] = mac;
+ _len[1] = SHA1_MAC_LEN;
+ return sha1_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 of failure
+ */
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+/**
+ * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key (e.g., PMK in IEEE 802.11i).
+ */
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+ u8 counter = 0;
+ size_t pos, plen;
+ u8 hash[SHA1_MAC_LEN];
+ size_t label_len = os_strlen(label) + 1;
+ const unsigned char *addr[3];
+ size_t len[3];
+
+ addr[0] = (u8 *) label;
+ len[0] = label_len;
+ addr[1] = data;
+ len[1] = data_len;
+ addr[2] = &counter;
+ len[2] = 1;
+
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ if (plen >= SHA1_MAC_LEN) {
+ if (hmac_sha1_vector(key, key_len, 3, addr, len,
+ &buf[pos]))
+ return -1;
+ pos += SHA1_MAC_LEN;
+ } else {
+ if (hmac_sha1_vector(key, key_len, 3, addr, len,
+ hash))
+ return -1;
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ counter++;
+ }
+
+ return 0;
+}
diff --git a/src/gsm/kdf/sha1.h b/src/gsm/kdf/sha1.h
new file mode 100644
index 00000000..f0c1a5f9
--- /dev/null
+++ b/src/gsm/kdf/sha1.h
@@ -0,0 +1,33 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef SHA1_H
+#define SHA1_H
+
+#define SHA1_MAC_LEN 20
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac);
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len);
+int __must_check tls_prf_sha1_md5(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed,
+ size_t seed_len, u8 *out, size_t outlen);
+int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen);
+#endif /* SHA1_H */
diff --git a/src/gsm/kdf/sha1_i.h b/src/gsm/kdf/sha1_i.h
new file mode 100644
index 00000000..ec2f82f7
--- /dev/null
+++ b/src/gsm/kdf/sha1_i.h
@@ -0,0 +1,29 @@
+/*
+ * SHA1 internal definitions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef SHA1_I_H
+#define SHA1_I_H
+
+struct SHA1Context {
+ u32 state[5];
+ u32 count[2];
+ unsigned char buffer[64];
+};
+
+void SHA1Init(struct SHA1Context *context);
+void SHA1Update(struct SHA1Context *context, const void *data, u32 len);
+void SHA1Final(unsigned char digest[20], struct SHA1Context *context);
+void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
+
+#endif /* SHA1_I_H */
diff --git a/src/gsm/kdf/sha256-internal.c b/src/gsm/kdf/sha256-internal.c
new file mode 100644
index 00000000..18cc8f80
--- /dev/null
+++ b/src/gsm/kdf/sha256-internal.c
@@ -0,0 +1,231 @@
+/*
+ * SHA-256 hash implementation and interface functions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+
+#include "common.h"
+#include "sha256.h"
+#include "sha256_i.h"
+#include "crypto.h"
+
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ struct sha256_state ctx;
+ size_t i;
+
+ sha256_init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ if (sha256_process(&ctx, addr[i], len[i]))
+ return -1;
+ if (sha256_done(&ctx, mac))
+ return -1;
+ return 0;
+}
+
+
+/* ===== start - public domain SHA256 implementation ===== */
+
+/* This is based on SHA256 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+/* the K array */
+static const unsigned long K[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+ 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+ 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+ 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+ 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+ 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+ 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+ 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+ 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+ 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+
+/* Various logical functions */
+#define RORc(x, y) \
+( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \
+ ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define S(x, n) RORc((x), (n))
+#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+/* compress 512-bits */
+static int sha256_compress(struct sha256_state *md, unsigned char *buf)
+{
+ u32 S[8], W[64], t0, t1;
+ u32 t;
+ int i;
+
+ /* copy state into S */
+ for (i = 0; i < 8; i++) {
+ S[i] = md->state[i];
+ }
+
+ /* copy the state into 512-bits into W[0..15] */
+ for (i = 0; i < 16; i++)
+ W[i] = WPA_GET_BE32(buf + (4 * i));
+
+ /* fill W[16..63] */
+ for (i = 16; i < 64; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
+ W[i - 16];
+ }
+
+ /* Compress */
+#define RND(a,b,c,d,e,f,g,h,i) \
+ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
+ t1 = Sigma0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1;
+
+ for (i = 0; i < 64; ++i) {
+ RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i);
+ t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
+ S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+ }
+
+ /* feedback */
+ for (i = 0; i < 8; i++) {
+ md->state[i] = md->state[i] + S[i];
+ }
+ return 0;
+}
+
+
+/* Initialize the hash state */
+void sha256_init(struct sha256_state *md)
+{
+ md->curlen = 0;
+ md->length = 0;
+ md->state[0] = 0x6A09E667UL;
+ md->state[1] = 0xBB67AE85UL;
+ md->state[2] = 0x3C6EF372UL;
+ md->state[3] = 0xA54FF53AUL;
+ md->state[4] = 0x510E527FUL;
+ md->state[5] = 0x9B05688CUL;
+ md->state[6] = 0x1F83D9ABUL;
+ md->state[7] = 0x5BE0CD19UL;
+}
+
+/**
+ Process a block of memory though the hash
+ @param md The hash state
+ @param in The data to hash
+ @param inlen The length of the data (octets)
+ @return CRYPT_OK if successful
+*/
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+ unsigned long inlen)
+{
+ unsigned long n;
+
+ if (md->curlen >= sizeof(md->buf))
+ return -1;
+
+ while (inlen > 0) {
+ if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) {
+ if (sha256_compress(md, (unsigned char *) in) < 0)
+ return -1;
+ md->length += SHA256_BLOCK_SIZE * 8;
+ in += SHA256_BLOCK_SIZE;
+ inlen -= SHA256_BLOCK_SIZE;
+ } else {
+ n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen));
+ os_memcpy(md->buf + md->curlen, in, n);
+ md->curlen += n;
+ in += n;
+ inlen -= n;
+ if (md->curlen == SHA256_BLOCK_SIZE) {
+ if (sha256_compress(md, md->buf) < 0)
+ return -1;
+ md->length += 8 * SHA256_BLOCK_SIZE;
+ md->curlen = 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (32 bytes)
+ @return CRYPT_OK if successful
+*/
+int sha256_done(struct sha256_state *md, unsigned char *out)
+{
+ int i;
+
+ if (md->curlen >= sizeof(md->buf))
+ return -1;
+
+ /* increase the length of the message */
+ md->length += md->curlen * 8;
+
+ /* append the '1' bit */
+ md->buf[md->curlen++] = (unsigned char) 0x80;
+
+ /* if the length is currently above 56 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (md->curlen > 56) {
+ while (md->curlen < SHA256_BLOCK_SIZE) {
+ md->buf[md->curlen++] = (unsigned char) 0;
+ }
+ sha256_compress(md, md->buf);
+ md->curlen = 0;
+ }
+
+ /* pad up to 56 bytes of zeroes */
+ while (md->curlen < 56) {
+ md->buf[md->curlen++] = (unsigned char) 0;
+ }
+
+ /* store length */
+ WPA_PUT_BE64(md->buf + 56, md->length);
+ sha256_compress(md, md->buf);
+
+ /* copy output */
+ for (i = 0; i < 8; i++)
+ WPA_PUT_BE32(out + (4 * i), md->state[i]);
+
+ return 0;
+}
+
+/* ===== end - public domain SHA256 implementation ===== */
diff --git a/src/gsm/kdf/sha256.c b/src/gsm/kdf/sha256.c
new file mode 100644
index 00000000..4f8b6cc5
--- /dev/null
+++ b/src/gsm/kdf/sha256.c
@@ -0,0 +1,156 @@
+/*
+ * SHA-256 hash implementation and interface functions
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+
+#include "common.h"
+#include "sha256.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (32 bytes)
+ */
+void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
+ unsigned char tk[32];
+ const u8 *_addr[6];
+ size_t _len[6], i;
+
+ if (num_elem > 5) {
+ /*
+ * Fixed limit on the number of fragments to avoid having to
+ * allocate memory (which could fail).
+ */
+ return;
+ }
+
+ /* if key is longer than 64 bytes reset it to key = SHA256(key) */
+ if (key_len > 64) {
+ sha256_vector(1, &key, &key_len, tk);
+ key = tk;
+ key_len = 32;
+ }
+
+ /* the HMAC_SHA256 transform looks like:
+ *
+ * SHA256(K XOR opad, SHA256(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected */
+
+ /* start out by storing key in ipad */
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with ipad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x36;
+
+ /* perform inner SHA256 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ for (i = 0; i < num_elem; i++) {
+ _addr[i + 1] = addr[i];
+ _len[i + 1] = len[i];
+ }
+ sha256_vector(1 + num_elem, _addr, _len, mac);
+
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with opad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x5c;
+
+ /* perform outer SHA256 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ _addr[1] = mac;
+ _len[1] = SHA256_MAC_LEN;
+ sha256_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha256 - HMAC-SHA256 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (20 bytes)
+ */
+void hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+/**
+ * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+ u16 counter = 1;
+ size_t pos, plen;
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+ u8 counter_le[2], length_le[2];
+
+ addr[0] = counter_le;
+ len[0] = 2;
+ addr[1] = (u8 *) label;
+ len[1] = os_strlen(label);
+ addr[2] = data;
+ len[2] = data_len;
+ addr[3] = length_le;
+ len[3] = sizeof(length_le);
+
+ WPA_PUT_LE16(length_le, buf_len * 8);
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ WPA_PUT_LE16(counter_le, counter);
+ if (plen >= SHA256_MAC_LEN) {
+ hmac_sha256_vector(key, key_len, 4, addr, len,
+ &buf[pos]);
+ pos += SHA256_MAC_LEN;
+ } else {
+ hmac_sha256_vector(key, key_len, 4, addr, len, hash);
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ counter++;
+ }
+}
diff --git a/src/gsm/kdf/sha256.h b/src/gsm/kdf/sha256.h
new file mode 100644
index 00000000..b1ce6afe
--- /dev/null
+++ b/src/gsm/kdf/sha256.h
@@ -0,0 +1,30 @@
+/*
+ * SHA256 hash implementation and interface functions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef SHA256_H
+#define SHA256_H
+
+#define SHA256_MAC_LEN 32
+
+void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+void hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac);
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+void tls_prf_sha256(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *out, size_t outlen);
+
+#endif /* SHA256_H */
diff --git a/src/gsm/kdf/sha256_i.h b/src/gsm/kdf/sha256_i.h
new file mode 100644
index 00000000..20ae4889
--- /dev/null
+++ b/src/gsm/kdf/sha256_i.h
@@ -0,0 +1,31 @@
+/*
+ * SHA-256 internal definitions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef SHA256_I_H
+#define SHA256_I_H
+
+#define SHA256_BLOCK_SIZE 64
+
+struct sha256_state {
+ u64 length;
+ u32 state[8], curlen;
+ u8 buf[SHA256_BLOCK_SIZE];
+};
+
+void sha256_init(struct sha256_state *md);
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+ unsigned long inlen);
+int sha256_done(struct sha256_state *md, unsigned char *out);
+
+#endif /* SHA256_I_H */
diff --git a/src/gsm/lapdm.c b/src/gsm/lapdm.c
index 8620cabe..1fc986d6 100644
--- a/src/gsm/lapdm.c
+++ b/src/gsm/lapdm.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup lapdm
@@ -31,6 +27,7 @@
#include <stdio.h>
#include <stdint.h>
+#include <inttypes.h>
#include <string.h>
#include <errno.h>
@@ -61,6 +58,7 @@
#define LAPDm_ADDR_SAPI(addr) (((addr) >> 2) & 0x7)
#define LAPDm_ADDR_CR(addr) (((addr) >> 1) & 0x1)
#define LAPDm_ADDR_EA(addr) ((addr) & 0x1)
+#define LAPDm_ADDR_SHORT_L2(addr) ((addr) & 0x3)
/* TS 04.06 Table 3 / Section 3.4.3 */
#define LAPDm_CTRL_I(nr, ns, p) ((((nr) & 0x7) << 5) | (((p) & 0x1) << 4) | (((ns) & 0x7) << 1))
@@ -199,9 +197,10 @@ void lapdm_entity_init3(struct lapdm_entity *le, enum lapdm_mode mode,
char name[256];
if (name_pfx) {
snprintf(name, sizeof(name), "%s[%s]", name_pfx, i == 0 ? "0" : "3");
- lapdm_dl_init(&le->datalink[i], le, t200_ms[i], n200, name);
+ lapdm_dl_init(&le->datalink[i], le, (t200_ms) ? t200_ms[i] : 0, n200, name);
} else
- lapdm_dl_init(&le->datalink[i], le, t200_ms[i], n200, NULL);
+ lapdm_dl_init(&le->datalink[i], le, (t200_ms) ? t200_ms[i] : 0, n200, NULL);
+ INIT_LLIST_HEAD(&le->datalink[i].tx_ui_queue);
}
lapdm_entity_set_mode(le, mode);
@@ -257,7 +256,7 @@ int lapdm_channel_init2(struct lapdm_channel *lc, enum lapdm_mode mode,
* \param[in] t200_ms_dcch per-SAPI array of T200 in milli-seconds for DCCH
* \param[in] t200_ms_acch per-SAPI array of T200 in milli-seconds for SACCH
* \param[in] chan_t GSM channel type (to correctly set N200)
- * \parma[in] name_pfx human-readable name (copied by function + extended with ACCH/DCCH)
+ * \param[in] name_pfx human-readable name (copied by function + extended with ACCH/DCCH)
*/
int lapdm_channel_init3(struct lapdm_channel *lc, enum lapdm_mode mode,
const int *t200_ms_dcch, const int *t200_ms_acch, enum gsm_chan_t chan_t,
@@ -298,6 +297,7 @@ void lapdm_entity_exit(struct lapdm_entity *le)
for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
dl = &le->datalink[i];
lapd_dl_exit(&dl->dl);
+ msgb_queue_free(&dl->tx_ui_queue);
}
}
@@ -361,11 +361,50 @@ static int tx_ph_data_enqueue(struct lapdm_datalink *dl, struct msgb *msg,
/* if there is a pending message, queue it */
if (le->tx_pending || le->flags & LAPDM_ENT_F_POLLING_ONLY) {
+ struct msgb *old_msg;
+
+ /* In 'RTS' mode there can be only one message. */
+ if (le->flags & LAPDM_ENT_F_RTS) {
+ /* Overwrite existing message by removing it first. */
+ if ((old_msg = msgb_dequeue(&dl->dl.tx_queue))) {
+ msgb_free(old_msg);
+ /* Reset V(S) to V(A), because there is no outstanding message now. */
+ dl->dl.v_send = dl->dl.v_ack;
+ }
+ }
+
*msgb_push(msg, 1) = pad;
*msgb_push(msg, 1) = link_id;
*msgb_push(msg, 1) = chan_nr;
msgb_enqueue(&dl->dl.tx_queue, msg);
- return -EBUSY;
+ return 0;
+ }
+
+ osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA,
+ PRIM_OP_REQUEST, msg);
+ pp.u.data.chan_nr = chan_nr;
+ pp.u.data.link_id = link_id;
+
+ /* send the frame now */
+ le->tx_pending = 0; /* disabled flow control */
+ lapdm_pad_msgb(msg, pad);
+
+ return le->l1_prim_cb(&pp.oph, le->l1_ctx);
+}
+
+static int tx_ph_data_enqueue_ui(struct lapdm_datalink *dl, struct msgb *msg,
+ uint8_t chan_nr, uint8_t link_id, uint8_t pad)
+{
+ struct lapdm_entity *le = dl->entity;
+ struct osmo_phsap_prim pp;
+
+ /* if there is a pending message, queue it */
+ if (le->tx_pending || le->flags & LAPDM_ENT_F_POLLING_ONLY) {
+ *msgb_push(msg, 1) = pad;
+ *msgb_push(msg, 1) = link_id;
+ *msgb_push(msg, 1) = chan_nr;
+ msgb_enqueue(&dl->tx_ui_queue, msg);
+ return 0;
}
osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA,
@@ -380,21 +419,60 @@ static int tx_ph_data_enqueue(struct lapdm_datalink *dl, struct msgb *msg,
return le->l1_prim_cb(&pp.oph, le->l1_ctx);
}
+/* Get transmit frame from queue, if any. In polling mode, indicate RTS to LAPD and start T200, if pending. */
+static struct msgb *tx_dequeue_msgb(struct lapdm_datalink *dl, uint32_t fn)
+{
+ struct msgb *msg;
+
+ /* Call RTS function of LAPD, to queue next frame. */
+ if (dl->entity->flags & LAPDM_ENT_F_RTS) {
+ struct lapd_msg_ctx lctx;
+ int rc;
+
+ /* Poll next frame. */
+ lctx.dl = &dl->dl;
+ rc = lapd_ph_rts_ind(&lctx);
+
+ /* If T200 has been started, calculate timeout FN. */
+ if (rc == 1) {
+ /* Set T200 in advance. */
+ dl->t200_timeout = fn;
+ ADD_MODULO(dl->t200_timeout, dl->t200_fn, GSM_MAX_FN);
+
+ LOGDL(&dl->dl, LOGL_INFO,
+ "T200 running from FN %"PRIu32" to FN %"PRIu32" (%"PRIu32" frames).\n",
+ fn, dl->t200_timeout, dl->t200_fn);
+ }
+ }
+
+ /* If there is no frame from LAPD, send UI frame, if any. */
+ msg = msgb_dequeue(&dl->dl.tx_queue);
+ if (msg)
+ LOGDL(&dl->dl, LOGL_INFO, "Sending frame from TX queue. (FN %"PRIu32")\n", fn);
+ else {
+ msg = msgb_dequeue(&dl->tx_ui_queue);
+ if (msg)
+ LOGDL(&dl->dl, LOGL_INFO, "Sending UI frame from TX queue. (FN %"PRIu32")\n", fn);
+ }
+ return msg;
+}
+
/* Dequeue a Downlink message for DCCH (dedicated channel) */
-static struct msgb *tx_dequeue_dcch_msgb(struct lapdm_entity *le)
+static struct msgb *tx_dequeue_dcch_msgb(struct lapdm_entity *le, uint32_t fn)
{
struct msgb *msg;
/* SAPI=0 always has higher priority than SAPI=3 */
- msg = msgb_dequeue(&le->datalink[DL_SAPI0].dl.tx_queue);
- if (msg == NULL) /* no SAPI=0 messages, dequeue SAPI=3 (if any) */
- msg = msgb_dequeue(&le->datalink[DL_SAPI3].dl.tx_queue);
+ msg = tx_dequeue_msgb(&le->datalink[DL_SAPI0], fn);
+ if (msg == NULL) { /* no SAPI=0 messages, dequeue SAPI=3 (if any) */
+ msg = tx_dequeue_msgb(&le->datalink[DL_SAPI3], fn);
+ }
return msg;
}
/* Dequeue a Downlink message for ACCH (associated channel) */
-static struct msgb *tx_dequeue_acch_msgb(struct lapdm_entity *le)
+static struct msgb *tx_dequeue_acch_msgb(struct lapdm_entity *le, uint32_t fn)
{
struct lapdm_datalink *dl;
int last = le->last_tx_dequeue;
@@ -406,7 +484,7 @@ static struct msgb *tx_dequeue_acch_msgb(struct lapdm_entity *le)
/* next */
i = (i + 1) % n;
dl = &le->datalink[i];
- if ((msg = msgb_dequeue(&dl->dl.tx_queue)))
+ if ((msg = tx_dequeue_msgb(dl, fn)))
break;
} while (i != last);
@@ -420,7 +498,7 @@ static struct msgb *tx_dequeue_acch_msgb(struct lapdm_entity *le)
/*! dequeue a msg that's pending transmission via L1 and wrap it into
* a osmo_phsap_prim */
-int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp)
+int lapdm_phsap_dequeue_prim_fn(struct lapdm_entity *le, struct osmo_phsap_prim *pp, uint32_t fn)
{
struct msgb *msg;
uint8_t pad;
@@ -428,9 +506,9 @@ int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp
/* Dequeue depending on channel type: DCCH or ACCH.
* See 3GPP TS 44.005, section 4.2.2 "Priority". */
if (le == &le->lapdm_ch->lapdm_dcch)
- msg = tx_dequeue_dcch_msgb(le);
+ msg = tx_dequeue_dcch_msgb(le, fn);
else
- msg = tx_dequeue_acch_msgb(le);
+ msg = tx_dequeue_acch_msgb(le, fn);
if (!msg)
return -ENODEV;
@@ -452,6 +530,57 @@ int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp
return 0;
}
+static void lapdm_t200_fn_dl(struct lapdm_datalink *dl, uint32_t fn)
+{
+ uint32_t diff;
+
+ OSMO_ASSERT((dl->dl.lapd_flags & LAPD_F_RTS));
+
+ /* If T200 is running, check if it has fired. */
+ if (dl->dl.t200_rts != LAPD_T200_RTS_RUNNING)
+ return;
+
+ /* Calculate how many frames fn is behind t200_timeout.
+ * If it is negative (>= GSM_MAX_FN / 2), we have not reached t200_timeout yet.
+ * If it is 0 or positive, we reached it or we are a bit too late, which is not a problem.
+ */
+ diff = fn;
+ ADD_MODULO(diff, GSM_MAX_FN - dl->t200_timeout, GSM_MAX_FN);
+ if (diff >= GSM_MAX_FN / 2)
+ return;
+
+ LOGDL(&dl->dl, LOGL_INFO, "T200 timeout at FN %"PRIu32", detected at FN %"PRIu32".\n", dl->t200_timeout, fn);
+
+ lapd_t200_timeout(&dl->dl);
+}
+
+/*! Get receive frame number from L1. It is used to check the T200 timeout.
+ * This function is used if LAPD is in RTS mode only. (Applies if the LAPDM_ENT_F_POLLING_ONLY flag is set.)
+ * This function must be called for every valid or invalid data frame received.
+ * The frame number fn must be the frame number of the first burst of a data frame.
+ * This function must be called after the frame is delivered to layer 2.
+ * In case of TCH, this this function must be called for every speech frame received, meaning that there was no valid
+ * data frame. */
+void lapdm_t200_fn(struct lapdm_entity *le, uint32_t fn)
+{
+ unsigned int i;
+
+ if (!(le->flags & LAPDM_ENT_F_POLLING_ONLY)) {
+ LOGP(DLLAPD, LOGL_ERROR, "Function call not allowed on timer based T200.\n");
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(le->datalink); i++)
+ lapdm_t200_fn_dl(&le->datalink[i], fn);
+}
+
+/*! dequeue a msg that's pending transmission via L1 and wrap it into
+ * a osmo_phsap_prim */
+int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp)
+{
+ return lapdm_phsap_dequeue_prim_fn(le, pp, 0);
+}
+
/* get next frame from the tx queue. because the ms has multiple datalinks,
* each datalink's queue is read round-robin.
*/
@@ -661,7 +790,8 @@ static int update_pending_frames(struct lapd_msg_ctx *lctx)
LAPDm_CTRL_PF_BIT(msg->l2h[1]));
rc = 0;
} else if (LAPDm_CTRL_is_S(msg->l2h[1])) {
- LOGDL(dl, LOGL_ERROR, "Supervisory frame in queue, this shouldn't happen\n");
+ msg->l2h[1] = LAPDm_CTRL_S(dl->v_recv, LAPDm_CTRL_S_BITS(msg->l2h[1]),
+ LAPDm_CTRL_PF_BIT(msg->l2h[1]));
}
}
@@ -703,7 +833,7 @@ static int lapdm_rx_not_permitted(const struct lapdm_entity *le,
/* input into layer2 (from layer 1) */
static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
- uint8_t chan_nr, uint8_t link_id)
+ uint8_t chan_nr, uint8_t link_id, uint32_t fn)
{
uint8_t cbits = chan_nr >> 3;
uint8_t sapi; /* we cannot take SAPI from link_id, as L1 has no clue */
@@ -718,6 +848,7 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
memset(&mctx, 0, sizeof(mctx));
mctx.chan_nr = chan_nr;
mctx.link_id = link_id;
+ mctx.fn = fn;
/* check for L1 chan_nr/link_id and determine LAPDm hdr format */
if (cbits == 0x10 || cbits == 0x12) {
@@ -729,15 +860,24 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
if (mctx.link_id & 0x40) {
/* It was received from network on SACCH */
- /* If UI on SACCH sent by BTS, lapdm_fmt must be B4 */
- if (le->mode == LAPDM_MODE_MS
- && LAPDm_CTRL_is_U(msg->l2h[3])
- && LAPDm_CTRL_U_BITS(msg->l2h[3]) == 0) {
+ /* A Short L3 header has both bits == 0. */
+ if (LAPDm_ADDR_SHORT_L2(msg->l2h[2]) == 0) {
+ mctx.lapdm_fmt = LAPDm_FMT_Bter;
+ n201 = N201_Bter_SACCH;
+ sapi = 0;
+ } else if (le->mode == LAPDM_MODE_MS
+ && LAPDm_CTRL_is_U(msg->l2h[3])
+ && LAPDm_CTRL_U_BITS(msg->l2h[3]) == 0) {
+ /* If UI on SACCH sent by BTS, lapdm_fmt must be B4 */
mctx.lapdm_fmt = LAPDm_FMT_B4;
n201 = N201_B4;
+ /* sapi is found after two-btyte L1 header */
+ sapi = (msg->l2h[2] >> 2) & 7;
} else {
mctx.lapdm_fmt = LAPDm_FMT_B;
n201 = N201_AB_SACCH;
+ /* sapi is found after two-btyte L1 header */
+ sapi = (msg->l2h[2] >> 2) & 7;
}
/* SACCH frames have a two-byte L1 header that
* OsmocomBB L1 doesn't strip */
@@ -745,11 +885,17 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
mctx.ta_ind = msg->l2h[1];
msgb_pull(msg, 2);
msg->l2h += 2;
- sapi = (msg->l2h[0] >> 2) & 7;
} else {
- mctx.lapdm_fmt = LAPDm_FMT_B;
- n201 = N201_AB_SDCCH;
- sapi = (msg->l2h[0] >> 2) & 7;
+ /* A Short L3 header has both bits == 0. */
+ if (LAPDm_ADDR_SHORT_L2(msg->l2h[0]) == 0) {
+ mctx.lapdm_fmt = LAPDm_FMT_Bter;
+ n201 = N201_Bter_SDCCH;
+ sapi = 0;
+ } else {
+ mctx.lapdm_fmt = LAPDm_FMT_B;
+ n201 = N201_AB_SDCCH;
+ sapi = (msg->l2h[0] >> 2) & 7;
+ }
}
}
@@ -839,13 +985,14 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
return -EINVAL;
}
/* send to LAPD */
+ LOGDL(lctx.dl, LOGL_DEBUG, "Frame received at FN %"PRIu32".\n", fn);
rc = lapd_ph_data_ind(msg, &lctx);
break;
case LAPDm_FMT_Bter:
- /* FIXME */
- msgb_free(msg);
- break;
+ /* fall-through */
case LAPDm_FMT_Bbis:
+ /* Update context so that users can read fields like fn: */
+ memcpy(&mctx.dl->mctx, &mctx, sizeof(mctx.dl->mctx));
/* directly pass up to layer3 */
msg->l3h = msg->l2h;
msgb_pull_to_l3(msg);
@@ -899,56 +1046,33 @@ int lapdm_phsap_up(struct osmo_prim_hdr *oph, struct lapdm_entity *le)
if (oph->sap != SAP_GSM_PH) {
LOGP(DLLAPD, LOGL_ERROR, "primitive for unknown SAP %u\n",
oph->sap);
- rc = -ENODEV;
- goto free;
+ msgb_free(oph->msg);
+ return -ENODEV;
}
- switch (oph->primitive) {
- case PRIM_PH_DATA:
- if (oph->operation != PRIM_OP_INDICATION) {
- LOGP(DLLAPD, LOGL_ERROR, "PH_DATA is not INDICATION %u\n",
- oph->operation);
- rc = -ENODEV;
- goto free;
- }
+ switch (OSMO_PRIM_HDR(oph)) {
+ case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_INDICATION):
rc = l2_ph_data_ind(oph->msg, le, pp->u.data.chan_nr,
- pp->u.data.link_id);
+ pp->u.data.link_id, pp->u.data.fn);
break;
- case PRIM_PH_RTS:
- if (oph->operation != PRIM_OP_INDICATION) {
- LOGP(DLLAPD, LOGL_ERROR, "PH_RTS is not INDICATION %u\n",
- oph->operation);
- rc = -ENODEV;
- goto free;
- }
+ case OSMO_PRIM(PRIM_PH_RTS, PRIM_OP_INDICATION):
rc = l2_ph_data_conf(oph->msg, le);
break;
- case PRIM_PH_RACH:
- switch (oph->operation) {
- case PRIM_OP_INDICATION:
- rc = l2_ph_rach_ind(le, pp->u.rach_ind.ra, pp->u.rach_ind.fn,
- pp->u.rach_ind.acc_delay);
- break;
- case PRIM_OP_CONFIRM:
- rc = l2_ph_chan_conf(oph->msg, le, pp->u.rach_ind.fn);
- break;
- default:
- rc = -EIO;
- goto free;
- }
+ case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_INDICATION):
+ rc = l2_ph_rach_ind(le, pp->u.rach_ind.ra, pp->u.rach_ind.fn,
+ pp->u.rach_ind.acc_delay);
+ break;
+ case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_CONFIRM):
+ rc = l2_ph_chan_conf(oph->msg, le, pp->u.rach_ind.fn);
break;
default:
LOGP(DLLAPD, LOGL_ERROR, "Unknown primitive %u\n",
oph->primitive);
- rc = -EINVAL;
- goto free;
+ msgb_free(oph->msg);
+ return -EINVAL;
}
return rc;
-
-free:
- msgb_free(oph->msg);
- return rc;
}
@@ -1034,6 +1158,7 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl)
uint8_t sapi = link_id & 7;
struct tlv_parsed tv;
int length, ui_bts;
+ bool use_b_ter;
if (!le) {
LOGDL(&dl->dl, LOGL_ERROR, "lapdm_datalink without entity error\n");
@@ -1059,8 +1184,10 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl)
}
msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);
length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
+ /* check for Bter frame */
+ use_b_ter = (length == ((link_id & 0x40) ? 21 : 23) && sapi == 0);
/* check if the layer3 message length exceeds N201 */
- if (length + ((link_id & 0x40) ? 4 : 2) + !ui_bts > 23) {
+ if (length + ((link_id & 0x40) ? 4 : 2) + !ui_bts > 23 && !use_b_ter) {
LOGDL(&dl->dl, LOGL_ERROR, "frame too large: %d > N201(%d) "
"(discarding)\n", length,
((link_id & 0x40) ? 18 : 20) + ui_bts);
@@ -1075,11 +1202,14 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl)
msgb_trim(msg, length);
/* Push L1 + LAPDm header on msgb */
- msg->l2h = msgb_push(msg, 2 + !ui_bts);
- msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, dl->dl.cr.loc2rem.cmd);
- msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_UI, 0);
- if (!ui_bts)
- msg->l2h[2] = LAPDm_LEN(length);
+ if (!use_b_ter) {
+ msg->l2h = msgb_push(msg, 2 + !ui_bts);
+ msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, dl->dl.cr.loc2rem.cmd);
+ msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_UI, 0);
+ if (!ui_bts)
+ msg->l2h[2] = LAPDm_LEN(length);
+ } else
+ msg->l2h = msg->data;
if (link_id & 0x40) {
msg->l2h = msgb_push(msg, 2);
msg->l2h[0] = le->tx_power;
@@ -1087,7 +1217,7 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl)
}
/* Tramsmit */
- return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, 23);
+ return tx_ph_data_enqueue_ui(dl, msg, chan_nr, link_id, 23);
}
/* L3 requests transfer of acknowledged information */
@@ -1484,6 +1614,7 @@ void lapdm_entity_reset(struct lapdm_entity *le)
for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
dl = &le->datalink[i];
lapd_dl_reset(&dl->dl);
+ msgb_queue_free(&dl->tx_ui_queue);
}
}
@@ -1497,7 +1628,22 @@ void lapdm_channel_reset(struct lapdm_channel *lc)
/*! Set the flags of a LAPDm entity */
void lapdm_entity_set_flags(struct lapdm_entity *le, unsigned int flags)
{
+ unsigned int dl_flags = 0;
+ struct lapdm_datalink *dl;
+ int i;
+
le->flags = flags;
+
+ /* Set flags at LAPD. */
+ if (le->flags & LAPDM_ENT_F_RTS)
+ dl_flags |= LAPD_F_RTS;
+ if (le->flags & LAPDM_ENT_F_DROP_2ND_REJ)
+ dl_flags |= LAPD_F_DROP_2ND_REJ;
+
+ for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
+ dl = &le->datalink[i];
+ lapd_dl_set_flags(&dl->dl, dl_flags);
+ }
}
/*! Set the flags of all LAPDm entities in a LAPDm channel */
@@ -1507,4 +1653,25 @@ void lapdm_channel_set_flags(struct lapdm_channel *lc, unsigned int flags)
lapdm_entity_set_flags(&lc->lapdm_acch, flags);
}
+/*! Set the T200 FN timer of a LAPDm entity
+ * \param[in] \ref lapdm_entity
+ * \param[in] t200_fn Array of T200 timeout in frame numbers for all SAPIs (0, 3) */
+void lapdm_entity_set_t200_fn(struct lapdm_entity *le, const uint32_t *t200_fn)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(le->datalink); i++)
+ le->datalink[i].t200_fn = t200_fn[i];
+}
+
+/*! Set the T200 FN timer of all LAPDm entities in a LAPDm channel
+ * \param[in] \ref lapdm_channel
+ * \param[in] t200_fn_dcch Array of T200 timeout in frame numbers for all SAPIs (0, 3) on SDCCH/FACCH
+ * \param[in] t200_fn_acch Array of T200 timeout in frame numbers for all SAPIs (0, 3) on SACCH */
+void lapdm_channel_set_t200_fn(struct lapdm_channel *lc, const uint32_t *t200_fn_dcch, const uint32_t *t200_fn_acch)
+{
+ lapdm_entity_set_t200_fn(&lc->lapdm_dcch, t200_fn_dcch);
+ lapdm_entity_set_t200_fn(&lc->lapdm_acch, t200_fn_acch);
+}
+
/*! @} */
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 3fd01db0..2c4c621c 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -40,6 +40,14 @@ abis_nm_put_sw_file;
abis_nm_get_sw_conf;
abis_nm_get_sw_desc_len;
+abis_nm_ipacc_freq_band_desc;
+abis_nm_ipacc_ciph_algo_desc;
+abis_nm_ipacc_chant_desc;
+abis_nm_ipacc_chanm_desc;
+abis_nm_ipacc_gprs_coding_desc;
+abis_nm_ipacc_rtp_feat_desc;
+abis_nm_ipacc_rsl_feat_desc;
+
osmo_sitype_strs;
osmo_c4;
osmo_get_rand_id;
@@ -111,6 +119,7 @@ gsm0480_gen_reject;
gsm0502_calc_paging_group;
gsm0502_fn_remap;
gsm0502_hop_seq_gen;
+gsm0502_fn2ccch_block;
gsm0503_xcch;
gsm0503_rach;
@@ -121,6 +130,11 @@ gsm0503_cs3;
gsm0503_cs2_np;
gsm0503_cs3_np;
gsm0503_tch_fr;
+gsm0503_tch_f24;
+gsm0503_tch_h24;
+gsm0503_tch_f48;
+gsm0503_tch_f96;
+gsm0503_tch_f144;
gsm0503_tch_hr;
gsm0503_tch_afs_12_2;
gsm0503_tch_afs_10_2;
@@ -170,6 +184,7 @@ gsm0808_create_ass_compl2;
gsm0808_create_assignment_failure;
gsm0808_create_ass_fail;
gsm0808_create_cipher;
+gsm0808_create_cipher2;
gsm0808_create_cipher_complete;
gsm0808_create_cipher_reject;
gsm0808_create_cipher_reject_ext;
@@ -204,6 +219,25 @@ gsm0808_create_handover_succeeded;
gsm0808_create_handover_complete;
gsm0808_create_handover_failure;
gsm0808_create_handover_performed;
+gsm0808_create_vgcs_vbs_setup;
+gsm0808_create_vgcs_vbs_setup_ack;
+gsm0808_create_vgcs_vbs_setup_refuse;
+gsm0808_create_vgcs_vbs_assign_req;
+gsm0808_create_vgcs_vbs_assign_res;
+gsm0808_create_vgcs_vbs_assign_fail;
+gsm0808_create_uplink_request;
+gsm0808_create_uplink_request_ack;
+gsm0808_create_uplink_request_cnf;
+gsm0808_create_uplink_app_data;
+gsm0808_create_uplink_release_ind;
+gsm0808_create_uplink_reject_cmd;
+gsm0808_create_uplink_release_cmd;
+gsm0808_create_uplink_seized_cmd;
+gsm0808_create_vgcs_additional_info;
+gsm0808_create_vgcs_vbs_area_cell_info;
+gsm0808_create_vgcs_vbs_assign_stat;
+gsm0808_create_vgcs_sms;
+gsm0808_create_notification_data;
gsm0808_create_common_id;
gsm0808_prepend_dtap_header;
gsm0808_enc_cause;
@@ -211,17 +245,38 @@ gsm0808_enc_aoip_trasp_addr;
gsm0808_dec_aoip_trasp_addr;
gsm0808_dec_osmux_cid;
gsm0808_enc_speech_codec;
+gsm0808_enc_speech_codec2;
gsm0808_dec_speech_codec;
gsm0808_enc_speech_codec_list;
+gsm0808_enc_speech_codec_list2;
gsm0808_dec_speech_codec_list;
gsm0808_enc_channel_type;
gsm0808_dec_channel_type;
gsm0808_enc_encrypt_info;
gsm0808_dec_encrypt_info;
+gsm0808_enc_kc128;
+gsm0808_dec_kc128;
gsm0808_enc_cell_id_list;
gsm0808_enc_cell_id_list2;
gsm0808_dec_cell_id_list;
gsm0808_dec_cell_id_list2;
+gsm0808_enc_group_callref;
+gsm0808_dec_group_callref;
+gsm0808_enc_priority;
+gsm0808_dec_priority;
+gsm0808_enc_vgcs_feature_flags;
+gsm0808_dec_vgcs_feature_flags;
+gsm0808_enc_data_identity;
+gsm0808_dec_data_identity;
+gsm0808_enc_msisdn;
+gsm0808_dec_msisdn;
+gsm0808_enc_talker_identity;
+gsm0808_dec_talker_identity;
+gsm0808_enc_assign_req;
+gsm0808_dec_assign_req;
+gsm0808_enc_cell_id_list_segment;
+gsm0808_dec_cell_id_list_segment;
+gsm0808_dec_call_id;
gsm0808_cell_id_list_add;
gsm0808_cell_id_to_list;
gsm0808_cell_id_to_cgi;
@@ -243,6 +298,8 @@ gsm0808_chan_type_to_speech_codec;
gsm0808_speech_codec_from_chan_type;
gsm0808_sc_cfg_from_gsm48_mr_cfg;
gsm48_mr_cfg_from_gsm0808_sc_cfg;
+gsm0808_amr_modes_from_cfg;
+gsm0808_amr_mode_names;
gsm0808_speech_codec_type_names;
gsm0808_permitted_speech_names;
gsm0808_chosen_enc_alg_names;
@@ -325,6 +382,9 @@ osmo_gsm48_rest_octets_si13_decode;
osmo_gsm48_rest_octets_si13_encode;
osmo_gsm48_rest_octets_si3_decode;
osmo_gsm48_rest_octets_si4_decode;
+osmo_gsm48_si1ro_nch_pos_encode;
+osmo_gsm48_si1ro_nch_pos_decode;
+gsm48_rr_short_pd_msg_name;
gsm48_rr_msg_name;
gsm48_cc_state_name;
gsm48_construct_ra;
@@ -379,10 +439,14 @@ osmo_mobile_identity_to_str_buf;
osmo_mobile_identity_to_str_c;
osmo_mobile_identity_cmp;
osmo_mobile_identity_decode;
+osmo_mobile_identity_decode_from_l3_buf;
osmo_mobile_identity_decode_from_l3;
osmo_mobile_identity_encoded_len;
osmo_mobile_identity_encode_buf;
osmo_mobile_identity_encode_msgb;
+osmo_routing_area_id_decode;
+osmo_routing_area_id_encode_buf;
+osmo_routing_area_id_encode_msgb;
gsm48_mm_att_tlvdef;
gsm48_number_of_paging_subchannels;
gsm48_parse_ra;
@@ -456,6 +520,13 @@ gsm48_pdisc_msgtype_name_buf;
gsm48_pdisc_msgtype_name_c;
gsm48_reject_value_names;
+osmo_gsm44068_msg_type_names;
+osmo_gsm44068_priority_level_names;
+osmo_gsm44068_cause_names;
+osmo_gsm44068_call_state_names;
+osmo_gsm44068_talker_priority_names;
+osmo_gsm44068_att_tlvdef;
+
gsm_7bit_decode;
gsm_7bit_decode_ussd;
gsm_7bit_encode;
@@ -481,6 +552,7 @@ gsm_gsmtime2fn;
osmo_dump_gsmtime;
osmo_dump_gsmtime_buf;
osmo_dump_gsmtime_c;
+gsm_rfn2fn;
gsm_milenage;
gsm_septet_encode;
@@ -515,7 +587,11 @@ lapdm_entity_init3;
lapdm_entity_reset;
lapdm_entity_set_flags;
lapdm_entity_set_mode;
+lapdm_entity_set_t200_fn;
+lapdm_channel_set_t200_fn;
lapdm_phsap_dequeue_prim;
+lapdm_phsap_dequeue_prim_fn;
+lapdm_t200_fn;
lapdm_phsap_up;
lapdm_rslms_recvmsg;
@@ -539,14 +615,23 @@ osmo_a5_2;
osmo_auth_alg_name;
osmo_auth_alg_parse;
osmo_auth_gen_vec;
+osmo_auth_gen_vec2;
osmo_auth_gen_vec_auts;
+osmo_auth_gen_vec_auts2;
osmo_auth_3g_from_2g;
osmo_auth_load;
osmo_auth_register;
osmo_auth_supported;
+osmo_auth_c2;
osmo_auth_c3;
osmo_sub_auth_type_names;
+osmo_kdf_kc128;
+osmo_kdf_kasme;
+osmo_kdf_enb;
+osmo_kdf_nh;
+osmo_kdf_nas;
+
osmo_rsl2sitype;
osmo_sitype2rsl;
@@ -708,6 +793,7 @@ osmo_cbsp_encode;
osmo_cbsp_decode;
osmo_cbsp_recv_buffered;
osmo_cbsp_errstr;
+osmo_cbsp_segmentation_cb;
osmo_i460_demux_in;
osmo_i460_mux_enqueue;
@@ -762,5 +848,29 @@ osmo_gad_raw_read;
osmo_gad_raw_write;
osmo_gad_type_names;
+osmo_iuup_compute_header_crc;
+osmo_iuup_compute_payload_crc;
+osmo_iuup_instance_alloc;
+osmo_iuup_instance_free;
+osmo_iuup_instance_set_user_prim_cb;
+osmo_iuup_instance_set_transport_prim_cb;
+osmo_iuup_tnl_prim_up;
+osmo_iuup_rnl_prim_down;
+osmo_iuup_rnl_prim_alloc;
+osmo_iuup_tnl_prim_alloc;
+
+osmo_csd_12k_6k_decode_frame;
+osmo_csd_12k_6k_encode_frame;
+osmo_csd_3k6_decode_frame;
+osmo_csd_3k6_encode_frame;
+osmo_csd_ubit_dump;
+
+osmo_rlp_decode;
+osmo_rlp_encode;
+osmo_rlp_fcs_compute;
+osmo_rlp_ftype_s_vals;
+osmo_rlp_ftype_u_vals;
+osmo_rlp_ftype_vals;
+
local: *;
};
diff --git a/src/gsm/milenage/milenage.c b/src/gsm/milenage/milenage.c
index 3c14ab96..ba9ab33b 100644
--- a/src/gsm/milenage/milenage.c
+++ b/src/gsm/milenage/milenage.c
@@ -244,19 +244,13 @@ int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc)
{
u8 res[8], ck[16], ik[16];
- int i;
if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL))
return -1;
osmo_auth_c3(kc, ck, ik);
+ osmo_auth_c2(sres, res, sizeof(res), 1);
-#ifdef GSM_MILENAGE_ALT_SRES
- os_memcpy(sres, res, 4);
-#else /* GSM_MILENAGE_ALT_SRES */
- for (i = 0; i < 4; i++)
- sres[i] = res[i] ^ res[i + 4];
-#endif /* GSM_MILENAGE_ALT_SRES */
return 0;
}
diff --git a/src/gsm/mncc.c b/src/gsm/mncc.c
index 938cf9a6..8a7dc4d6 100644
--- a/src/gsm/mncc.c
+++ b/src/gsm/mncc.c
@@ -20,7 +20,7 @@
*
*/
-#include "../config.h"
+#include "config.h"
#ifdef HAVE_SYS_SOCKET_H
diff --git a/src/gsm/rlp.c b/src/gsm/rlp.c
new file mode 100644
index 00000000..1e90a689
--- /dev/null
+++ b/src/gsm/rlp.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2022-2023 Harald Welte <laforge@osmocom.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*! \addtogroup rlp
+ * @{
+ * RLP (Radio Link Protocol) as per 3GPP TS 24.022
+ *
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/gsm/rlp.h>
+
+const struct value_string osmo_rlp_ftype_vals[] = {
+ { OSMO_RLP_FT_U, "U" },
+ { OSMO_RLP_FT_S, "S" },
+ { OSMO_RLP_FT_IS, "IS" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_rlp_ftype_u_vals[] = {
+ { OSMO_RLP_U_FT_SABM, "SABM" },
+ { OSMO_RLP_U_FT_UA, "UA" },
+ { OSMO_RLP_U_FT_DISC, "DISC" },
+ { OSMO_RLP_U_FT_DM, "DM" },
+ { OSMO_RLP_U_FT_NULL, "NULL" },
+ { OSMO_RLP_U_FT_UI, "UI" },
+ { OSMO_RLP_U_FT_XID, "XID" },
+ { OSMO_RLP_U_FT_TEST, "TEST" },
+ { OSMO_RLP_U_FT_REMAP, "REMAP" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_rlp_ftype_s_vals[] = {
+ { OSMO_RLP_S_FT_RR, "RR" },
+ { OSMO_RLP_S_FT_REJ, "REJ" },
+ { OSMO_RLP_S_FT_RNR, "RNR" },
+ { OSMO_RLP_S_FT_SREJ, "SREJ" },
+ { 0, NULL }
+};
+
+/* number of bytes used up by FCS */
+#define FCS_SIZE_BYTES 3
+
+/*! decode a RLP frame into its abstract representation. Doesn't check FCS correctness.
+ * \param[out] out caller-allocated memory for output of decoded frame
+ * \param[in] version RLP version number to use when decoding
+ * \param[in] data raw RLP frame input data
+ * \param[in] data_len length of data (in octets); must be 30 (240bit) or 72 (576bit)
+ * \returns 0 in case of success; negative on error */
+int osmo_rlp_decode(struct osmo_rlp_frame_decoded *out, uint8_t version, const uint8_t *data, size_t data_len)
+{
+ const uint8_t hdr_len = 2; /* will become a variable when we introduce v2 support */
+ uint8_t n_s, n_r;
+
+ if (data_len != 240/8 && data_len != 576/8)
+ return -EINVAL;
+
+ /* we only support version 0+1 so far */
+ if (version >= 2)
+ return -ENOTSUP;
+
+ memset(out, 0, sizeof(*out));
+ out->version = version;
+
+ out->c_r = data[0] & 1;
+ n_s = (data[0] >> 3) | (data[1] & 1) << 5;
+ n_r = (data[1] >> 2);
+ out->fcs = (data[data_len-1] << 16) | (data[data_len-2]) << 8 | (data[data_len-3] << 0);
+ out->p_f = (data[1] >> 1) & 1;
+
+ switch (n_s) {
+ case 0x3f:
+ out->ftype = OSMO_RLP_FT_U;
+ out->u_ftype = n_r & 0x1f;
+ if (out->u_ftype == OSMO_RLP_U_FT_XID) {
+ memcpy(out->info, data + hdr_len, data_len - (hdr_len + FCS_SIZE_BYTES));
+ out->info_len = data_len - (hdr_len + FCS_SIZE_BYTES);
+ }
+ break;
+ case 0x3e:
+ out->ftype = OSMO_RLP_FT_S;
+ out->s_ftype = (data[0] >> 1) & 3;
+ out->n_r = n_r;
+ break;
+ default:
+ out->ftype = OSMO_RLP_FT_IS;
+ out->s_ftype = (data[0] >> 1) & 3;
+ out->n_s = n_s;
+ out->n_r = n_r;
+ memcpy(out->info, data + hdr_len, data_len - (hdr_len + FCS_SIZE_BYTES));
+ out->info_len = data_len - (2 + 3);
+ break;
+ }
+
+ return 0;
+}
+
+/*! encode a RLP frame from its abstract representation. Generates FCS.
+ * \param[out] out caller-allocated output buffer
+ * \param[in] out_size size of output buffer (in octets); must be 30 (240bit) or 72 (576bit)
+ * \param[in] in decoded RLP frame which is to be encoded
+ * \returns number of output bytes used; negative on error */
+int osmo_rlp_encode(uint8_t *out, size_t out_size, const struct osmo_rlp_frame_decoded *in)
+{
+ const uint8_t hdr_len = 2; /* will become a variable when we introduce v2 support */
+ uint8_t n_s, n_r, s_bits;
+ uint32_t fcs;
+
+ /* we only support version 0+1 so far */
+ if (in->version >= 2)
+ return -ENOTSUP;
+
+ if (out_size != 240/8 && out_size != 576/8)
+ return -EINVAL;
+
+ memset(out, 0, out_size);
+
+ if (in->c_r)
+ out[0] |= 0x01;
+ if (in->p_f)
+ out[1] |= 0x02;
+
+ switch (in->ftype) {
+ case OSMO_RLP_FT_U:
+ n_s = 0x3f;
+ n_r = in->u_ftype;
+ s_bits = 0;
+ if (in->u_ftype == OSMO_RLP_U_FT_XID) {
+ if (in->info_len > out_size - (hdr_len + FCS_SIZE_BYTES))
+ return -EINVAL;
+ memcpy(out+hdr_len, in->info, in->info_len);
+ }
+ break;
+ case OSMO_RLP_FT_S:
+ n_s = 0x3e;
+ n_r = in->n_r;
+ s_bits = in->s_ftype;
+ break;
+ case OSMO_RLP_FT_IS:
+ /* we only support 240 bit so far */
+ if (in->info_len > out_size - (hdr_len + FCS_SIZE_BYTES))
+ return -EINVAL;
+ n_s = in->n_s;
+ n_r = in->n_r;
+ s_bits = in->s_ftype;
+ memcpy(out+hdr_len, in->info, in->info_len);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* patch N(S) into output data */
+ out[0] |= (n_s & 0x1F) << 3;
+ out[1] |= (n_s & 0x20) >> 5;
+
+ /* patch N(R) / M-bits into output data */
+ out[1] |= (n_r & 0x3f) << 2;
+
+ /* patch S-bits into output data */
+ out[0] |= (s_bits & 3) << 1;
+
+ /* compute FCS + add it to end of frame */
+ fcs = osmo_rlp_fcs_compute(out, out_size - FCS_SIZE_BYTES);
+ out[out_size - 3] = (fcs >> 0) & 0xff;
+ out[out_size - 2] = (fcs >> 8) & 0xff;
+ out[out_size - 1] = (fcs >> 16) & 0xff;
+
+ return out_size;
+}
+
+
+static const uint32_t rlp_fcs_table[256] = {
+ 0x00B29D2D, 0x00643A5B, 0x0044D87A, 0x00927F0C, 0x00051C38, 0x00D3BB4E, 0x00F3596F, 0x0025FE19,
+ 0x008694BC, 0x005033CA, 0x0070D1EB, 0x00A6769D, 0x003115A9, 0x00E7B2DF, 0x00C750FE, 0x0011F788,
+ 0x00DA8E0F, 0x000C2979, 0x002CCB58, 0x00FA6C2E, 0x006D0F1A, 0x00BBA86C, 0x009B4A4D, 0x004DED3B,
+ 0x00EE879E, 0x003820E8, 0x0018C2C9, 0x00CE65BF, 0x0059068B, 0x008FA1FD, 0x00AF43DC, 0x0079E4AA,
+ 0x0062BB69, 0x00B41C1F, 0x0094FE3E, 0x00425948, 0x00D53A7C, 0x00039D0A, 0x00237F2B, 0x00F5D85D,
+ 0x0056B2F8, 0x0080158E, 0x00A0F7AF, 0x007650D9, 0x00E133ED, 0x0037949B, 0x001776BA, 0x00C1D1CC,
+ 0x000AA84B, 0x00DC0F3D, 0x00FCED1C, 0x002A4A6A, 0x00BD295E, 0x006B8E28, 0x004B6C09, 0x009DCB7F,
+ 0x003EA1DA, 0x00E806AC, 0x00C8E48D, 0x001E43FB, 0x008920CF, 0x005F87B9, 0x007F6598, 0x00A9C2EE,
+ 0x0049DA1E, 0x009F7D68, 0x00BF9F49, 0x0069383F, 0x00FE5B0B, 0x0028FC7D, 0x00081E5C, 0x00DEB92A,
+ 0x007DD38F, 0x00AB74F9, 0x008B96D8, 0x005D31AE, 0x00CA529A, 0x001CF5EC, 0x003C17CD, 0x00EAB0BB,
+ 0x0021C93C, 0x00F76E4A, 0x00D78C6B, 0x00012B1D, 0x00964829, 0x0040EF5F, 0x00600D7E, 0x00B6AA08,
+ 0x0015C0AD, 0x00C367DB, 0x00E385FA, 0x0035228C, 0x00A241B8, 0x0074E6CE, 0x005404EF, 0x0082A399,
+ 0x0099FC5A, 0x004F5B2C, 0x006FB90D, 0x00B91E7B, 0x002E7D4F, 0x00F8DA39, 0x00D83818, 0x000E9F6E,
+ 0x00ADF5CB, 0x007B52BD, 0x005BB09C, 0x008D17EA, 0x001A74DE, 0x00CCD3A8, 0x00EC3189, 0x003A96FF,
+ 0x00F1EF78, 0x0027480E, 0x0007AA2F, 0x00D10D59, 0x00466E6D, 0x0090C91B, 0x00B02B3A, 0x00668C4C,
+ 0x00C5E6E9, 0x0013419F, 0x0033A3BE, 0x00E504C8, 0x007267FC, 0x00A4C08A, 0x008422AB, 0x005285DD,
+ 0x001F18F0, 0x00C9BF86, 0x00E95DA7, 0x003FFAD1, 0x00A899E5, 0x007E3E93, 0x005EDCB2, 0x00887BC4,
+ 0x002B1161, 0x00FDB617, 0x00DD5436, 0x000BF340, 0x009C9074, 0x004A3702, 0x006AD523, 0x00BC7255,
+ 0x00770BD2, 0x00A1ACA4, 0x00814E85, 0x0057E9F3, 0x00C08AC7, 0x00162DB1, 0x0036CF90, 0x00E068E6,
+ 0x00430243, 0x0095A535, 0x00B54714, 0x0063E062, 0x00F48356, 0x00222420, 0x0002C601, 0x00D46177,
+ 0x00CF3EB4, 0x001999C2, 0x00397BE3, 0x00EFDC95, 0x0078BFA1, 0x00AE18D7, 0x008EFAF6, 0x00585D80,
+ 0x00FB3725, 0x002D9053, 0x000D7272, 0x00DBD504, 0x004CB630, 0x009A1146, 0x00BAF367, 0x006C5411,
+ 0x00A72D96, 0x00718AE0, 0x005168C1, 0x0087CFB7, 0x0010AC83, 0x00C60BF5, 0x00E6E9D4, 0x00304EA2,
+ 0x00932407, 0x00458371, 0x00656150, 0x00B3C626, 0x0024A512, 0x00F20264, 0x00D2E045, 0x00044733,
+ 0x00E45FC3, 0x0032F8B5, 0x00121A94, 0x00C4BDE2, 0x0053DED6, 0x008579A0, 0x00A59B81, 0x00733CF7,
+ 0x00D05652, 0x0006F124, 0x00261305, 0x00F0B473, 0x0067D747, 0x00B17031, 0x00919210, 0x00473566,
+ 0x008C4CE1, 0x005AEB97, 0x007A09B6, 0x00ACAEC0, 0x003BCDF4, 0x00ED6A82, 0x00CD88A3, 0x001B2FD5,
+ 0x00B84570, 0x006EE206, 0x004E0027, 0x0098A751, 0x000FC465, 0x00D96313, 0x00F98132, 0x002F2644,
+ 0x00347987, 0x00E2DEF1, 0x00C23CD0, 0x00149BA6, 0x0083F892, 0x00555FE4, 0x0075BDC5, 0x00A31AB3,
+ 0x00007016, 0x00D6D760, 0x00F63541, 0x00209237, 0x00B7F103, 0x00615675, 0x0041B454, 0x00971322,
+ 0x005C6AA5, 0x008ACDD3, 0x00AA2FF2, 0x007C8884, 0x00EBEBB0, 0x003D4CC6, 0x001DAEE7, 0x00CB0991,
+ 0x00686334, 0x00BEC442, 0x009E2663, 0x00488115, 0x00DFE221, 0x00094557, 0x0029A776, 0x00FF0000
+};
+
+/*! compute RLP FCS according to 3GPP TS 24.022 Section 4.4.
+ * \param[in] in input data over which to compute FCS
+ * \param[in] in_len length of input data (in octets)
+ * \returns computed frame check sequence (FCS). */
+uint32_t osmo_rlp_fcs_compute(const uint8_t *in, size_t in_len)
+{
+ uint32_t divider = 0;
+ size_t i;
+
+ for (i = 0; i < in_len; i++) {
+ uint8_t input = in[i] ^ (divider & 0xff);
+ divider = (divider >> 8) ^ rlp_fcs_table[input];
+ }
+
+ return divider;
+}
+
+/*! @} */
diff --git a/src/gsm/rsl.c b/src/gsm/rsl.c
index 2ab49c23..fbba982a 100644
--- a/src/gsm/rsl.c
+++ b/src/gsm/rsl.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
@@ -127,6 +123,10 @@ const struct tlv_definition rsl_att_tlvdef = {
[RSL_IE_LLP_APDU] = { TLV_TYPE_TLV },
[RSL_IE_SIEMENS_MRPCI] = { TLV_TYPE_TV },
[RSL_IE_OSMO_REP_ACCH_CAP] = { TLV_TYPE_TLV },
+ [RSL_IE_OSMO_TRAINING_SEQUENCE] = { TLV_TYPE_TLV },
+ [RSL_IE_OSMO_TEMP_OVP_ACCH_CAP] = { TLV_TYPE_TLV },
+ [RSL_IE_OSMO_OSMUX_CID] = { TLV_TYPE_TLV },
+ [RSL_IE_IPAC_SRTP_CONFIG] = { TLV_TYPE_TLV },
[RSL_IE_IPAC_PROXY_UDP] = { TLV_TYPE_FIXED, 2 },
[RSL_IE_IPAC_BSCMPL_TOUT] = { TLV_TYPE_TV },
[RSL_IE_IPAC_REMOTE_IP] = { TLV_TYPE_FIXED, 4 },
@@ -135,6 +135,8 @@ const struct tlv_definition rsl_att_tlvdef = {
[RSL_IE_IPAC_LOCAL_PORT] = { TLV_TYPE_FIXED, 2 },
[RSL_IE_IPAC_SPEECH_MODE] = { TLV_TYPE_TV },
[RSL_IE_IPAC_LOCAL_IP] = { TLV_TYPE_FIXED, 4 },
+ [RSL_IE_IPAC_CONN_STAT] = { TLV_TYPE_TLV, 28 },
+ [RSL_IE_IPAC_HO_C_PARMS] = { TLV_TYPE_TLV },
[RSL_IE_IPAC_CONN_ID] = { TLV_TYPE_FIXED, 2 },
[RSL_IE_IPAC_RTP_CSD_FMT] = { TLV_TYPE_TV },
[RSL_IE_IPAC_RTP_JIT_BUF] = { TLV_TYPE_FIXED, 2 },
@@ -158,6 +160,7 @@ uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot)
switch (type) {
case RSL_CHAN_Lm_ACCHs:
+ case RSL_CHAN_OSMO_VAMOS_Lm_ACCHs:
subch &= 0x01;
break;
case RSL_CHAN_SDCCH4_ACCH:
@@ -186,38 +189,34 @@ int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *tim
{
*timeslot = chan_nr & 0x7;
- if ((chan_nr & 0xf8) == RSL_CHAN_Bm_ACCHs) {
- *type = RSL_CHAN_Bm_ACCHs;
- *subch = 0;
- } else if ((chan_nr & 0xf0) == RSL_CHAN_Lm_ACCHs) {
- *type = RSL_CHAN_Lm_ACCHs;
- *subch = (chan_nr >> 3) & 0x1;
- } else if ((chan_nr & 0xe0) == RSL_CHAN_SDCCH4_ACCH) {
- *type = RSL_CHAN_SDCCH4_ACCH;
- *subch = (chan_nr >> 3) & 0x3;
- } else if ((chan_nr & 0xc0) == RSL_CHAN_SDCCH8_ACCH) {
- *type = RSL_CHAN_SDCCH8_ACCH;
- *subch = (chan_nr >> 3) & 0x7;
- } else if ((chan_nr & 0xf8) == RSL_CHAN_BCCH) {
- *type = RSL_CHAN_BCCH;
- *subch = 0;
- } else if ((chan_nr & 0xf8) == RSL_CHAN_RACH) {
- *type = RSL_CHAN_RACH;
- *subch = 0;
- } else if ((chan_nr & 0xf8) == RSL_CHAN_PCH_AGCH) {
- *type = RSL_CHAN_PCH_AGCH;
- *subch = 0;
- } else if ((chan_nr & 0xf8) == RSL_CHAN_OSMO_PDCH) {
- *type = RSL_CHAN_OSMO_PDCH;
+ switch (chan_nr & RSL_CHAN_NR_MASK) {
+ case RSL_CHAN_Bm_ACCHs:
+ case RSL_CHAN_BCCH:
+ case RSL_CHAN_RACH:
+ case RSL_CHAN_PCH_AGCH:
+ case RSL_CHAN_OSMO_PDCH:
+ case RSL_CHAN_OSMO_CBCH4:
+ case RSL_CHAN_OSMO_CBCH8:
+ case RSL_CHAN_OSMO_VAMOS_Bm_ACCHs:
+ *type = chan_nr & RSL_CHAN_NR_MASK;
*subch = 0;
- } else if ((chan_nr & 0xf8) == RSL_CHAN_OSMO_CBCH4) {
- *type = RSL_CHAN_OSMO_CBCH4;
- *subch = 0;
- } else if ((chan_nr & 0xf8) == RSL_CHAN_OSMO_CBCH8) {
- *type = RSL_CHAN_OSMO_CBCH8;
- *subch = 0;
- } else
- return -EINVAL;
+ break;
+ default:
+ if ((chan_nr & 0xf0) == RSL_CHAN_Lm_ACCHs) {
+ *type = RSL_CHAN_Lm_ACCHs;
+ *subch = (chan_nr >> 3) & 0x1;
+ } else if ((chan_nr & 0xe0) == RSL_CHAN_SDCCH4_ACCH) {
+ *type = RSL_CHAN_SDCCH4_ACCH;
+ *subch = (chan_nr >> 3) & 0x3;
+ } else if ((chan_nr & 0xc0) == RSL_CHAN_SDCCH8_ACCH) {
+ *type = RSL_CHAN_SDCCH8_ACCH;
+ *subch = (chan_nr >> 3) & 0x7;
+ } else if ((chan_nr & 0xf0) == RSL_CHAN_OSMO_VAMOS_Lm_ACCHs) {
+ *type = RSL_CHAN_OSMO_VAMOS_Lm_ACCHs;
+ *subch = (chan_nr >> 3) & 0x1;
+ } else
+ return -EINVAL;
+ }
return 0;
}
@@ -253,6 +252,10 @@ char *rsl_chan_nr_str_buf(char *buf, size_t buf_len, uint8_t chan_nr)
snprintf(buf, buf_len, "CBCH(SDCCH/4) on TS%d", ts);
else if (cbits == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH8)
snprintf(buf, buf_len, "CBCH(SDCCH/8) on TS%d", ts);
+ else if (cbits == ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Bm_ACCHs)
+ snprintf(buf, buf_len, "VAMOS TCH/F on TS%d", ts);
+ else if ((cbits & 0x1e) == ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Lm_ACCHs(0))
+ snprintf(buf, buf_len, "VAMOS TCH/H(%u) on TS%d", cbits & 0x01, ts);
else
snprintf(buf, buf_len, "UNKNOWN on TS%d", ts);
@@ -265,7 +268,7 @@ char *rsl_chan_nr_str_buf(char *buf, size_t buf_len, uint8_t chan_nr)
*/
const char *rsl_chan_nr_str(uint8_t chan_nr)
{
- static __thread char str[20];
+ static __thread char str[32];
return rsl_chan_nr_str_buf(str, sizeof(str), chan_nr);
}
@@ -276,10 +279,10 @@ const char *rsl_chan_nr_str(uint8_t chan_nr)
*/
char *rsl_chan_nr_str_c(const void *ctx, uint8_t chan_nr)
{
- char *str = talloc_size(ctx, 20);
+ char *str = talloc_size(ctx, 32);
if (!str)
return NULL;
- return rsl_chan_nr_str_buf(str, 20, chan_nr);
+ return rsl_chan_nr_str_buf(str, 32, chan_nr);
}
static const struct value_string rsl_err_vals[] = {
@@ -550,7 +553,7 @@ void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
/* construct a RSLms RLL message (DATA INDICATION, UNIT DATA
* INDICATION) and send it off via RSLms */
- /* Push the L3 IE tag and lengh */
+ /* Push the L3 IE tag and length */
msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
/* Then push the RSL header */
@@ -618,6 +621,10 @@ const struct tlv_definition rsl_ipac_eie_tlvdef = {
[RSL_IPAC_EIE_3G_NCELL_LIST] = { TLV_TYPE_TLV },
[RSL_IPAC_EIE_SDCCH_CTL_PARAM] = { TLV_TYPE_TV },
[RSL_IPAC_EIE_AMR_CONV_THRESH] = { TLV_TYPE_FIXED, 9 },
+ /* Osmocom extensions: */
+ [RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG]= { TLV_TYPE_TLV },
+ [RSL_IPAC_EIE_OSMO_MS_PWR_CTL] = { TLV_TYPE_TLV },
+ [RSL_IPAC_EIE_OSMO_PC_THRESH_COMP]= { TLV_TYPE_TLV },
},
};
diff --git a/src/gsm/rxlev_stat.c b/src/gsm/rxlev_stat.c
index 9c650cc1..f1b77f4c 100644
--- a/src/gsm/rxlev_stat.c
+++ b/src/gsm/rxlev_stat.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <unistd.h>
diff --git a/src/gsm/sysinfo.c b/src/gsm/sysinfo.c
index b615871f..40e26524 100644
--- a/src/gsm/sysinfo.c
+++ b/src/gsm/sysinfo.c
@@ -46,7 +46,7 @@ osmo_static_assert(sizeof(struct gsm48_system_information_type_4) == 13, _si4_si
/* bs11 forgot the l2 len, 0-6 rest octets */
osmo_static_assert(sizeof(struct gsm48_system_information_type_5) == 18, _si5_size);
osmo_static_assert(sizeof(struct gsm48_system_information_type_6) == 11, _si6_size);
-
+osmo_static_assert(sizeof(struct gsm48_system_information_type_10) == 1, _si10_size);
osmo_static_assert(sizeof(struct gsm48_system_information_type_13) == 3, _si13_size);
static const uint8_t sitype2rsl[_MAX_SYSINFO_TYPE] = {
diff --git a/src/gsm/tlv_parser.c b/src/gsm/tlv_parser.c
index c5aac97c..8dd460db 100644
--- a/src/gsm/tlv_parser.c
+++ b/src/gsm/tlv_parser.c
@@ -126,7 +126,7 @@ int osmo_tlvp_merge(struct tlv_parsed *dst, const struct tlv_parsed *src)
* \param[inout] msg Caller-allocated message buffer with sufficient tailroom
* \param[in] type TLV type/format to use during encode
* \param[in] tag Tag of TLV to be encoded
- * \parma[in] len Length of TLV to be encoded
+ * \param[in] len Length of TLV to be encoded
* \param[in] val Value part of TLV to be encoded
* \returns 0 on success; negative in case of error */
int tlv_encode_one(struct msgb *msg, enum tlv_type type, uint8_t tag,
@@ -225,7 +225,11 @@ int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, const
* \param[in] def structure defining the valid TLV tags / configurations
* \param[in] buf the input data buffer to be parsed
* \param[in] buf_len length of the input data buffer
- * \returns number of bytes consumed by the TLV entry / IE parsed; negative in case of error
+ * \returns number of bytes consumed by the TLV entry / IE parsed; negative in case of error.
+ *
+ * In IEs of type TLV_TYPE_SINGLE_TV, the data pointer \ref o_val will point to the
+ * byte shared by both the Tag and te Value, hence the tag is to be trimmed
+ * by the caller.
*/
int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
const struct tlv_definition *def,
@@ -241,7 +245,13 @@ int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
*o_tag = tag;
/* single octet TV IE */
- if (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV) {
+ if (def->def[tag >> 4].type == TLV_TYPE_SINGLE_TV) {
+ *o_tag = tag >> 4;
+ *o_val = buf;
+ *o_len = 1;
+ return 1;
+ } else if ((tag > 0x0f) && (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV)) {
+ /* backward compat for old IEs with half-octet tag defined as 0xN0: */
*o_tag = tag & 0xf0;
*o_val = buf;
*o_len = 1;
diff --git a/src/gsm/tuak/KeccakP-1600-3gpp.c b/src/gsm/tuak/KeccakP-1600-3gpp.c
new file mode 100644
index 00000000..3f5e2ad4
--- /dev/null
+++ b/src/gsm/tuak/KeccakP-1600-3gpp.c
@@ -0,0 +1,176 @@
+/* -----------------------------------------------------------------------
+ * code extracted from 3GPP TS 35.231, annex E for Keccak core functions
+ * https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2402
+ *-----------------------------------------------------------------------*/
+
+/* This code may be freely used or adapted.
+*/
+
+#include "KeccakP-1600-3gpp.h"
+
+
+const uint8_t Rho[25] = {0,1,62,28,27,36,44,6,55,20,3,10,43,25,39,41,45,
+ 15,21,8,18,2,61,56,14};
+
+const uint8_t Pi[25] = {0,6,12,18,24,3,9,10,16,22,1,7,13,19,20,4,5,11,17,
+ 23,2,8,14,15,21};
+
+const uint8_t Iota[24] = {1,146,218,112,155,33,241,89,138,136,57,42,187,203,
+ 217,83,82,192,26,106,241,208,33,120};
+
+#define ROTATE64(value, n) \
+((((uint64_t)(value))<<(n)) | (((uint64_t)(value))>>(64-(n))))
+
+/* ---------------------------------------------------------------------
+ 64-bit version of Keccak_f(1600)
+ ---------------------------------------------------------------------
+*/
+void Keccak_f_64(uint64_t s[25])
+{ uint64_t t[5];
+ uint8_t i, j, round;
+
+ for(round=0; round<24; ++round)
+ { /* Theta function */
+ for(i=0; i<5; ++i)
+ t[i] = s[i] ^ s[5+i] ^ s[10+i] ^ s[15+i] ^ s[20+i];
+ for(i=0; i<5; ++i, s+=5)
+ { s[0] ^= t[4] ^ ROTATE64(t[1], 1);
+ s[1] ^= t[0] ^ ROTATE64(t[2], 1);
+ s[2] ^= t[1] ^ ROTATE64(t[3], 1);
+ s[3] ^= t[2] ^ ROTATE64(t[4], 1);
+ s[4] ^= t[3] ^ ROTATE64(t[0], 1);
+ }
+ s -= 25;
+
+ /* Rho function */
+ for(i=1; i<25; ++i)
+ s[i] = ROTATE64(s[i], Rho[i]);
+
+ /* Pi function */
+ for(t[1] = s[i=1]; (j=Pi[i]) > 1; s[i]=s[j], i=j);
+ s[i] = t[1];
+
+ /* Chi function */
+ for(i=0; i<5; ++i, s += 5)
+ { t[0] = (~s[1]) & s[2];
+ t[1] = (~s[2]) & s[3];
+ t[2] = (~s[3]) & s[4];
+ t[3] = (~s[4]) & s[0];
+ t[4] = (~s[0]) & s[1];
+ for(j=0; j<5; ++j) s[j] ^= t[j];
+ }
+ s -= 25;
+
+ /* Iota function */
+ t[0] = Iota[round];
+ *s ^= (t[0] | (t[0]<<11) | (t[0]<<26) | (t[0]<<57))
+ & 0x800000008000808BULL; /* set & mask bits 0,1,3,7,15,31,63 */
+ }
+}
+
+
+/* ---------------------------------------------------------------------
+ 8-bit version of Keccak_f(1600)
+ ---------------------------------------------------------------------
+*/
+void Keccak_f_8(uint8_t s[200])
+{ uint8_t t[40], i, j, k, round;
+
+ for(round=0; round<24; ++round)
+ { /* Theta function */
+ for(i=0; i<40; ++i)
+ t[i]=s[i]^s[40+i]^s[80+i]^s[120+i]^s[160+i];
+ for(i=0; i<200; i+=8)
+ for(j = (i+32)%40, k=0; k<8; ++k)
+ s[i+k] ^= t[j+k];
+ for(i=0; i<40; t[i] = (t[i]<<1)|j, i+=8)
+ for(j = t[i+7]>>7, k=7; k; --k)
+ t[i+k] = (t[i+k]<<1)|(t[i+k-1]>>7);
+ for(i=0; i<200; i+=8)
+ for(j = (i+8)%40, k=0; k<8; ++k)
+ s[i+k] ^= t[j+k];
+
+ /* Rho function */
+ for(i=8; i<200; i+=8)
+ { for(j = Rho[i>>3]>>3, k=0; k<8; ++k) /* j:=bytes to shift, s->t */
+ t[(k+j)&7] = s[i+k];
+ for(j = Rho[i>>3]&7, k=7; k; --k) /* j:=bits to shift, t->s */
+ s[i+k] = (t[k]<<j) | (t[k-1]>>(8-j));
+ s[i] = (t[0]<<j) | (t[7]>>(8-j));
+ }
+
+ /* Pi function */
+ for(k=8; k<16; ++k) t[k] = s[k]; /* =memcpy(t+8, s+8, 8) */
+ for(i=1; (j=Pi[i])>1; i=j)
+ for(k=0; k<8; ++k) /* =memcpy(s+(i<<3), s+(j<<3), 8) */
+ s[(i<<3)|k] = s[(j<<3)|k];
+ for(k=0; k<8; ++k) /* =memcpy(s+(i<<3), t+8, 8) */
+ s[(i<<3)|k] = t[k+8];
+
+ /* Chi function */
+ for(i=0; i<200; i+=40)
+ { for(j=0; j<40; ++j)
+ t[j]=(~s[i+(j+8)%40]) & s[i+(j+16)%40];
+ for(j=0; j<40; ++j) s[i+j]^=t[j];
+ }
+
+ /* Iota function */
+ k = Iota[round];
+ s[0] ^= k & 0x8B; /* bits 0, 1, 3, 7 */
+ s[1] ^= (k<<3)&0x80; /* bit 15 */
+ s[3] ^= (k<<2)&0x80; /* bit 31 */
+ s[7] ^= (k<<1)&0x80; /* bit 63 */
+
+ }
+}
+
+/* ---------------------------------------------------------------------
+ 32-bit version of Keccak_f(1600)
+ ---------------------------------------------------------------------
+*/
+void Keccak_f_32(uint32_t s[50])
+{ uint32_t t[10];
+ uint8_t i, j, round, k;
+
+ for(round=0; round<24; ++round)
+ { /* Theta function */
+ for(i=0; i<10; ++i)
+ t[i] = s[i] ^ s[10+i] ^ s[20+i] ^ s[30+i] ^ s[40+i];
+ for(i=0; i<5; ++i)
+ for(j=8, k=2; ; j%=10, k=(k+2)%10)
+ { *s++ ^= t[j++] ^ ((t[k]<<1)|(t[k+1]>>31));
+ *s++ ^= t[j++] ^ ((t[k+1]<<1)|(t[k]>>31));
+ if(j==8) break;
+ }
+ s -= 50;
+
+ /* Rho function */
+ for(i=2; i<50; i+=2)
+ { k = Rho[i>>1] & 0x1f;
+ t[0] = (s[i+1] << k) | (s[i] >> (32-k));
+ t[1] = (s[i] << k) | (s[i+1] >> (32-k));
+ k = Rho[i>>1] >> 5;
+ s[i] = t[1-k], s[i+1] = t[k];
+ }
+
+ /* Pi function */
+ for(i=2, t[0]=s[2], t[1]=s[3]; (j=(Pi[i>>1]<<1))>2; i=j)
+ s[i]=s[j], s[i+1]=s[j+1];
+ s[i]=t[0], s[i+1]=t[1];
+
+ /* Chi function */
+ for(i=0; i<5; ++i, s+=10)
+ { for(j=0; j<10; ++j)
+ t[j] = (~s[(j+2)%10]) & s[(j+4)%10];
+ for(j=0; j<10; ++j)
+ s[j] ^= t[j];
+ }
+ s -= 50;
+
+ /* Iota function */
+ t[0] = Iota[round];
+ s[0] ^= (t[0] | (t[0]<<11) | (t[0]<<26)) & 0x8000808B;
+ s[1] ^= (t[0]<<25) & 0x80000000;
+ }
+}
+
diff --git a/src/gsm/tuak/KeccakP-1600-3gpp.h b/src/gsm/tuak/KeccakP-1600-3gpp.h
new file mode 100644
index 00000000..a23cc460
--- /dev/null
+++ b/src/gsm/tuak/KeccakP-1600-3gpp.h
@@ -0,0 +1,25 @@
+/* -----------------------------------------------------------------------
+ * code extracted from 3GPP TS 35.231, annex E for Keccak core functions
+ * https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2402
+ *-----------------------------------------------------------------------*/
+
+/* this is the trick to make the code cross-platform
+ * at least, Win32 / Linux */
+
+#if defined(_WIN32) || defined(__WIN32__)
+# include <windows.h>
+# define EXPORTIT __declspec(dllexport)
+#else
+# define EXPORTIT
+#endif
+
+#include <stdint.h>
+
+/*------------------------------------------------------------------------
+ * KeccakP-1600-3gpp.h
+ *------------------------------------------------------------------------*/
+
+EXPORTIT void Keccak_f_8 (uint8_t s[200]);
+EXPORTIT void Keccak_f_32(uint32_t s[50]);
+EXPORTIT void Keccak_f_64(uint64_t s[25]);
+
diff --git a/src/gsm/tuak/tuak.c b/src/gsm/tuak/tuak.c
new file mode 100644
index 00000000..c044a377
--- /dev/null
+++ b/src/gsm/tuak/tuak.c
@@ -0,0 +1,372 @@
+/* (C) 2023 by Harald Welte <laforge@osmocom.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+
+#include "KeccakP-1600-3gpp.h"
+
+/* TUAK authentication algorithm
+ * as proposed by 3GPP as an alternative to Milenage
+ * algorithm based on SHA-3 (more exactly its KeccakP-1600 permutation)
+ * see 3GPP TS 35.231, 232 and 233 */
+
+static unsigned int g_keccak_iterations = 1;
+static const char algoname[] = "TUAK1.0";
+const uint8_t zero16[16] = { 0, };
+
+void tuak_set_keccak_iterations(unsigned int i)
+{
+ g_keccak_iterations = i;
+}
+
+/* append data from 'input' to 'buf' at 'idx', reversing byte order */
+#define PUSH_DATA(buf, idx, input, nbytes) \
+ for (int i = nbytes-1; i >= 0; i--) { \
+ buf[idx++] = input[i]; \
+ }
+
+/* like memcpy(), but reversing they order of bytes */
+void memcpy_reverse(uint8_t *dst, const uint8_t *src, size_t len)
+{
+ for (size_t i = 0; i < len; i++)
+ dst[i] = src[len-i-1];
+}
+
+static void tuak_core(uint8_t buf[200], const uint8_t *opc, uint8_t instance, const uint8_t *_rand,
+ const uint8_t *amf, const uint8_t *sqn, const uint8_t *k, uint8_t k_len_bytes,
+ unsigned int keccac_iterations)
+{
+ unsigned int idx = 0;
+
+ PUSH_DATA(buf, idx, opc, 32);
+ buf[idx++] = instance;
+ PUSH_DATA(buf, idx, algoname, strlen(algoname)); /* without trailing NUL */
+ PUSH_DATA(buf, idx, _rand, 16);
+ PUSH_DATA(buf, idx, amf, 2);
+ PUSH_DATA(buf, idx, sqn, 6);
+ PUSH_DATA(buf, idx, k, k_len_bytes);
+ memset(buf+idx, 0, 32-k_len_bytes); idx += 32-k_len_bytes;
+ buf[idx++] = 0x1f;
+ memset(buf+idx, 0, 38); idx += 38;
+ buf[idx++] = 0x80;
+ memset(buf+idx, 0, 64); idx += 64;
+ OSMO_ASSERT(idx == 200);
+
+ for (unsigned int i = 0; i < keccac_iterations; i++)
+ Keccak_f_64((uint64_t *) buf);
+}
+
+/**
+ * tuak_f1 - TUAK f1 algorithm
+ * @opc: OPc = 256-bit value derived from OP and K
+ * @k: K = 128-bit or 256-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sqn: SQN = 48-bit sequence number
+ * @amf: AMF = 16-bit authentication management field
+ * @mac_a: Buffer for MAC-A = 64/128/256-bit network authentication code
+ * Returns: 0 on success, -1 on failure
+ */
+int tuak_f1(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes, const uint8_t *_rand,
+ const uint8_t *sqn, const uint8_t *amf, uint8_t *mac_a, uint8_t mac_a_len_bytes,
+ unsigned int keccac_iterations)
+{
+ uint8_t buf[200];
+ uint8_t instance = 0x00;
+
+ switch (mac_a_len_bytes) {
+ case 8:
+ instance |= 0x08;
+ break;
+ case 16:
+ instance |= 0x10;
+ break;
+ case 32:
+ instance |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (k_len_bytes) {
+ case 16:
+ break;
+ case 32:
+ instance |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tuak_core(buf, opc, instance, _rand, amf, sqn, k, k_len_bytes, keccac_iterations);
+
+ memcpy_reverse(mac_a, buf, mac_a_len_bytes);
+
+ return 0;
+}
+
+/**
+ * tuak_f1star - TUAK f1* algorithm
+ * @opc: OPc = 256-bit value derived from OP and K
+ * @k: K = 128-bit or 256-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sqn: SQN = 48-bit sequence number
+ * @amf: AMF = 16-bit authentication management field
+ * @mac_s: Buffer for MAC-S = 64/128/256-bit resync authentication code
+ * Returns: 0 on success, -1 on failure
+ */
+int tuak_f1star(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes, const uint8_t *_rand,
+ const uint8_t *sqn, const uint8_t *amf, uint8_t *mac_s, uint8_t mac_s_len_bytes,
+ unsigned int keccac_iterations)
+{
+ uint8_t buf[200];
+ uint8_t instance = 0x80;
+
+ switch (mac_s_len_bytes) {
+ case 8:
+ instance |= 0x08;
+ break;
+ case 16:
+ instance |= 0x10;
+ break;
+ case 32:
+ instance |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (k_len_bytes) {
+ case 16:
+ break;
+ case 32:
+ instance |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tuak_core(buf, opc, instance, _rand, amf, sqn, k, k_len_bytes, keccac_iterations);
+
+ memcpy_reverse(mac_s, buf, mac_s_len_bytes);
+
+ return 0;
+}
+
+/**
+ * tuak_f2345 - TUAK f2, f3, f4, f5, algorithms
+ * @opc: OPc = 256-bit value derived from OP and K
+ * @k: K = 128/256-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @res: Buffer for RES = 32/64/128/256-bit signed response (f2), or %NULL
+ * @ck: Buffer for CK = 128/256-bit confidentiality key (f3), or %NULL
+ * @ik: Buffer for IK = 128/256-bit integrity key (f4), or %NULL
+ * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL
+ * Returns: 0 on success, -1 on failure
+ */
+int tuak_f2345(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *_rand, uint8_t *res, uint8_t res_len_bytes,
+ uint8_t *ck, uint8_t ck_len_bytes,
+ uint8_t *ik, uint8_t ik_len_bytes, uint8_t *ak, unsigned int keccac_iterations)
+{
+ uint8_t buf[200];
+ uint8_t instance = 0x40;
+
+ switch (res_len_bytes) {
+ case 4:
+ break;
+ case 8:
+ instance |= 0x08;
+ break;
+ case 16:
+ instance |= 0x10;
+ break;
+ case 32:
+ instance |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (ck_len_bytes) {
+ case 16:
+ break;
+ case 32:
+ instance |= 0x04;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (ik_len_bytes) {
+ case 16:
+ break;
+ case 32:
+ instance |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (k_len_bytes) {
+ case 16:
+ break;
+ case 32:
+ instance |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tuak_core(buf, opc, instance, _rand, zero16, zero16, k, k_len_bytes, keccac_iterations);
+
+ if (res)
+ memcpy_reverse(res, buf, res_len_bytes);
+
+ if (ck)
+ memcpy_reverse(ck, buf + 32, ck_len_bytes);
+
+ if (ik)
+ memcpy_reverse(ik, buf + 64, ik_len_bytes);
+
+ if (ak)
+ memcpy_reverse(ak, buf + 96, 6);
+
+ return 0;
+}
+
+/**
+ * tuak_f5star - TUAK f5* algorithm
+ * @opc: OPc = 256-bit value derived from OP and K
+ * @k: K = 128/256-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @ak: Buffer for AK = 48-bit anonymity key (f5)
+ * Returns: 0 on success, -1 on failure
+ */
+int tuak_f5star(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *_rand, uint8_t *ak, unsigned int keccac_iterations)
+{
+ uint8_t buf[200];
+ uint8_t instance = 0xc0;
+
+ switch (k_len_bytes) {
+ case 16:
+ break;
+ case 32:
+ instance += 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tuak_core(buf, opc, instance, _rand, zero16, zero16, k, k_len_bytes, keccac_iterations);
+
+ memcpy_reverse(ak, buf + 96, 6);
+
+ return 0;
+}
+
+/**
+ * tuak_generate - Generate AKA AUTN,IK,CK,RES
+ * @opc: OPc = 256-bit operator variant algorithm configuration field (encr.)
+ * @amf: AMF = 16-bit authentication management field
+ * @k: K = 128/256-bit subscriber key
+ * @sqn: SQN = 48-bit sequence number
+ * @_rand: RAND = 128-bit random challenge
+ * @autn: Buffer for AUTN = 128-bit authentication token
+ * @ik: Buffer for IK = 128/256-bit integrity key (f4), or %NULL
+ * @ck: Buffer for CK = 128/256-bit confidentiality key (f3), or %NULL
+ * @res: Buffer for RES = 32/64/128-bit signed response (f2), or %NULL
+ * @res_len: Max length for res; set to used length or 0 on failure
+ */
+void tuak_generate(const uint8_t *opc, const uint8_t *amf, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *sqn, const uint8_t *_rand, uint8_t *autn, uint8_t *ik,
+ uint8_t *ck, uint8_t *res, size_t *res_len)
+{
+ int i;
+ uint8_t mac_a[8], ak[6];
+
+ if (*res_len < 4) {
+ *res_len = 0;
+ return;
+ }
+ if (tuak_f1(opc, k, k_len_bytes, _rand, sqn, amf, mac_a, sizeof(mac_a), g_keccak_iterations) ||
+ tuak_f2345(opc, k, k_len_bytes, _rand, res, *res_len, ck, 16, ik, 16, ak, g_keccak_iterations)) {
+ *res_len = 0;
+ return;
+ }
+
+ /* AUTN = (SQN ^ AK) || AMF || MAC */
+ for (i = 0; i < 6; i++)
+ autn[i] = sqn[i] ^ ak[i];
+ memcpy(autn + 6, amf, 2);
+ memcpy(autn + 8, mac_a, 8);
+}
+
+
+/**
+ * tuak_auts - Milenage AUTS validation
+ * @opc: OPc = 256-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128/256-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @auts: AUTS = 112-bit authentication token from client
+ * @sqn: Buffer for SQN = 48-bit sequence number
+ * Returns: 0 = success (sqn filled), -1 on failure
+ */
+int tuak_auts(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *_rand, const uint8_t *auts, uint8_t *sqn)
+{
+ uint8_t amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+ uint8_t ak[6], mac_s[8];
+ int i;
+
+ if (tuak_f5star(opc, k, k_len_bytes, _rand, ak, g_keccak_iterations))
+ return -1;
+ for (i = 0; i < 6; i++)
+ sqn[i] = auts[i] ^ ak[i];
+ if (tuak_f1star(opc, k, k_len_bytes, _rand, sqn, amf, mac_s, 8, g_keccak_iterations) ||
+ memcmp(mac_s, auts + 6, 8) != 0)
+ return -1;
+ return 0;
+}
+
+int tuak_opc_gen(uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes, const uint8_t *op)
+{
+ uint8_t buf[200];
+ uint8_t instance;
+
+ switch (k_len_bytes) {
+ case 16:
+ instance = 0x00;
+ break;
+ case 32:
+ instance = 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tuak_core(buf, op, instance, zero16, zero16, zero16, k, k_len_bytes, g_keccak_iterations);
+
+ memcpy_reverse(opc, buf, 32);
+
+ return 0;
+}
diff --git a/src/gsm/tuak/tuak.h b/src/gsm/tuak/tuak.h
new file mode 100644
index 00000000..1a808224
--- /dev/null
+++ b/src/gsm/tuak/tuak.h
@@ -0,0 +1,33 @@
+#pragma once
+#include <stdint.h>
+
+/* low-level functions */
+
+int tuak_f1(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes, const uint8_t *_rand,
+ const uint8_t *sqn, const uint8_t *amf, uint8_t *mac_a, uint8_t mac_a_len_bytes,
+ unsigned int keccac_iterations);
+
+int tuak_f1star(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes, const uint8_t *_rand,
+ const uint8_t *sqn, const uint8_t *amf, uint8_t *mac_s, uint8_t mac_s_len_bytes,
+ unsigned int keccac_iterations);
+
+int tuak_f2345(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *_rand, uint8_t *res, uint8_t res_len_bytes,
+ uint8_t *ck, uint8_t ck_len_bytes,
+ uint8_t *ik, uint8_t ik_len_bytes, uint8_t *ak, unsigned int keccac_iterations);
+
+int tuak_f5star(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *_rand, uint8_t *ak, unsigned int keccac_iterations);
+
+/* high-level API */
+
+void tuak_set_keccak_iterations(unsigned int i);
+
+void tuak_generate(const uint8_t *opc, const uint8_t *amf, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *sqn, const uint8_t *_rand, uint8_t *autn, uint8_t *ik,
+ uint8_t *ck, uint8_t *res, size_t *res_len);
+
+int tuak_auts(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *_rand, const uint8_t *auts, uint8_t *sqn);
+
+int tuak_opc_gen(uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes, const uint8_t *op);
diff --git a/src/isdn/Makefile.am b/src/isdn/Makefile.am
new file mode 100644
index 00000000..3e7f86eb
--- /dev/null
+++ b/src/isdn/Makefile.am
@@ -0,0 +1,26 @@
+# 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
+
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
+
+if ENABLE_PSEUDOTALLOC
+AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
+endif
+
+noinst_LTLIBRARIES = libisdnint.la
+lib_LTLIBRARIES = libosmoisdn.la
+
+libisdnint_la_SOURCES = i460_mux.c lapd_core.c v110.c v110_ta.c
+
+libisdnint_la_LDFLAGS = -no-undefined
+libisdnint_la_LIBADD = $(top_builddir)/src/core/libosmocore.la
+
+libosmoisdn_la_SOURCES =
+libosmoisdn_la_LDFLAGS = $(LTLDFLAGS_OSMOISDN) -version-info $(LIBVERSION) -no-undefined
+libosmoisdn_la_LIBADD = libisdnint.la $(TALLOC_LIBS)
+
+EXTRA_DIST = libosmoisdn.map
+EXTRA_libosmoisdn_la_DEPENDENCIES = libosmoisdn.map
diff --git a/src/gsm/i460_mux.c b/src/isdn/i460_mux.c
index 91ab2a19..b070bbdc 100644
--- a/src/gsm/i460_mux.c
+++ b/src/isdn/i460_mux.c
@@ -14,11 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
*/
#include <errno.h>
@@ -26,10 +21,12 @@
#include <osmocom/core/bits.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/i460_mux.h>
+#include <osmocom/isdn/i460_mux.h>
-/* count the number of sub-channels in this I460 slot */
-static int osmo_i460_subchan_count(struct osmo_i460_timeslot *ts)
+/*! count the number of sub-channels in this I.460 slot.
+ * \param[in] ts timeslot that holds the I.460 subchannels.
+ * \return number of subchannels. */
+int osmo_i460_subchan_count(struct osmo_i460_timeslot *ts)
{
int i, num_used = 0;
@@ -124,10 +121,10 @@ static void demux_subchan_extract_bits(struct osmo_i460_subchan *schan, const ui
}
}
-/*! Data from E1 timeslot into de-multiplexer
- * \param[in] ts timeslot state
- * \param[in] data input data bytes as received from E1/T1
- * \param[in] data_len length of data in bytes */
+/*! Feed multiplexed data (from an E1 timeslot) into de-multiplexer.
+ * \param[in] ts timeslot state.
+ * \param[in] data input data bytes as received from E1/T1.
+ * \param[in] data_len length of data in bytes. */
void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len)
{
struct osmo_i460_subchan *schan;
@@ -200,7 +197,7 @@ static ubit_t mux_schan_provide_bit(struct osmo_i460_subchan *schan)
/*! provide one byte with the subchan-specific bits of given sub-channel.
* \param[in] schan sub-channel that is to provide bits
- * \parma[out] mask bitmask of those bits filled in
+ * \param[out] mask bitmask of those bits filled in
* \returns bits of given sub-channel */
static uint8_t mux_subchan_provide_bits(struct osmo_i460_subchan *schan, uint8_t *mask)
{
@@ -248,16 +245,14 @@ static uint8_t mux_subchan_provide_bits(struct osmo_i460_subchan *schan, uint8_t
/* provide one byte of multiplexed I.460 bits */
static uint8_t mux_timeslot_provide_bits(struct osmo_i460_timeslot *ts)
{
- int i, count = 0;
uint8_t ret = 0xff; /* unused bits must be '1' as per I.460 */
- for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
+ for (int i = 0; i < ARRAY_SIZE(ts->schan); i++) {
struct osmo_i460_subchan *schan = &ts->schan[i];
uint8_t bits, mask;
if (schan->rate == OSMO_I460_RATE_NONE)
continue;
- count++;
bits = mux_subchan_provide_bits(schan, &mask);
ret &= ~mask;
ret |= bits;
@@ -267,11 +262,10 @@ static uint8_t mux_timeslot_provide_bits(struct osmo_i460_timeslot *ts)
}
-/*! Data from E1 timeslot into de-multiplexer
- * \param[in] ts timeslot state
- * \param[out] out caller-provided buffer where to store generated output bytes
- * \param[in] out_len number of bytes to be stored at out
- */
+/*! Get multiplexed data from de-multiplexer (for feeding it into an E1 timeslot).
+ * \param[in] ts timeslot state.
+ * \param[out] out caller-provided buffer where to store generated output bytes.
+ * \param[in] out_len number of bytes to be stored at out. */
int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len)
{
int i;
diff --git a/src/gsm/lapd_core.c b/src/isdn/lapd_core.c
index e0c232fe..b32ed263 100644
--- a/src/gsm/lapd_core.c
+++ b/src/isdn/lapd_core.c
@@ -18,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup lapd
@@ -73,6 +69,7 @@
//#define TEST_CONTENT_RESOLUTION_NETWORK
#include <stdio.h>
+#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
@@ -82,7 +79,7 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/lapd_core.h>
+#include <osmocom/isdn/lapd_core.h>
#include <osmocom/gsm/rsl.h>
/* TS 04.06 Table 4 / Section 3.8.1 */
@@ -111,7 +108,7 @@
static void lapd_t200_cb(void *data);
static void lapd_t203_cb(void *data);
-static int lapd_send_i(struct lapd_msg_ctx *lctx, int line);
+static int lapd_send_i(struct lapd_datalink *dl, int line, bool rts);
static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
/* UTILITY FUNCTIONS */
@@ -172,12 +169,17 @@ static void lapd_dl_flush_hist(struct lapd_datalink *dl)
}
}
-static void lapd_dl_flush_tx(struct lapd_datalink *dl)
+static void lapd_dl_flush_tx_queue(struct lapd_datalink *dl)
{
struct msgb *msg;
while ((msg = msgb_dequeue(&dl->tx_queue)))
msgb_free(msg);
+}
+
+static void lapd_dl_flush_tx(struct lapd_datalink *dl)
+{
+ lapd_dl_flush_tx_queue(dl);
lapd_dl_flush_hist(dl);
}
@@ -202,11 +204,35 @@ static inline const char *lapd_state_name(enum lapd_state state)
static void lapd_start_t200(struct lapd_datalink *dl)
{
- if (osmo_timer_pending(&dl->t200))
- return;
- LOGDL(dl, LOGL_INFO, "start T200 (timeout=%d.%06ds)\n",
- dl->t200_sec, dl->t200_usec);
- osmo_timer_schedule(&dl->t200, dl->t200_sec, dl->t200_usec);
+ if ((dl->lapd_flags & LAPD_F_RTS)) {
+ if (dl->t200_rts != LAPD_T200_RTS_OFF)
+ return;
+ LOGDL(dl, LOGL_INFO, "Start T200. (pending until triggered by RTS)\n");
+ dl->t200_rts = LAPD_T200_RTS_PENDING;
+ } else {
+ if (osmo_timer_pending(&dl->t200))
+ return;
+ LOGDL(dl, LOGL_INFO, "Start T200 (timeout=%d.%06ds).\n", dl->t200_sec, dl->t200_usec);
+ osmo_timer_schedule(&dl->t200, dl->t200_sec, dl->t200_usec);
+ }
+}
+
+/*! Handle timeout condition of T200 in RTS mode.
+ * The caller (LAPDm code) implements the T200 timer and must detect timeout condition.
+ * The function gets called by LAPDm code when it detects a timeout of T200.
+ * \param[in] dl caller-allocated datalink structure */
+int lapd_t200_timeout(struct lapd_datalink *dl)
+{
+ OSMO_ASSERT((dl->lapd_flags & LAPD_F_RTS));
+
+ if (dl->t200_rts != LAPD_T200_RTS_RUNNING)
+ return -EINVAL;
+
+ dl->t200_rts = LAPD_T200_RTS_OFF;
+
+ lapd_t200_cb(dl);
+
+ return 0;
}
static void lapd_start_t203(struct lapd_datalink *dl)
@@ -219,10 +245,24 @@ static void lapd_start_t203(struct lapd_datalink *dl)
static void lapd_stop_t200(struct lapd_datalink *dl)
{
- if (!osmo_timer_pending(&dl->t200))
- return;
+ if ((dl->lapd_flags & LAPD_F_RTS)) {
+ if (dl->t200_rts == LAPD_T200_RTS_OFF)
+ return;
+ dl->t200_rts = LAPD_T200_RTS_OFF;
+ } else {
+ if (!osmo_timer_pending(&dl->t200))
+ return;
+ osmo_timer_del(&dl->t200);
+ }
LOGDL(dl, LOGL_INFO, "stop T200\n");
- osmo_timer_del(&dl->t200);
+}
+
+static bool lapd_is_t200_started(struct lapd_datalink *dl)
+{
+ if ((dl->lapd_flags & LAPD_F_RTS))
+ return (dl->t200_rts != LAPD_T200_RTS_OFF);
+ else
+ return osmo_timer_pending(&dl->t200);
}
static void lapd_stop_t203(struct lapd_datalink *dl)
@@ -357,6 +397,21 @@ void lapd_dl_reset(struct lapd_datalink *dl)
lapd_dl_newstate(dl, LAPD_STATE_IDLE);
}
+/*! Set lapd_flags to change behaviour
+ * \param[in] dl \ref lapd_datalink instance
+ * \param[in] flags \ref lapd_flags */
+int lapd_dl_set_flags(struct lapd_datalink *dl, unsigned int flags)
+{
+ if (lapd_is_t200_started(dl) && (flags & LAPD_F_RTS) != (dl->lapd_flags & LAPD_F_RTS)) {
+ LOGDL(dl, LOGL_ERROR, "Changing RTS flag not allowed while T200 is running.\n");
+ return -EINVAL;
+ }
+
+ dl->lapd_flags = flags;
+
+ return 0;
+}
+
/* reset and de-allocate history buffer */
void lapd_dl_exit(struct lapd_datalink *dl)
{
@@ -596,6 +651,8 @@ static void lapd_t200_cb(void *data)
switch (dl->state) {
case LAPD_STATE_SABM_SENT:
/* 5.4.1.3 */
+ /* increment re-transmission counter */
+ dl->retrans_ctr++;
if (dl->retrans_ctr >= dl->n200_est_rel + 1) {
/* flush tx and send buffers */
lapd_dl_flush_tx(dl);
@@ -614,13 +671,13 @@ static void lapd_t200_cb(void *data)
}
/* retransmit SABM command */
lapd_send_resend(dl);
- /* increment re-transmission counter */
- dl->retrans_ctr++;
/* restart T200 (PH-READY-TO-SEND) */
lapd_start_t200(dl);
break;
case LAPD_STATE_DISC_SENT:
/* 5.4.4.3 */
+ /* increment re-transmission counter */
+ dl->retrans_ctr++;
if (dl->retrans_ctr >= dl->n200_est_rel + 1) {
/* send MDL ERROR INIDCATION to L3 */
mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx);
@@ -638,8 +695,6 @@ static void lapd_t200_cb(void *data)
}
/* retransmit DISC command */
lapd_send_resend(dl);
- /* increment re-transmission counter */
- dl->retrans_ctr++;
/* restart T200 (PH-READY-TO-SEND) */
lapd_start_t200(dl);
break;
@@ -741,12 +796,14 @@ static void lapd_t203_cb(void *data)
lapd_start_t200(dl);
}
-/* 5.5.3.1: Common function to acknowlege frames up to the given N(R) value */
-static void lapd_acknowledge(struct lapd_msg_ctx *lctx)
+/* 5.5.3.1: Common function to acknowlege frames up to the given N(R) value
+ * In case of a sequence error, the cause is returned with negative sign. */
+static int lapd_acknowledge(struct lapd_msg_ctx *lctx)
{
struct lapd_datalink *dl = lctx->dl;
uint8_t nr = lctx->n_recv;
- int s = 0, rej = 0, t200_reset = 0;
+ int s = 0, rej = 0;
+ bool t200_reset = false;
int i, h;
/* supervisory frame ? */
@@ -771,9 +828,8 @@ static void lapd_acknowledge(struct lapd_msg_ctx *lctx)
* link layer entity shall reset the timer T200 on
* receipt of a valid I frame with N(R) higher than V(A),
* or an REJ with an N(R) equal to V(A). */
- if ((!rej && nr != dl->v_ack)
- || (rej && nr == dl->v_ack)) {
- t200_reset = 1;
+ if ((!rej && nr != dl->v_ack) || (rej && nr == dl->v_ack)) {
+ t200_reset = true;
lapd_stop_t200(dl);
/* 5.5.3.1 Note 1 + 2 imply timer recovery cond. */
}
@@ -781,10 +837,9 @@ static void lapd_acknowledge(struct lapd_msg_ctx *lctx)
* N(R) is called valid, if and only if
* (N(R)-V(A)) mod 8 <= (V(S)-V(A)) mod 8.
*/
- if (sub_mod(nr, dl->v_ack, dl->v_range)
- > sub_mod(dl->v_send, dl->v_ack, dl->v_range)) {
+ if (sub_mod(nr, dl->v_ack, dl->v_range) > sub_mod(dl->v_send, dl->v_ack, dl->v_range)) {
LOGDL(dl, LOGL_NOTICE, "N(R) sequence error\n");
- mdl_error(MDL_CAUSE_SEQ_ERR, lctx);
+ return -MDL_CAUSE_SEQ_ERR;
}
}
@@ -805,11 +860,10 @@ static void lapd_acknowledge(struct lapd_msg_ctx *lctx)
/* Stop T203, if running */
lapd_stop_t203(dl);
/* Start T203, if T200 is not running in MF EST state, if enabled */
- if (!osmo_timer_pending(&dl->t200)
- && (dl->t203_sec || dl->t203_usec)
- && (dl->state == LAPD_STATE_MF_EST)) {
+ if (!lapd_is_t200_started(dl) && (dl->t203_sec || dl->t203_usec) && (dl->state == LAPD_STATE_MF_EST))
lapd_start_t203(dl);
- }
+
+ return 0;
}
/* L1 -> L2 */
@@ -946,7 +1000,7 @@ static int lapd_rx_u_sabm(struct msgb *msg, struct lapd_msg_ctx *lctx)
dl->cont_res = lapd_msgb_alloc(length, "CONT RES");
memcpy(msgb_put(dl->cont_res, length), msg->l3h,
length);
- LOGDL(dl, LOGL_NOTICE, "Store content res.\n");
+ LOGDL(dl, LOGL_INFO, "Store content res.\n");
}
/* send notification to L3 */
if (length == 0) {
@@ -1246,7 +1300,7 @@ static int lapd_rx_u_ua(struct msgb *msg, struct lapd_msg_ctx *lctx)
/* enter multiple-frame-established state */
lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
/* send outstanding frames, if any (resume / reconnect) */
- lapd_send_i(lctx, __LINE__);
+ lapd_send_i(dl, __LINE__, false);
/* send notification to L3 */
rc = send_dl_simple(PRIM_DL_EST, PRIM_OP_CONFIRM, lctx);
msgb_free(msg);
@@ -1369,7 +1423,7 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
}
/* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
+ lapd_send_i(dl, __LINE__, false);
break;
case LAPD_S_RNR:
@@ -1380,6 +1434,10 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
/* 5.5.5 */
/* Set peer receiver busy condition */
dl->peer_busy = 1;
+ /* Flush pending messages in TX queue. */
+ lapd_dl_flush_tx_queue(dl);
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
if (lctx->p_f) {
if (lctx->cr == dl->cr.rem2loc.cmd) {
@@ -1406,7 +1464,7 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
LOGDL(dl, LOGL_INFO, "RNR not polling/final state received\n");
/* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
+ lapd_send_i(dl, __LINE__, false);
break;
case LAPD_S_REJ:
@@ -1420,6 +1478,8 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
dl->peer_busy = 0;
/* V(S) and V(A) to the N(R) in the REJ frame */
dl->v_send = dl->v_ack = lctx->n_recv;
+ /* Flush pending messages in TX queue. */
+ lapd_dl_flush_tx_queue(dl);
/* stop Timer T200 */
lapd_stop_t200(dl);
/* 5.5.3.2 */
@@ -1456,6 +1516,8 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
dl->peer_busy = 0;
/* V(S) and V(A) to the N(R) in the REJ frame */
dl->v_send = dl->v_ack = lctx->n_recv;
+ /* Flush pending messages in TX queue. */
+ lapd_dl_flush_tx_queue(dl);
/* stop Timer T200 */
lapd_stop_t200(dl);
/* 5.5.7 Clear timer recovery condition */
@@ -1465,6 +1527,8 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
dl->peer_busy = 0;
/* V(S) and V(A) to the N(R) in the REJ frame */
dl->v_send = dl->v_ack = lctx->n_recv;
+ /* Flush pending messages in TX queue. */
+ lapd_dl_flush_tx_queue(dl);
/* 5.5.3.2 */
if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) {
if (!dl->own_busy && !dl->seq_err_cond) {
@@ -1491,8 +1555,8 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
/* FIXME: 5.5.4.2 2) */
- /* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
+ /* Send message, if possible due to acknowledged data and new V(S) and V(A). */
+ lapd_send_i(dl, __LINE__, false);
break;
default:
@@ -1514,6 +1578,8 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
uint8_t ns = lctx->n_send;
int length = lctx->length;
int rc;
+ bool i_frame_in_queue = false;
+ int mdl_cause = 0;
LOGDL(dl, LOGL_INFO, "I received in state %s on SAPI(%u)\n",
lapd_state_name(dl->state), lctx->sapi);
@@ -1571,9 +1637,8 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
ns, dl->v_recv, lapd_state_name(dl->state));
/* discard data */
msgb_free(msg);
- if (dl->seq_err_cond != 1) {
- /* FIXME: help me understand what exactly todo here
- */
+ /* Send reject, but suppress second reject if LAPD_F_DROP_2ND_REJ flag is set. */
+ if (dl->seq_err_cond != 1 || !(dl->lapd_flags & LAPD_F_DROP_2ND_REJ)) {
dl->seq_err_cond = 1;
lapd_send_rej(lctx, lctx->p_f);
} else {
@@ -1591,10 +1656,12 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
}
/* Even if N(s) sequence error, acknowledge to N(R)-1 */
/* 5.5.3.1: Acknowlege all transmitted frames up the N(R)-1 */
- lapd_acknowledge(lctx); /* V(A) is also set here */
+ mdl_cause = lapd_acknowledge(lctx); /* V(A) is also set here */
+ if (mdl_cause < 0)
+ mdl_error(-mdl_cause, lctx);
/* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
+ lapd_send_i(dl, __LINE__, false);
return 0;
}
@@ -1604,8 +1671,15 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
dl->v_recv = inc_mod(dl->v_recv, dl->v_range);
LOGDL(dl, LOGL_INFO, "incrementing V(R) to %u\n", dl->v_recv);
+ /* Update all pending frames in the queue to the new V(R) state. */
+ if (dl->update_pending_frames) {
+ rc = dl->update_pending_frames(lctx);
+ if (!rc)
+ i_frame_in_queue = true;
+ }
+
/* 5.5.3.1: Acknowlege all transmitted frames up the the N(R)-1 */
- lapd_acknowledge(lctx); /* V(A) is also set here */
+ mdl_cause = lapd_acknowledge(lctx); /* V(A) is also set here */
/* Only if we are not in own receiver busy condition */
if (!dl->own_busy) {
@@ -1650,6 +1724,10 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
} else
LOGDL(dl, LOGL_INFO, "I frame ignored during own receiver busy condition\n");
+ /* Indicate sequence error, if exists. */
+ if (mdl_cause < 0)
+ mdl_error(-mdl_cause, lctx);
+
/* Check for P bit */
if (lctx->p_f) {
/* 5.5.2.1 */
@@ -1668,14 +1746,8 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
/* check if we are not in own receiver busy */
if (!dl->own_busy) {
/* NOTE: V(R) is already set above */
- rc = lapd_send_i(lctx, __LINE__);
-
- /* if update_pending_iframe returns 0 it updated
- * the lapd header of an iframe in the tx queue */
- if (rc && dl->update_pending_frames)
- rc = dl->update_pending_frames(lctx);
-
- if (rc) {
+ rc = lapd_send_i(dl, __LINE__, false);
+ if (rc && !i_frame_in_queue) {
LOGDL(dl, LOGL_INFO, "we are not busy and have no pending data, "
"send RR\n");
/* Send RR with F=0 */
@@ -1691,7 +1763,7 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
}
/* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
+ lapd_send_i(dl, __LINE__, false);
return rc;
}
@@ -1712,13 +1784,38 @@ int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx)
rc = lapd_rx_i(msg, lctx);
break;
default:
- LOGDL(lctx->dl, LOGL_NOTICE, "unknown LAPD format\n");
+ LOGDL(lctx->dl, LOGL_NOTICE, "unknown LAPD format 0x%02x\n", lctx->format);
msgb_free(msg);
rc = -EINVAL;
}
return rc;
}
+/*! Enqueue next LAPD frame and run pending T200. (Must be called when frame is ready to send.)
+ * The caller (LAPDm code) calls this function before it sends the next frame.
+ * If there is no frame in the TX queue, LAPD will enqueue next I-frame, if possible.
+ * If the T200 is pending, it is changed to running state.
+ * \param[in] lctx LAPD context
+ * \param[out] rc set to 1, if timer T200 state changed to running, set to 0, if not. */
+int lapd_ph_rts_ind(struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+
+ /* If there is no pending frame, try to enqueue next I frame. */
+ if (llist_empty(&dl->tx_queue) && (dl->state == LAPD_STATE_MF_EST || dl->state == LAPD_STATE_TIMER_RECOV)) {
+ /* Send an I frame, if there are pending outgoing messages. */
+ lapd_send_i(dl, __LINE__, true);
+ }
+
+ /* Run T200 at RTS, if pending. Tell caller that is has been started. (rc = 1) */
+ if (dl->t200_rts == LAPD_T200_RTS_PENDING) {
+ dl->t200_rts = LAPD_T200_RTS_RUNNING;
+ return 1;
+ }
+
+ return 0;
+}
+
/* L3 -> L2 */
/* send unit data */
@@ -1742,6 +1839,20 @@ static int lapd_udata_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
return dl->send_ph_data_req(&nctx, msg);
}
+static void msg_to_tx_hist(struct lapd_history *tx_hist, const struct msgb *msg, int length, int more)
+{
+ tx_hist->msg = lapd_msgb_alloc(msg->len, "HIST");
+ tx_hist->more = more;
+ msgb_put(tx_hist->msg, msg->len);
+ if (length)
+ memcpy(tx_hist->msg->data, msg->l3h, msg->len);
+}
+
+static void msg_to_tx_hist0(struct lapd_datalink *dl, const struct msgb *msg)
+{
+ return msg_to_tx_hist(&dl->tx_hist[0], msg, msg->len, 0);
+}
+
/* request link establishment */
static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
{
@@ -1780,11 +1891,8 @@ static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
nctx.more = 0;
/* Transmit-buffer carries exactly one segment */
- dl->tx_hist[0].msg = lapd_msgb_alloc(msg->len, "HIST");
- msgb_put(dl->tx_hist[0].msg, msg->len);
- if (msg->len)
- memcpy(dl->tx_hist[0].msg->data, msg->l3h, msg->len);
- dl->tx_hist[0].more = 0;
+ msg_to_tx_hist0(dl, msg);
+
/* set Vs to 0, because it is used as index when resending SABM */
dl->v_send = 0;
@@ -1818,15 +1926,15 @@ static int lapd_data_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
msgb_enqueue(&dl->send_queue, msg);
/* Send message, if possible */
- lapd_send_i(&dl->lctx, __LINE__);
+ lapd_send_i(dl, __LINE__, false);
return 0;
}
/* Send next I frame from queued/buffered data */
-static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
+static int lapd_send_i(struct lapd_datalink *dl, int line, bool rts)
{
- struct lapd_datalink *dl = lctx->dl;
+ struct lapd_msg_ctx *lctx = &dl->lctx;
uint8_t k = dl->k;
uint8_t h;
struct msgb *msg;
@@ -1834,18 +1942,26 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
int rc = - 1; /* we sent nothing */
struct lapd_msg_ctx nctx;
+ if (!rts)
+ LOGDL(dl, LOGL_INFO, "%s() called from line %d\n", __func__, line);
- LOGDL(dl, LOGL_INFO, "%s() called from line %d\n", __func__, line);
+ if ((dl->lapd_flags & LAPD_F_RTS) && !llist_empty(&dl->tx_queue)) {
+ if (!rts)
+ LOGDL(dl, LOGL_INFO, "There is a frame in the TX queue, not checking for sending I frame.\n");
+ return rc;
+ }
next_frame:
if (dl->peer_busy) {
- LOGDL(dl, LOGL_INFO, "peer busy, not sending\n");
+ if (!rts)
+ LOGDL(dl, LOGL_INFO, "Peer busy, not sending.\n");
return rc;
}
if (dl->state == LAPD_STATE_TIMER_RECOV) {
- LOGDL(dl, LOGL_INFO, "timer recovery, not sending\n");
+ if (!rts)
+ LOGDL(dl, LOGL_INFO, "Timer recovery, not sending.\n");
return rc;
}
@@ -1856,8 +1972,9 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
* of the error recovery procedures as described in subclauses 5.5.4 and
* 5.5.7. */
if (dl->v_send == add_mod(dl->v_ack, k, dl->v_range)) {
- LOGDL(dl, LOGL_INFO, "k frames outstanding, not sending more "
- "(k=%u V(S)=%u V(A)=%u)\n", k, dl->v_send, dl->v_ack);
+ if (!rts)
+ LOGDL(dl, LOGL_INFO, "k frames outstanding, not sending more. (k=%u V(S)=%u V(A)=%u)\n",
+ k, dl->v_send, dl->v_ack);
return rc;
}
@@ -1899,7 +2016,7 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
msg = lapd_msgb_alloc(length, "LAPD I");
msg->l3h = msgb_put(msg, length);
/* assemble message */
- memcpy(&nctx, &dl->lctx, sizeof(nctx));
+ memcpy(&nctx, lctx, sizeof(nctx));
/* keep nctx.ldp */
/* keep nctx.sapi */
/* keep nctx.tei */
@@ -1917,11 +2034,8 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
memcpy(msg->l3h, dl->send_buffer->l3h + dl->send_out,
length);
/* store in tx_hist */
- dl->tx_hist[h].msg = lapd_msgb_alloc(msg->len, "HIST");
- msgb_put(dl->tx_hist[h].msg, msg->len);
- if (length)
- memcpy(dl->tx_hist[h].msg->data, msg->l3h, msg->len);
- dl->tx_hist[h].more = nctx.more;
+ msg_to_tx_hist(&dl->tx_hist[h], msg, length, nctx.more);
+
/* Add length to track how much is already in the tx buffer */
dl->send_out += length;
} else {
@@ -1932,7 +2046,7 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
msg = lapd_msgb_alloc(length, "LAPD I resend");
msg->l3h = msgb_put(msg, length);
/* assemble message */
- memcpy(&nctx, &dl->lctx, sizeof(nctx));
+ memcpy(&nctx, lctx, sizeof(nctx));
/* keep nctx.ldp */
/* keep nctx.sapi */
/* keep nctx.tei */
@@ -1954,7 +2068,7 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
/* If timer T200 is not running at the time right before transmitting a
* frame, when the PH-READY-TO-SEND primitive is received from the
* physical layer., it shall be set. */
- if (!osmo_timer_pending(&dl->t200)) {
+ if (!lapd_is_t200_started(dl)) {
/* stop Timer T203, if running */
lapd_stop_t203(dl);
/* start Timer T200 */
@@ -1963,7 +2077,11 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
dl->send_ph_data_req(&nctx, msg);
- rc = 0; /* we sent something */
+ /* When using RTS, we send only one frame. */
+ if ((dl->lapd_flags & LAPD_F_RTS))
+ return 0;
+
+ rc = 0; /* We sent an I frame, so sending RR frame is not required. */
goto next_frame;
}
@@ -2042,11 +2160,8 @@ static int lapd_res_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
nctx.length = 0;
nctx.more = 0;
- dl->tx_hist[0].msg = lapd_msgb_alloc(msg->len, "HIST");
- msgb_put(dl->tx_hist[0].msg, msg->len);
- if (msg->len)
- memcpy(dl->tx_hist[0].msg->data, msg->l3h, msg->len);
- dl->tx_hist[0].more = 0;
+ msg_to_tx_hist0(dl, msg);
+
/* set Vs to 0, because it is used as index when resending SABM */
dl->v_send = 0;
@@ -2106,11 +2221,8 @@ static int lapd_rel_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
nctx.length = 0;
nctx.more = 0;
- dl->tx_hist[0].msg = lapd_msgb_alloc(msg->len, "HIST");
- msgb_put(dl->tx_hist[0].msg, msg->len);
- if (msg->len)
- memcpy(dl->tx_hist[0].msg->data, msg->l3h, msg->len);
- dl->tx_hist[0].more = 0;
+ msg_to_tx_hist0(dl, msg);
+
/* set Vs to 0, because it is used as index when resending DISC */
dl->v_send = 0;
diff --git a/src/isdn/libosmoisdn.map b/src/isdn/libosmoisdn.map
new file mode 100644
index 00000000..8f34f0d8
--- /dev/null
+++ b/src/isdn/libosmoisdn.map
@@ -0,0 +1,52 @@
+LIBOSMOISDN_1.0 {
+global:
+
+tall_lapd_ctx;
+lapd_dl_exit;
+lapd_dl_init;
+lapd_dl_init2;
+lapd_dl_set_name;
+lapd_dl_reset;
+lapd_dl_set_flags;
+lapd_msgb_alloc;
+lapd_ph_data_ind;
+lapd_ph_rts_ind;
+lapd_recv_dlsap;
+lapd_set_mode;
+lapd_state_names;
+lapd_t200_timeout;
+
+osmo_i460_demux_in;
+osmo_i460_mux_enqueue;
+osmo_i460_mux_out;
+osmo_i460_subchan_add;
+osmo_i460_subchan_del;
+osmo_i460_subchan_count;
+osmo_i460_ts_init;
+
+osmo_v110_decode_frame;
+osmo_v110_encode_frame;
+osmo_v110_ubit_dump;
+osmo_v110_e1e2e3;
+osmo_v110_sync_ra1_get_user_data_chunk_bitlen;
+osmo_v110_sync_ra1_get_user_data_rate;
+osmo_v110_sync_ra1_get_intermediate_rate;
+osmo_v110_sync_ra1_user_to_ir;
+osmo_v110_sync_ra1_ir_to_user;
+
+osmo_v110_ta_alloc;
+osmo_v110_ta_free;
+osmo_v110_ta_set_timer_val_ms;
+osmo_v110_ta_frame_in;
+osmo_v110_ta_frame_out;
+osmo_v110_ta_sync_ind;
+osmo_v110_ta_desync_ind;
+osmo_v110_ta_get_status;
+osmo_v110_ta_get_circuit;
+osmo_v110_ta_set_circuit;
+
+osmo_v110_ta_circuit_names;
+osmo_v110_ta_circuit_descs;
+
+local: *;
+};
diff --git a/src/isdn/v110.c b/src/isdn/v110.c
new file mode 100644
index 00000000..b4bc4e30
--- /dev/null
+++ b/src/isdn/v110.c
@@ -0,0 +1,590 @@
+/* V.110 frames according to ITU-T V.110
+ *
+ * This code implements the following functionality:
+ * - parsing/encoding of osmo_v110_decoded_frame from/to actual 80-bit V.110 frame
+ * - synchronous rate adapting of user bit rate to V.110 D-bits as per Table 6
+ *
+ * It is (at least initially) a very "naive" implementation, as it first and foremost
+ * aims to be functional and correct, rather than efficient in any way. Hence it
+ * operates on unpacked bits (ubit_t, 1 bit per byte), and has various intermediate
+ * representations and indirect function calls. If needed, a more optimized variant
+ * can always be developed later on.
+ */
+
+/* (C) 2022 by Harald Welte <laforge@osmocom.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/bits.h>
+
+#include <osmocom/isdn/v110.h>
+
+/*************************************************************************
+ * V.110 frame decoding/encoding (ubits <-> struct with D/S/X/E bits)
+ *************************************************************************/
+
+/*! Decode a 80-bit V.110 frame present as 80 ubits into a struct osmo_v110_decoded_frame.
+ * \param[out] fr caller-allocated output data structure, filled by this function
+ * \param[in] ra_bits One V.110 frame as 80 unpacked bits.
+ * \param[in] n_bits number of unpacked bits provided in ra_bits
+ * \returns 0 in case of success; negative on error. */
+int osmo_v110_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits)
+{
+ if (n_bits < 80)
+ return -EINVAL;
+
+ /* X1 .. X2 */
+ fr->x_bits[0] = ra_bits[2 * 8 + 7];
+ fr->x_bits[1] = ra_bits[7 * 8 + 7];
+
+ /* S1, S3, S4, S6, S8, S9 */
+ fr->s_bits[0] = ra_bits[1 * 8 + 7];
+ fr->s_bits[2] = ra_bits[3 * 8 + 7];
+ fr->s_bits[3] = ra_bits[4 * 8 + 7];
+ fr->s_bits[5] = ra_bits[6 * 8 + 7];
+ fr->s_bits[7] = ra_bits[8 * 8 + 7];
+ fr->s_bits[8] = ra_bits[9 * 8 + 7];
+
+ /* E1 .. E7 */
+ memcpy(fr->e_bits, ra_bits + 5 * 8 + 1, 7);
+
+ /* D-bits */
+ memcpy(fr->d_bits + 0 * 6, ra_bits + 1 * 8 + 1, 6);
+ memcpy(fr->d_bits + 1 * 6, ra_bits + 2 * 8 + 1, 6);
+ memcpy(fr->d_bits + 2 * 6, ra_bits + 3 * 8 + 1, 6);
+ memcpy(fr->d_bits + 3 * 6, ra_bits + 4 * 8 + 1, 6);
+
+ memcpy(fr->d_bits + 4 * 6, ra_bits + 6 * 8 + 1, 6);
+ memcpy(fr->d_bits + 5 * 6, ra_bits + 7 * 8 + 1, 6);
+ memcpy(fr->d_bits + 6 * 6, ra_bits + 8 * 8 + 1, 6);
+ memcpy(fr->d_bits + 7 * 6, ra_bits + 9 * 8 + 1, 6);
+
+ return 0;
+}
+
+/*! Encode a struct osmo_v110_decoded_frame into an 80-bit V.110 frame as ubits.
+ * \param[out] ra_bits caller-provided output buffer at leat 80 ubits large
+ * \param[in] n_bits length of ra_bits. Must be at least 80.
+ * \param[in] input data structure
+ * \returns number of bits written to ra_bits */
+int osmo_v110_encode_frame(ubit_t *ra_bits, size_t n_bits, const struct osmo_v110_decoded_frame *fr)
+{
+ if (n_bits < 80)
+ return -ENOSPC;
+
+ /* alignment pattern */
+ memset(ra_bits+0, 0, 8);
+ for (int i = 1; i < 10; i++)
+ ra_bits[i*8] = 1;
+
+ /* X1 .. X2 */
+ ra_bits[2 * 8 + 7] = fr->x_bits[0];
+ ra_bits[7 * 8 + 7] = fr->x_bits[1];
+
+ /* S1, S3, S4, S6, S8, S9 */
+ ra_bits[1 * 8 + 7] = fr->s_bits[0];
+ ra_bits[3 * 8 + 7] = fr->s_bits[2];
+ ra_bits[4 * 8 + 7] = fr->s_bits[3];
+ ra_bits[6 * 8 + 7] = fr->s_bits[5];
+ ra_bits[8 * 8 + 7] = fr->s_bits[7];
+ ra_bits[9 * 8 + 7] = fr->s_bits[8];
+
+ /* E1 .. E7 */
+ memcpy(ra_bits + 5 * 8 + 1, fr->e_bits, 7);
+
+ /* D-bits */
+ memcpy(ra_bits + 1 * 8 + 1, fr->d_bits + 0 * 6, 6);
+ memcpy(ra_bits + 2 * 8 + 1, fr->d_bits + 1 * 6, 6);
+ memcpy(ra_bits + 3 * 8 + 1, fr->d_bits + 2 * 6, 6);
+ memcpy(ra_bits + 4 * 8 + 1, fr->d_bits + 3 * 6, 6);
+
+ memcpy(ra_bits + 6 * 8 + 1, fr->d_bits + 4 * 6, 6);
+ memcpy(ra_bits + 7 * 8 + 1, fr->d_bits + 5 * 6, 6);
+ memcpy(ra_bits + 8 * 8 + 1, fr->d_bits + 6 * 6, 6);
+ memcpy(ra_bits + 9 * 8 + 1, fr->d_bits + 7 * 6, 6);
+
+ return 10 * 8;
+}
+
+/*! Print a encoded V.110 frame in the same table-like structure as the spec.
+ * \param outf output FILE stream to which to dump
+ * \param[in] fr unpacked bits to dump
+ * \param[in] in_len length of unpacked bits available at fr. */
+void osmo_v110_ubit_dump(FILE *outf, const ubit_t *fr, size_t in_len)
+{
+ if (in_len < 80)
+ fprintf(outf, "short input data\n");
+
+ for (unsigned int octet = 0; octet < 10; octet++) {
+ fprintf(outf, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ fr[octet * 8 + 0], fr[octet * 8 + 1], fr[octet * 8 + 2], fr[octet * 8 + 3],
+ fr[octet * 8 + 4], fr[octet * 8 + 5], fr[octet * 8 + 6], fr[octet * 8 + 7]);
+ }
+}
+
+/*************************************************************************
+ * RA1 synchronous rate adaptation
+ *************************************************************************/
+
+/* I actually couldn't find any reference as to the value of F(ill) bits */
+#define F 1
+
+/*! E1/E2/E3 bit values as per Table 5/V.110 */
+const ubit_t osmo_v110_e1e2e3[_NUM_OSMO_V110_SYNC_RA1][3] = {
+ [OSMO_V110_SYNC_RA1_600] = { 1, 0, 0 },
+ [OSMO_V110_SYNC_RA1_1200] = { 0, 1, 0 },
+ [OSMO_V110_SYNC_RA1_2400] = { 1, 1, 0 },
+ [OSMO_V110_SYNC_RA1_4800] = { 0, 1, 1 },
+ [OSMO_V110_SYNC_RA1_7200] = { 1, 0, 1 },
+ [OSMO_V110_SYNC_RA1_9600] = { 0, 1, 1 },
+ [OSMO_V110_SYNC_RA1_12000] = { 0, 0, 1 },
+ [OSMO_V110_SYNC_RA1_14400] = { 1, 0, 1 },
+ [OSMO_V110_SYNC_RA1_19200] = { 0, 1, 1 },
+ [OSMO_V110_SYNC_RA1_24000] = { 0, 0, 1 },
+ [OSMO_V110_SYNC_RA1_28800] = { 1, 0, 1 },
+ [OSMO_V110_SYNC_RA1_38400] = { 0, 1, 1 },
+};
+
+/*! Adapt from 6 synchronous 600bit/s input bits to a decoded V.110 frame.
+ * \param[out] fr caller-allocated output frame to which E+D bits are stored
+ * \param[in] d_in input user bits
+ * \param[in] in_len number of bits in d_in. Must be 6.
+ * \returns 0 on success; negative in case of error. */
+static int v110_adapt_600_to_IR8000(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
+{
+ if (in_len != 6)
+ return -EINVAL;
+
+ /* Table 6a / V.110 */
+ osmo_v110_e1e2e3_set(fr->e_bits, OSMO_V110_SYNC_RA1_600);
+ for (int i = 0; i < 6; i++)
+ memset(fr->d_bits + i*8, d_in[i], 8);
+
+ return 0;
+}
+
+static int v110_adapt_IR8000_to_600(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
+{
+ if (out_len < 6)
+ return -ENOSPC;
+
+ if (osmo_v110_e1e2e3_cmp(fr->e_bits, OSMO_V110_SYNC_RA1_600))
+ return -EINVAL;
+
+ for (int i = 0; i < 6; i++) {
+ /* we only use one of the bits, not some kind of consistency check or majority vote */
+ d_out[i] = fr->d_bits[i*8];
+ }
+
+ return 6;
+}
+
+/*! Adapt from 12 synchronous 1200bit/s input bits to a decoded V.110 frame.
+ * \param[out] fr caller-allocated output frame to which E+D bits are stored
+ * \param[in] d_in input user bits
+ * \param[in] in_len number of bits in d_in. Must be 12.
+ * \returns 0 on success; negative in case of error. */
+static int v110_adapt_1200_to_IR8000(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
+{
+ if (in_len != 12)
+ return -EINVAL;
+
+ /* Table 6b / V.110 */
+ osmo_v110_e1e2e3_set(fr->e_bits, OSMO_V110_SYNC_RA1_1200);
+ for (int i = 0; i < 12; i++)
+ memset(fr->d_bits + i*4, d_in[i], 4);
+
+ return 0;
+}
+
+static int v110_adapt_IR8000_to_1200(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
+{
+ if (out_len < 12)
+ return -ENOSPC;
+
+ if (osmo_v110_e1e2e3_cmp(fr->e_bits, OSMO_V110_SYNC_RA1_1200))
+ return -EINVAL;
+
+ for (int i = 0; i < 12; i++) {
+ /* we only use one of the bits, not some kind of consistency check or majority vote */
+ d_out[i] = fr->d_bits[i*4];
+ }
+
+ return 12;
+}
+
+/*! Adapt from 24 synchronous 2400bit/s input bits to a decoded V.110 frame.
+ * \param[out] fr caller-allocated output frame to which E+D bits are stored
+ * \param[in] d_in input user bits
+ * \param[in] in_len number of bits in d_in. Must be 24.
+ * \returns 0 on success; negative in case of error. */
+static int v110_adapt_2400_to_IR8000(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
+{
+ if (in_len != 24)
+ return -EINVAL;
+
+ /* Table 6c / V.110 */
+ osmo_v110_e1e2e3_set(fr->e_bits, OSMO_V110_SYNC_RA1_2400);
+ for (int i = 0; i < 24; i++) {
+ fr->d_bits[i*2 + 0] = d_in[i];
+ fr->d_bits[i*2 + 1] = d_in[i];
+ }
+
+ return 0;
+}
+
+static int v110_adapt_IR8000_to_2400(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
+{
+ if (out_len < 24)
+ return -ENOSPC;
+
+ /* Table 6c / V.110 */
+ if (osmo_v110_e1e2e3_cmp(fr->e_bits, OSMO_V110_SYNC_RA1_2400))
+ return -EINVAL;
+
+ for (int i = 0; i < 24; i++) {
+ /* we only use one of the bits, not some kind of consistency check or majority vote */
+ d_out[i] = fr->d_bits[i*2];
+ }
+
+ return 24;
+}
+
+/*! Adapt from 36 synchronous N x 3600bit/s input bits to a decoded V.110 frame.
+ * \param[out] fr caller-allocated output frame to which E+D bits are stored
+ * \param[in] d_in input user bits
+ * \param[in] in_len number of bits in d_in. Must be 36.
+ * \returns 0 on success; negative in case of error. */
+static int v110_adapt_Nx3600_to_IR(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
+{
+ int d_idx = 0;
+
+ if (in_len != 36)
+ return -EINVAL;
+
+ /* Table 6d / V.110 (7200 is one of Nx3600) */
+ osmo_v110_e1e2e3_set(fr->e_bits, OSMO_V110_SYNC_RA1_7200);
+
+ memcpy(fr->d_bits + d_idx, d_in + 0, 10); d_idx += 10; /* D1..D10 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 10, 2); d_idx += 2; /* D11..D12 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 12, 2); d_idx += 2; /* D13..D14 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 14, 14); d_idx += 14; /* D15..D28 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 28, 2); d_idx += 2; /* D29..D30 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 30, 2); d_idx += 2; /* D31..D32 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 32, 4); d_idx += 4; /* D33..D36 */
+
+ OSMO_ASSERT(d_idx == 48);
+
+ return 0;
+}
+
+static int v110_adapt_IR_to_Nx3600(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
+{
+ int d_idx = 0;
+
+ if (out_len < 36)
+ return -ENOSPC;
+
+ /* Table 6d / V.110 (7200 is one of Nx3600) */
+ if (osmo_v110_e1e2e3_cmp(fr->e_bits, OSMO_V110_SYNC_RA1_7200))
+ return -EINVAL;
+
+ memcpy(d_out + 0, fr->d_bits + d_idx, 10); d_idx += 10; /* D1..D10 */
+ d_idx += 2;
+ memcpy(d_out + 10, fr->d_bits + d_idx, 2); d_idx += 2; /* D11..D12 */
+ d_idx += 2;
+ memcpy(d_out + 12, fr->d_bits + d_idx, 2); d_idx += 2; /* D13..D14 */
+ d_idx += 2;
+ memcpy(d_out + 14, fr->d_bits + d_idx, 14); d_idx += 14;/* D15..D28 */
+ d_idx += 2;
+ memcpy(d_out + 28, fr->d_bits + d_idx, 2); d_idx += 2; /* D29..D30 */
+ d_idx += 2;
+ memcpy(d_out + 30, fr->d_bits + d_idx, 2); d_idx += 2; /* D31..D32 */
+ d_idx += 2;
+ memcpy(d_out + 32, fr->d_bits + d_idx, 4); d_idx += 4; /* D33..D36 */
+
+ OSMO_ASSERT(d_idx == 48);
+
+ return 36;
+}
+
+
+/*! Adapt from 48 synchronous N x 4800bit/s input bits to a decoded V.110 frame.
+ * \param[out] fr caller-allocated output frame to which E+D bits are stored
+ * \param[in] d_in input user bits
+ * \param[in] in_len number of bits in d_in. Must be 48.
+ * \returns 0 on success; negative in case of error. */
+static int v110_adapt_Nx4800_to_IR(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
+{
+ if (in_len != 48)
+ return -EINVAL;
+
+ /* Table 6e / V.110 (4800 is one of Nx4800) */
+ osmo_v110_e1e2e3_set(fr->e_bits, OSMO_V110_SYNC_RA1_4800);
+
+ memcpy(fr->d_bits, d_in, 48);
+
+ return 0;
+}
+
+static int v110_adapt_IR_to_Nx4800(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
+{
+ if (out_len < 48)
+ return -ENOSPC;
+
+ /* Table 6e / V.110 (4800 is one of Nx4800) */
+ if (osmo_v110_e1e2e3_cmp(fr->e_bits, OSMO_V110_SYNC_RA1_4800))
+ return -EINVAL;
+
+ memcpy(d_out, fr->d_bits, 48);
+
+ return 48;
+}
+
+/*! Adapt from 30 synchronous N x 12000bit/s input bits to a decoded V.110 frame.
+ * \param[out] fr caller-allocated output frame to which E+D bits are stored
+ * \param[in] d_in input user bits
+ * \param[in] in_len number of bits in d_in. Must be 30.
+ * \returns 0 on success; negative in case of error. */
+static int v110_adapt_Nx12000_to_IR(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
+{
+ int d_idx = 0;
+
+ if (in_len != 30)
+ return -EINVAL;
+
+ /* Table 6f / V.110 (12000 is one of Nx12000) */
+ osmo_v110_e1e2e3_set(fr->e_bits, OSMO_V110_SYNC_RA1_12000);
+
+ memcpy(fr->d_bits + d_idx, d_in + 0, 10); d_idx += 10; /* D1..D10 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 10, 2); d_idx += 2; /* D11..D12 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 12, 2); d_idx += 2; /* D13..D14 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ fr->d_bits[d_idx++] = d_in[14]; /* D15 */
+ memset(fr->d_bits + d_idx, F, 3); d_idx += 3;
+ memcpy(fr->d_bits + d_idx, d_in + 15, 10); d_idx += 10; /* D16..D25 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 25, 2); d_idx += 2; /* D26..D27 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 27, 2); d_idx += 2; /* D28..D29 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ fr->d_bits[d_idx++] = d_in[29]; /* D30 */
+ memset(fr->d_bits + d_idx, F, 3); d_idx += 3;
+
+ OSMO_ASSERT(d_idx == 48);
+
+ return 0;
+}
+
+static int v110_adapt_IR_to_Nx12000(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
+{
+ int d_idx = 0;
+
+ if (out_len < 30)
+ return -ENOSPC;
+
+ /* Table 6f / V.110 (12000 is one of Nx12000) */
+ if (osmo_v110_e1e2e3_cmp(fr->e_bits, OSMO_V110_SYNC_RA1_12000))
+ return -EINVAL;
+
+ memcpy(d_out + 0, fr->d_bits + d_idx, 10); d_idx += 10; /* D1..D10 */
+ d_idx += 2;
+ memcpy(d_out + 10, fr->d_bits + d_idx, 2); d_idx += 2; /* D11..D12 */
+ d_idx += 2;
+ memcpy(d_out + 12, fr->d_bits + d_idx, 2); d_idx += 2; /* D13..D14 */
+ d_idx += 2;
+ d_out[14] = fr->d_bits[d_idx++]; /* D15 */
+ d_idx += 3;
+ memcpy(d_out + 15, fr->d_bits + d_idx, 10); d_idx += 10;/* D16..D25 */
+ d_idx += 2;
+ memcpy(d_out + 25, fr->d_bits + d_idx, 2); d_idx += 2; /* D26..D27 */
+ d_idx += 2;
+ memcpy(d_out + 27, fr->d_bits + d_idx, 2); d_idx += 2; /* D28..D29 */
+ d_idx += 2;
+ d_out[29] = fr->d_bits[d_idx++]; /* D30 */
+ d_idx += 3;
+
+ OSMO_ASSERT(d_idx == 48);
+
+ return 30;
+}
+
+/* definition of a synchronous V.110 RA1 rate adaptation. There is one for each supported tuple
+ * of user data rate and intermediate rate (IR). */
+struct osmo_v110_sync_ra1 {
+ unsigned int data_rate;
+ unsigned int intermediate_rate;
+ unsigned int user_data_chunk_bits;
+ /*! RA1 function in user bitrate -> intermediate rate direction */
+ int (*adapt_user_to_ir)(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len);
+ /*! RA1 function in intermediate rate -> user bitrate direction */
+ int (*adapt_ir_to_user)(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr);
+};
+
+/* all of the synchronous data signalling rates; see Table 1/V.110 */
+static const struct osmo_v110_sync_ra1 osmo_v110_sync_ra1_def[_NUM_OSMO_V110_SYNC_RA1] = {
+ [OSMO_V110_SYNC_RA1_600] = {
+ .data_rate = 600,
+ .intermediate_rate = 8000,
+ .user_data_chunk_bits = 6,
+ .adapt_user_to_ir = v110_adapt_600_to_IR8000,
+ .adapt_ir_to_user = v110_adapt_IR8000_to_600,
+ },
+ [OSMO_V110_SYNC_RA1_1200] = {
+ .data_rate = 1200,
+ .intermediate_rate = 8000,
+ .user_data_chunk_bits = 12,
+ .adapt_user_to_ir = v110_adapt_1200_to_IR8000,
+ .adapt_ir_to_user = v110_adapt_IR8000_to_1200,
+ },
+ [OSMO_V110_SYNC_RA1_2400] = {
+ .data_rate = 2400,
+ .intermediate_rate = 8000,
+ .user_data_chunk_bits = 24,
+ .adapt_user_to_ir = v110_adapt_2400_to_IR8000,
+ .adapt_ir_to_user = v110_adapt_IR8000_to_2400,
+ },
+ [OSMO_V110_SYNC_RA1_4800] = {
+ .data_rate = 4800,
+ .intermediate_rate = 8000,
+ .user_data_chunk_bits = 48,
+ .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
+ },
+ [OSMO_V110_SYNC_RA1_7200] = {
+ .data_rate = 7200,
+ .intermediate_rate = 16000,
+ .user_data_chunk_bits = 36,
+ .adapt_user_to_ir = v110_adapt_Nx3600_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx3600,
+ },
+ [OSMO_V110_SYNC_RA1_9600] = {
+ .data_rate = 9600,
+ .intermediate_rate = 16000,
+ .user_data_chunk_bits = 48,
+ .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
+ },
+ [OSMO_V110_SYNC_RA1_12000] = {
+ .data_rate = 12000,
+ .intermediate_rate = 32000,
+ .user_data_chunk_bits = 30,
+ .adapt_user_to_ir = v110_adapt_Nx12000_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx12000,
+ },
+ [OSMO_V110_SYNC_RA1_14400] = {
+ .data_rate = 14400,
+ .intermediate_rate = 32000,
+ .user_data_chunk_bits = 36,
+ .adapt_user_to_ir = v110_adapt_Nx3600_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx3600,
+ },
+ [OSMO_V110_SYNC_RA1_19200] = {
+ .data_rate = 19200,
+ .intermediate_rate = 32000,
+ .user_data_chunk_bits = 48,
+ .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
+ },
+ [OSMO_V110_SYNC_RA1_24000] = {
+ .data_rate = 24000,
+ .intermediate_rate = 64000,
+ .user_data_chunk_bits = 30,
+ .adapt_user_to_ir = v110_adapt_Nx12000_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx12000,
+ },
+ [OSMO_V110_SYNC_RA1_28800] = {
+ .data_rate = 28800,
+ .intermediate_rate = 64000,
+ .user_data_chunk_bits = 36,
+ .adapt_user_to_ir = v110_adapt_Nx3600_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx3600,
+ },
+ [OSMO_V110_SYNC_RA1_38400] = {
+ .data_rate = 38400,
+ .intermediate_rate = 64000,
+ .user_data_chunk_bits = 48,
+ .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
+ },
+};
+
+/*! obtain the size (in number of bits) of the user data bits in one V.110
+ * frame for specified RA1 rate */
+int osmo_v110_sync_ra1_get_user_data_chunk_bitlen(enum osmo_v100_sync_ra1_rate rate)
+{
+ if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
+ return -EINVAL;
+
+ return osmo_v110_sync_ra1_def[rate].user_data_chunk_bits;
+}
+
+/*! obtain the user data rate (in bits/s) for specified RA1 rate */
+int osmo_v110_sync_ra1_get_user_data_rate(enum osmo_v100_sync_ra1_rate rate)
+{
+ if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
+ return -EINVAL;
+
+ return osmo_v110_sync_ra1_def[rate].data_rate;
+}
+
+/*! obtain the intermediate rate (in bits/s) for specified RA1 rate */
+int osmo_v110_sync_ra1_get_intermediate_rate(enum osmo_v100_sync_ra1_rate rate)
+{
+ if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
+ return -EINVAL;
+
+ return osmo_v110_sync_ra1_def[rate].intermediate_rate;
+}
+
+/*! perform V.110 RA1 function in user rate -> intermediate rate direction.
+ * \param[in] rate specification of the user bitrate
+ * \param[out] fr caller-allocated output buffer for the [decoded] V.110 frame generated
+ * \param[in] d_in input user data (unpacked bits)
+ * \param[in] in_len length of user input data (in number of bits)
+ * \returns 0 on success; negative in case of error */
+int osmo_v110_sync_ra1_user_to_ir(enum osmo_v100_sync_ra1_rate rate, struct osmo_v110_decoded_frame *fr,
+ const ubit_t *d_in, size_t in_len)
+{
+ if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
+ return -EINVAL;
+
+ return osmo_v110_sync_ra1_def[rate].adapt_user_to_ir(fr, d_in, in_len);
+}
+
+/*! perform V.110 RA1 function in intermediate rate -> user rate direction.
+ * \param[in] rate specification of the user bitrate
+ * \param[out] d_out caller-allocated output user data (unpacked bits)
+ * \param[out] out_len length of d_out output buffer
+ * \param[in] fr [decoded] V.110 frame used as input
+ * \returns number of unpacked bits written to d_out on success; negative in case of error */
+int osmo_v110_sync_ra1_ir_to_user(enum osmo_v100_sync_ra1_rate rate, ubit_t *d_out, size_t out_len,
+ const struct osmo_v110_decoded_frame *fr)
+{
+ if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
+ return -EINVAL;
+
+ return osmo_v110_sync_ra1_def[rate].adapt_ir_to_user(d_out, out_len, fr);
+}
diff --git a/src/isdn/v110_ta.c b/src/isdn/v110_ta.c
new file mode 100644
index 00000000..6730b20c
--- /dev/null
+++ b/src/isdn/v110_ta.c
@@ -0,0 +1,881 @@
+/*! \file v110_ta.c
+ * TA (Terminal Adapter) implementation as per ITU-T V.110. */
+/*
+ * (C) 2022 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Initial (Work-in-Progress) implementation by Harald Welte,
+ * completed and co-authored by Vadim Yanitskiy.
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/fsm.h>
+
+#include <osmocom/isdn/v110.h>
+#include <osmocom/isdn/v110_ta.h>
+
+#define S(x) (1 << (x))
+
+#define V24_FLAGMASK_IS_ON(flags, circuit) \
+ (((flags) & S(circuit)) != 0)
+
+#define V24_FLAGMASK_IS_OFF(flags, circuit) \
+ (((flags) & S(circuit)) == 0)
+
+#define V24_FLAGMASK_SET_ON(flags, circuit) \
+ (flags) |= S(circuit)
+
+#define V24_FLAGMASK_SET_OFF(flags, circuit) \
+ (flags) &= ~S(circuit)
+
+/* inverse logic: ON = binary 0; OFF = binary 1 */
+#define V110_SX_BIT_ON 0
+#define V110_SX_BIT_OFF 1
+
+const struct value_string osmo_v110_ta_circuit_names[] = {
+ { OSMO_V110_TA_C_105, "105/RTS" },
+ { OSMO_V110_TA_C_106, "106/CTS" },
+ { OSMO_V110_TA_C_107, "107/DSR" },
+ { OSMO_V110_TA_C_108, "108/DTR" },
+ { OSMO_V110_TA_C_109, "109/DCD" },
+ { OSMO_V110_TA_C_133, "133" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_v110_ta_circuit_descs[] = {
+ { OSMO_V110_TA_C_105, "Request to Send" },
+ { OSMO_V110_TA_C_106, "Clear to Send" },
+ { OSMO_V110_TA_C_107, "Data Set Ready" },
+ { OSMO_V110_TA_C_108, "Data Terminal Ready" },
+ { OSMO_V110_TA_C_109, "Data Carrier Detect" },
+ { OSMO_V110_TA_C_133, "Ready for receiving" },
+ { 0, NULL }
+};
+
+static const struct osmo_tdef v110_ta_tdef[] = {
+ { .T = OSMO_V110_TA_TIMER_X1,
+ .unit = OSMO_TDEF_MS, .default_val = 3000, /* suggested in 7.1.5 e) */
+ .desc = "ITU-T V.110 7.1.5 Loss of frame synchronization: sync recovery timer" },
+ { .T = OSMO_V110_TA_TIMER_T1,
+ .unit = OSMO_TDEF_MS, .default_val = 10000, /* suggested in 7.1.2.2 */
+ .desc = "ITU-T V.110 7.1.2 Connect TA to line: sync establishment timer" },
+ { .T = OSMO_V110_TA_TIMER_T2,
+ .unit = OSMO_TDEF_MS, .default_val = 5000, /* suggested in 7.1.4.1 */
+ .desc = "ITU-T V.110 7.1.4 Disconnect mode: disconnect confirmation timer" },
+ { /* end of list */ }
+};
+
+/*********************************************************************************
+ * V.110 TERMINAL ADAPTER FSM
+ *********************************************************************************/
+
+enum v110_ta_fsm_state {
+ V110_TA_ST_IDLE_READY, /* 7.1.1 Idle (or ready) state */
+ V110_TA_ST_CON_TA_TO_LINE, /* 7.1.2 Connect TA to line state */
+ V110_TA_ST_DATA_TRANSFER, /* 7.1.3 Data transfer state */
+ V110_TA_ST_DISCONNECTING, /* 7.1.4 Disconnect mode */
+ V110_TA_ST_RESYNCING, /* 7.1.5 Re-synchronizing state */
+};
+
+enum v110_ta_fsm_event {
+ V110_TA_EV_RX_FRAME_IND, /* a V.110 frame was received by the lower layer */
+ V110_TA_EV_TX_FRAME_RTS, /* a V.110 frame is to be sent by the lower layer */
+ V110_TA_EV_V24_STATUS_CHG, /* V.24 flag-mask has been updated by TE */
+ V110_TA_EV_SYNC_IND, /* the lower layer has synchronized to the frame clock */
+ V110_TA_EV_DESYNC_IND, /* the lower layer has lost frame clock synchronization */
+ V110_TA_EV_TIMEOUT, /* generic event for handling a timeout condition */
+};
+
+static const struct value_string v110_ta_fsm_event_names[] = {
+ { V110_TA_EV_RX_FRAME_IND, "RX_FRAME_IND" },
+ { V110_TA_EV_TX_FRAME_RTS, "TX_FRAME_RTS" },
+ { V110_TA_EV_V24_STATUS_CHG, "V24_STATUS_CHG" },
+ { V110_TA_EV_SYNC_IND, "SYNC_IND" },
+ { V110_TA_EV_DESYNC_IND, "DESYNC_IND" },
+ { V110_TA_EV_TIMEOUT, "TIMEOUT" },
+ { 0, NULL }
+};
+
+enum v110_ta_d_bit_mode {
+ V110_TA_DBIT_M_ALL_ZERO = 0, /* set all bits to binary '0' */
+ V110_TA_DBIT_M_ALL_ONE = 1, /* set all bits to binary '1' */
+ V110_TA_DBIT_M_FORWARD, /* forward D-bits to/from DTE */
+};
+
+struct v110_ta_state {
+ /*! V.24 status flags shared between DTE (user) and DCE (TA, us) */
+ unsigned int v24_flags;
+ struct {
+ /* what kind of D-bits to transmit in V.110 frames */
+ enum v110_ta_d_bit_mode d_bit_mode;
+ /* what to put in S-bits of transmitted V.110 frames */
+ ubit_t s_bits;
+ /* what to put in X-bits of transmitted V.110 frames */
+ ubit_t x_bits;
+ } tx;
+ struct {
+ enum v110_ta_d_bit_mode d_bit_mode;
+ } rx;
+};
+
+struct osmo_v110_ta {
+ const char *name;
+ struct osmo_tdef *Tdefs;
+ struct osmo_fsm_inst *fi;
+ struct osmo_v110_ta_cfg *cfg;
+ struct v110_ta_state state;
+};
+
+static inline bool v110_df_x_bits_are(const struct osmo_v110_decoded_frame *df, ubit_t cmp)
+{
+ return (df->x_bits[0] == cmp) && (df->x_bits[1] == cmp);
+}
+
+static inline bool v110_df_s_bits_are(const struct osmo_v110_decoded_frame *df, ubit_t cmp)
+{
+ /* ITU-T Table 2/V.110 (see also 5.1.2.3) defines the following S-bits:
+ * S1, S3, S4, S6, S8, S9 (6 bits total). However, fr->s_bits[] contains
+ * 9 (MAX_S_BITS) bits, including the undefined bits S2, S5, S7.
+ * Hence we must skip those undefined bits. */
+ static const uint8_t sbit_map[] = { 0, 2, 3, 5, 7, 8 };
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(sbit_map); i++) {
+ uint8_t idx = sbit_map[i];
+ if (df->s_bits[idx] != cmp)
+ return false;
+ }
+
+ return true;
+}
+
+static inline bool v110_df_d_bits_are(const struct osmo_v110_decoded_frame *df, ubit_t cmp)
+{
+ for (unsigned int i = 0; i < MAX_D_BITS; i++) {
+ if (df->d_bits[i] != cmp)
+ return false;
+ }
+
+ return true;
+}
+
+/* handle one V.110 frame and forward user bits to the application */
+static void v110_ta_handle_frame(const struct osmo_v110_ta *ta,
+ const struct osmo_v110_decoded_frame *df)
+{
+ const struct osmo_v110_ta_cfg *cfg = ta->cfg;
+ const struct v110_ta_state *ts = &ta->state;
+ ubit_t user_bits[MAX_D_BITS];
+ int num_user_bits;
+ int rc;
+
+ switch (ts->rx.d_bit_mode) {
+ case V110_TA_DBIT_M_ALL_ZERO:
+ case V110_TA_DBIT_M_ALL_ONE:
+ /* generate as many user bits as needed for the configured rate */
+ num_user_bits = osmo_v110_sync_ra1_get_user_data_chunk_bitlen(cfg->rate);
+ OSMO_ASSERT(num_user_bits > 0);
+ /* set them all to binary '0' or binary '1' */
+ memset(&user_bits[0], (int)ts->rx.d_bit_mode, num_user_bits);
+ cfg->rx_cb(cfg->priv, &user_bits[0], num_user_bits);
+ break;
+ case V110_TA_DBIT_M_FORWARD:
+ rc = osmo_v110_sync_ra1_ir_to_user(cfg->rate, &user_bits[0], sizeof(user_bits), df);
+ if (rc > 0)
+ cfg->rx_cb(cfg->priv, &user_bits[0], rc);
+ /* XXX else: indicate an error somehow? */
+ break;
+ }
+}
+
+/* build one V.110 frame to transmit */
+static void v110_ta_build_frame(const struct osmo_v110_ta *ta,
+ struct osmo_v110_decoded_frame *df)
+{
+ const struct osmo_v110_ta_cfg *cfg = ta->cfg;
+ const struct v110_ta_state *ts = &ta->state;
+ ubit_t user_bits[MAX_D_BITS];
+ int num_user_bits;
+ int rc;
+
+ /* E-bits (E1/E2/E3 may be overwritten below) */
+ memset(df->e_bits, 1, sizeof(df->e_bits));
+ /* S-bits */
+ memset(df->s_bits, ts->tx.s_bits, sizeof(df->s_bits));
+ /* X-bits */
+ memset(df->x_bits, ts->tx.x_bits, sizeof(df->x_bits));
+
+ /* D-bits */
+ switch (ts->tx.d_bit_mode) {
+ case V110_TA_DBIT_M_ALL_ZERO:
+ case V110_TA_DBIT_M_ALL_ONE:
+ /* set them all to binary '0' or binary '1' */
+ memset(df->d_bits, (int)ts->tx.d_bit_mode, sizeof(df->d_bits));
+ break;
+ case V110_TA_DBIT_M_FORWARD:
+ /* how many user bits to retrieve */
+ num_user_bits = osmo_v110_sync_ra1_get_user_data_chunk_bitlen(cfg->rate);
+ OSMO_ASSERT(num_user_bits > 0);
+ /* retrieve user bits from the application */
+ cfg->tx_cb(cfg->priv, &user_bits[0], num_user_bits);
+ /* convert user bits to intermediate rate (store to df) */
+ rc = osmo_v110_sync_ra1_user_to_ir(cfg->rate, df, &user_bits[0], num_user_bits);
+ OSMO_ASSERT(rc == 0);
+ break;
+ }
+}
+
+static void v110_ta_flags_update(struct osmo_v110_ta *ta, unsigned int v24_flags)
+{
+ struct osmo_v110_ta_cfg *cfg = ta->cfg;
+
+ if (ta->state.v24_flags == v24_flags)
+ return;
+ if (cfg->status_update_cb != NULL)
+ cfg->status_update_cb(cfg->priv, v24_flags);
+ ta->state.v24_flags = v24_flags;
+}
+
+static const struct osmo_tdef_state_timeout v110_ta_fsm_timeouts[32] = {
+ [V110_TA_ST_RESYNCING] = { .T = OSMO_V110_TA_TIMER_X1 },
+ [V110_TA_ST_CON_TA_TO_LINE] = { .T = OSMO_V110_TA_TIMER_T1 },
+ [V110_TA_ST_DISCONNECTING] = { .T = OSMO_V110_TA_TIMER_T2 },
+};
+
+#define v110_ta_fsm_state_chg(state) \
+ osmo_tdef_fsm_inst_state_chg(fi, state, \
+ v110_ta_fsm_timeouts, \
+ ((struct osmo_v110_ta *)(fi->priv))->Tdefs, \
+ 0)
+
+/* ITU-T V.110 Section 7.1.1 */
+static void v110_ta_fsm_idle_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+ unsigned int v24_flags = ta->state.v24_flags;
+
+ /* 7.1.1.2 During the idle (or ready) state the TA will transmit continuous binary 1s into the B-channel */
+ ts->tx.d_bit_mode = V110_TA_DBIT_M_ALL_ONE; /* circuit 103: continuous binary '1' */
+ ts->tx.s_bits = V110_SX_BIT_OFF; /* OFF is binary '1' */
+ ts->tx.x_bits = V110_SX_BIT_OFF; /* OFF is binary '1' */
+
+ /* 7.1.1.3 During the idle (or ready) state the TA (DCE) will transmit the following toward the DTE: */
+ /* - circuit 104: continuous binary '1' */
+ ts->rx.d_bit_mode = V110_TA_DBIT_M_ALL_ONE;
+ /* - circuits 107, 106, 109 = OFF */
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_106);
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_107);
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_109);
+ v110_ta_flags_update(ta, v24_flags);
+}
+
+/* ITU-T V.110 Section 7.1.1 */
+static void v110_ta_fsm_idle_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+
+ switch (event) {
+ case V110_TA_EV_V24_STATUS_CHG:
+ /* When the TA is to be switched to the data mode, circuit 108 must be ON */
+ if (V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_108)) {
+ /* 7.12.2: Start timer T1 when switching to CON_TA_LINE */
+ v110_ta_fsm_state_chg(V110_TA_ST_CON_TA_TO_LINE);
+ }
+ break;
+ case V110_TA_EV_RX_FRAME_IND:
+ v110_ta_handle_frame(ta, (const struct osmo_v110_decoded_frame *)data);
+ break;
+ case V110_TA_EV_TX_FRAME_RTS:
+ v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* ITU-T V.110 Section 7.1.2 */
+static void v110_ta_fsm_connect_ta_to_line_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+
+ /* 7.1.2.1 Switching to the data mode causes the TA to transmit the following towards the ISDN: */
+ /* a) frame synchronization pattern as described in 5.1.3.1 and 5.2.1 (done by the API user) */
+ /* b) circuit 103: continuous binary '1' */
+ ts->tx.d_bit_mode = V110_TA_DBIT_M_ALL_ONE;
+ /* c) status bits S = OFF and X = OFF */
+ ts->tx.s_bits = V110_SX_BIT_OFF; /* OFF is binary '1' */
+ ts->tx.x_bits = V110_SX_BIT_OFF; /* OFF is binary '1' */
+
+ /* 7.1.2.2 ... the receiver in the TA will begin to search for the frame synchronization
+ * pattern in the received bit stream (see 5.1.3.1 and 5.2.1) and start timer T1. */
+ OSMO_ASSERT(fi->T == OSMO_V110_TA_TIMER_T1);
+}
+
+/* ITU-T V.110 Section 7.1.2 */
+static void v110_ta_fsm_connect_ta_to_line(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+
+ switch (event) {
+ case V110_TA_EV_V24_STATUS_CHG:
+ /* If circuit 108 is OFF, we go back to IDLE/READY */
+ if (V24_FLAGMASK_IS_OFF(ts->v24_flags, OSMO_V110_TA_C_108))
+ v110_ta_fsm_state_chg(V110_TA_ST_IDLE_READY);
+ break;
+ case V110_TA_EV_SYNC_IND:
+ /* 7.1.2.3 When the receiver recognizes the frame synchronization pattern, it causes the S-
+ * and X-bits in the transmitted frames to be turned ON (provided that circuit 108 is ON). */
+ OSMO_ASSERT(V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_108));
+ ts->tx.s_bits = V110_SX_BIT_ON;
+ ts->tx.x_bits = V110_SX_BIT_ON;
+ break;
+ case V110_TA_EV_RX_FRAME_IND:
+ {
+ const struct osmo_v110_decoded_frame *df = data;
+ unsigned int v24_flags = ta->state.v24_flags;
+
+ /* 7.1.2.4 When the receiver recognizes that the status of bits S and X are ON */
+ if (v110_df_s_bits_are(df, V110_SX_BIT_ON) &&
+ v110_df_x_bits_are(df, V110_SX_BIT_ON)) {
+ /* ... it will perform the following functions: */
+ /* a) Turn ON circuit 107 toward the DTE and stop timer T1 */
+ V24_FLAGMASK_SET_ON(v24_flags, OSMO_V110_TA_C_107);
+ osmo_timer_del(&fi->timer);
+ /* b) Then, circuit 103 may be connected to the data bits in the frame; however, the
+ * DTE must maintain a binary 1 condition on circuit 103 until circuit 106 is turned
+ * ON in the next portion of the sequence. */
+ /* c) Turn ON circuit 109 and connect the data bits to circuit 104. */
+ V24_FLAGMASK_SET_ON(v24_flags, OSMO_V110_TA_C_109);
+ ts->rx.d_bit_mode = V110_TA_DBIT_M_FORWARD;
+ /* d) After an interval of N bits (see 6.3), it will turn ON circuit 106. */
+ V24_FLAGMASK_SET_ON(v24_flags, OSMO_V110_TA_C_106);
+ ts->tx.d_bit_mode = V110_TA_DBIT_M_FORWARD;
+ v110_ta_flags_update(ta, v24_flags);
+ /* Circuit 106 transitioning from OFF to ON will cause the transmitted data to
+ * transition from binary 1 to the data mode. */
+ v110_ta_fsm_state_chg(V110_TA_ST_DATA_TRANSFER);
+ }
+
+ v110_ta_handle_frame(ta, df);
+ break;
+ }
+ case V110_TA_EV_TX_FRAME_RTS:
+ v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
+ break;
+ case V110_TA_EV_TIMEOUT:
+ v110_ta_fsm_state_chg(V110_TA_ST_IDLE_READY);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* ITU-T V.110 Section 7.1.3 */
+static void v110_ta_fsm_data_transfer_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+ unsigned int v24_flags = ta->state.v24_flags;
+
+ /* 7.1.3.1 While in the data transfer state, the following circuit conditions exist:
+ * a): 105, 107, 108, and 109 are in the ON condition */
+ /* XXX: OSMO_ASSERT(V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_105)); */
+ V24_FLAGMASK_SET_ON(v24_flags, OSMO_V110_TA_C_107);
+ /* XXX: OSMO_ASSERT(V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_108)); */
+ V24_FLAGMASK_SET_ON(v24_flags, OSMO_V110_TA_C_109);
+ /* b) data is being transmitted on circuit 103 and received on circuit 104 */
+ ts->rx.d_bit_mode = V110_TA_DBIT_M_FORWARD;
+ ts->tx.d_bit_mode = V110_TA_DBIT_M_FORWARD;
+ /* c) circuits 133 (when implemented) and 106 are in the ON condition unless local out-of-band
+ * flow control is being used, either or both circuits may be in the ON or the OFF condition. */
+ if (!ta->cfg->flow_ctrl.end_to_end) {
+ /* XXX: OSMO_ASSERT(V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_133)); */
+ V24_FLAGMASK_SET_ON(v24_flags, OSMO_V110_TA_C_106);
+ }
+ v110_ta_flags_update(ta, v24_flags);
+
+ /* 7.1.3.2 While in the data transfer state, the following status bit conditions exist: */
+ /* a) status bits S in both directions are in the ON condition; */
+ ts->tx.s_bits = V110_SX_BIT_ON;
+ /* b) status bits X in both directions are in the ON condition unless end-to-end flow control
+ * is being used, in which case status bit X in either or both directions may be in the
+ * ON or the OFF condition. */
+ if (!ta->cfg->flow_ctrl.end_to_end)
+ ts->tx.x_bits = V110_SX_BIT_ON;
+}
+
+/* ITU-T V.110 Section 7.1.3 */
+static void v110_ta_fsm_data_transfer(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+
+ /* 7.1.3.3 While in the data transfer state: */
+ /* a) the S status bits shall *not* be mapped to/from the interchange circuits */
+ /* b) the X status bits shall *not* be mapped according to Table 3,
+ * unless end-to-end flow control is implemented */
+ /* TODO: if (ta->cfg->flow_ctrl.end_to_end) { ... } */
+
+ switch (event) {
+ case V110_TA_EV_V24_STATUS_CHG:
+ /* 7.1.4.1 At the completion of the data transfer phase, the local DTE will indicate a
+ * disconnect request by turning OFF circuit 108 */
+ if (V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_108))
+ break;
+ v110_ta_fsm_state_chg(V110_TA_ST_DISCONNECTING);
+ break;
+ case V110_TA_EV_DESYNC_IND:
+ v110_ta_fsm_state_chg(V110_TA_ST_RESYNCING);
+ break;
+ case V110_TA_EV_TX_FRAME_RTS:
+ v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
+ break;
+ case V110_TA_EV_RX_FRAME_IND:
+ {
+ const struct osmo_v110_decoded_frame *df = data;
+ unsigned int v24_flags = ta->state.v24_flags;
+
+ /* 7.1.4.2 ... this TA will recognize the transition of the status bits S from
+ * ON to OFF and the data bits from data to binary 0 as a disconnect request */
+ if (v110_df_s_bits_are(df, V110_SX_BIT_OFF) && v110_df_d_bits_are(df, 0)) {
+ /* ... and it will turn OFF circuits 107 and 109. */
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_107);
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_109);
+ v110_ta_flags_update(ta, v24_flags);
+ /* DTE should respond by turning OFF circuit 108 */
+ break; /* XXX: shall we forward D-bits to DTE anyway? */
+ }
+
+ v110_ta_handle_frame(ta, df);
+ break;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* ITU-T V.110 Section 7.1.4 */
+static void v110_ta_fsm_disconnect_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+ unsigned int v24_flags = ta->state.v24_flags;
+
+ /* 7.1.4.1 At the completion of the data transfer phase, the local DTE will indicate a
+ * disconnect request by turning OFF circuit 108. This will cause the following to occur: */
+ /* a) the status bits S in the frame toward ISDN will turn OFF, status bits X are kept ON */
+ ts->tx.s_bits = V110_SX_BIT_OFF;
+ /* b) circuit 106 will be turned OFF */
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_106);
+ v110_ta_flags_update(ta, v24_flags);
+ /* c) the data bits in the frame will be set to binary 0. */
+ ts->tx.d_bit_mode = V110_TA_DBIT_M_ALL_ZERO;
+
+ /* To guard against the failure of the remote TA to respond to the disconnect request,
+ * the local TA may start a timer T2 (suggested value 5 s) which is stopped by the
+ * reception or transmission of any D-channel clearing message (DISCONNECT, RELEASE,
+ * RELEASE COMPLETE). */
+ OSMO_ASSERT(fi->T == OSMO_V110_TA_TIMER_T2);
+}
+
+/* ITU-T V.110 Section 7.1.4 */
+static void v110_ta_fsm_disconnect(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+
+ switch (event) {
+ case V110_TA_EV_V24_STATUS_CHG:
+ break; /* nothing to do */
+ case V110_TA_EV_TX_FRAME_RTS:
+ v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
+ break;
+ case V110_TA_EV_RX_FRAME_IND:
+ {
+ const struct osmo_v110_decoded_frame *df = data;
+
+ /* 7.1.4.3 The TA at the station that originated the disconnect request will
+ * recognize reception of S = OFF or the loss of framing signals as a disconnect
+ * acknowledgement and turn OFF circuits 107 and 109. */
+ if (v110_df_s_bits_are(df, V110_SX_BIT_OFF)) {
+ /* circuits 107 and 109 set to off in .onenter() */
+ v110_ta_fsm_state_chg(V110_TA_ST_IDLE_READY);
+ }
+
+ v110_ta_handle_frame(ta, df);
+ break;
+ }
+ case V110_TA_EV_DESYNC_IND:
+ case V110_TA_EV_TIMEOUT:
+ /* circuits 107 and 109 set to off in .onenter() */
+ v110_ta_fsm_state_chg(V110_TA_ST_IDLE_READY);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* ITU-T V.110 Section 7.1.5 */
+static void v110_ta_fsm_resyncing_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+
+ /* 7.1.5 In the event of loss of frame synchronization, the (local) TA should
+ * attempt to resynchronize as follows: */
+ /* a) Place circuit 104 in binary 1 condition (passes from the data mode) */
+ ts->rx.d_bit_mode = V110_TA_DBIT_M_ALL_ONE;
+ /* b) Turn OFF status bit X in the transmitted frame */
+ ts->tx.x_bits = V110_SX_BIT_OFF;
+
+ /* guard timeout, see 7.1.5 e) */
+ OSMO_ASSERT(fi->T == OSMO_V110_TA_TIMER_X1);
+}
+
+/* ITU-T V.110 Section 7.1.5 */
+static void v110_ta_fsm_resyncing(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+ unsigned int v24_flags = ta->state.v24_flags;
+
+ switch (event) {
+ case V110_TA_EV_V24_STATUS_CHG:
+ break; /* TODO: handle circuit 108 being set to OFF? */
+ case V110_TA_EV_TX_FRAME_RTS:
+ v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
+ break;
+ case V110_TA_EV_SYNC_IND:
+ /* f) If resynchronization is achieved, the local TA should turn ON status bit X */
+ ts->tx.x_bits = V110_SX_BIT_ON;
+ v110_ta_fsm_state_chg(V110_TA_ST_DATA_TRANSFER);
+ break;
+ case V110_TA_EV_TIMEOUT:
+ /* e) If after an interval of X1 seconds the local TA cannot attain synchronization,
+ * it should send a disconnect request by turning OFF all of the status bits for several
+ * (at least three) frames with data bits set to binary 0 and then disconnect by turning
+ * OFF circuit 107 and transferring to the disconnected mode as discussed in 7.1.4.2. */
+ ts->tx.s_bits = V110_SX_BIT_OFF;
+ ts->tx.x_bits = V110_SX_BIT_OFF;
+ ts->tx.d_bit_mode = V110_TA_DBIT_M_ALL_ZERO;
+ /* TODO: actually Tx those frames (delay state transition) */
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_107);
+ v110_ta_flags_update(ta, v24_flags);
+ v110_ta_fsm_state_chg(V110_TA_ST_DISCONNECTING);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static int v110_ta_timer_cb(struct osmo_fsm_inst *fi)
+{
+ osmo_fsm_inst_dispatch(fi, V110_TA_EV_TIMEOUT, NULL);
+ return 0;
+}
+
+static const struct osmo_fsm_state v110_ta_states[] = {
+ [V110_TA_ST_IDLE_READY] = {
+ .name = "IDLE_READY",
+ .in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
+ | S(V110_TA_EV_TX_FRAME_RTS)
+ | S(V110_TA_EV_RX_FRAME_IND),
+ .out_state_mask = S(V110_TA_ST_IDLE_READY)
+ | S(V110_TA_ST_CON_TA_TO_LINE),
+ .action = &v110_ta_fsm_idle_ready,
+ .onenter = &v110_ta_fsm_idle_ready_onenter,
+ },
+ [V110_TA_ST_CON_TA_TO_LINE] = {
+ .name = "CONNECT_TA_TO_LINE",
+ .in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
+ | S(V110_TA_EV_TIMEOUT)
+ | S(V110_TA_EV_SYNC_IND)
+ | S(V110_TA_EV_TX_FRAME_RTS)
+ | S(V110_TA_EV_RX_FRAME_IND),
+ .out_state_mask = S(V110_TA_ST_DATA_TRANSFER)
+ | S(V110_TA_ST_IDLE_READY),
+ .action = &v110_ta_fsm_connect_ta_to_line,
+ .onenter = &v110_ta_fsm_connect_ta_to_line_onenter,
+ },
+ [V110_TA_ST_DATA_TRANSFER] = {
+ .name = "DATA_TRANSFER",
+ .in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
+ | S(V110_TA_EV_DESYNC_IND)
+ | S(V110_TA_EV_TX_FRAME_RTS)
+ | S(V110_TA_EV_RX_FRAME_IND),
+ .out_state_mask = S(V110_TA_ST_RESYNCING)
+ | S(V110_TA_ST_DISCONNECTING),
+ .action = &v110_ta_fsm_data_transfer,
+ .onenter = &v110_ta_fsm_data_transfer_onenter,
+ },
+ [V110_TA_ST_DISCONNECTING] = {
+ .name = "DISCONNECTING",
+ .in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
+ | S(V110_TA_EV_TIMEOUT)
+ | S(V110_TA_EV_TX_FRAME_RTS)
+ | S(V110_TA_EV_RX_FRAME_IND)
+ | S(V110_TA_EV_DESYNC_IND),
+ .out_state_mask = S(V110_TA_ST_IDLE_READY),
+ .action = &v110_ta_fsm_disconnect,
+ .onenter = &v110_ta_fsm_disconnect_onenter,
+ },
+ [V110_TA_ST_RESYNCING] = {
+ .name = "RESYNCING",
+ .in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
+ | S(V110_TA_EV_TIMEOUT)
+ | S(V110_TA_EV_TX_FRAME_RTS)
+ | S(V110_TA_EV_SYNC_IND),
+ .out_state_mask = S(V110_TA_ST_IDLE_READY)
+ | S(V110_TA_ST_DATA_TRANSFER),
+ .action = &v110_ta_fsm_resyncing,
+ .onenter = &v110_ta_fsm_resyncing_onenter,
+ },
+};
+
+static struct osmo_fsm osmo_v110_ta_fsm = {
+ .name = "V110-TA",
+ .states = v110_ta_states,
+ .num_states = ARRAY_SIZE(v110_ta_states),
+ .timer_cb = v110_ta_timer_cb,
+ .log_subsys = DLGLOBAL,
+ .event_names = v110_ta_fsm_event_names,
+};
+
+static __attribute__((constructor)) void on_dso_load(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&osmo_v110_ta_fsm) == 0);
+}
+
+/*! Allocate a V.110 TA (Terminal Adapter) instance.
+ * \param[in] ctx parent talloc context.
+ * \param[in] name name of the TA instance.
+ * \param[in] cfg initial configuration of the TA instance.
+ * \returns pointer to allocated TA instance; NULL on error. */
+struct osmo_v110_ta *osmo_v110_ta_alloc(void *ctx, const char *name,
+ const struct osmo_v110_ta_cfg *cfg)
+{
+ struct osmo_v110_ta *ta;
+
+ OSMO_ASSERT(cfg != NULL);
+ OSMO_ASSERT(cfg->rx_cb != NULL);
+ OSMO_ASSERT(cfg->tx_cb != NULL);
+
+ /* local (TE-TA) flow control is not implemented */
+ if (cfg->flow_ctrl.local != OSMO_V110_LOCAL_FLOW_CTRL_NONE) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Local (TE-TA) flow control is not implemented\n");
+ return NULL;
+ }
+
+ ta = talloc_zero(ctx, struct osmo_v110_ta);
+ if (ta == NULL)
+ return NULL;
+
+ ta->name = talloc_strdup(ta, name);
+ ta->cfg = talloc_memdup(ta, cfg, sizeof(*cfg));
+ if (ta->name == NULL || ta->cfg == NULL)
+ goto exit_free;
+
+ ta->Tdefs = talloc_memdup(ta, v110_ta_tdef, sizeof(v110_ta_tdef));
+ if (ta->Tdefs == NULL)
+ goto exit_free;
+ osmo_tdefs_reset(ta->Tdefs); /* apply default values */
+
+ ta->fi = osmo_fsm_inst_alloc(&osmo_v110_ta_fsm, ta, ta, LOGL_DEBUG, name);
+ if (ta->fi == NULL)
+ goto exit_free;
+
+ /* perform a loop transition to init the internal state */
+ osmo_fsm_inst_state_chg(ta->fi, V110_TA_ST_IDLE_READY, 0, 0);
+
+ return ta;
+
+exit_free:
+ if (ta->fi != NULL)
+ osmo_fsm_inst_free(ta->fi);
+ talloc_free(ta);
+ return NULL;
+}
+
+/*! Release memory taken by the given V.110 TA instance.
+ * \param[in] ta TA instance to be free()d. */
+void osmo_v110_ta_free(struct osmo_v110_ta *ta)
+{
+ if (ta == NULL)
+ return;
+ if (ta->fi != NULL)
+ osmo_fsm_inst_free(ta->fi);
+ talloc_free(ta); /* also free()s name and cfg */
+}
+
+/*! Configure a timer of the given V.110 TA instance.
+ * \param[in] ta TA instance to be configured.
+ * \param[in] timer a timer to be configured.
+ * \param[in] val_ms the new timeout value to set (in milliseconds).
+ * \returns 0 in case of success; negative on error. */
+int osmo_v110_ta_set_timer_val_ms(struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_timer timer,
+ unsigned long val_ms)
+{
+ return osmo_tdef_set(ta->Tdefs, (int)timer, val_ms, OSMO_TDEF_MS);
+}
+
+/*! Feed a [decoded] V.110 frame into the given TA instance.
+ *
+ * This function, like its _out counterpart, is intended to be used by the lower layers
+ * receiving V.110 frames over some medium. The caller of this function is responsible
+ * for finding the synchronization pattern (if needed), aligning to the frame boundaries,
+ * and decoding frames using osmo_v110_decode_frame() or osmo_csd_*_decode_frame().
+ *
+ * Bits E1/E2/E3 are expected to be set by the caller (if not being transmitted
+ * over the medium) in accordance with the configured synchronous user rate.
+ *
+ * Bits D1..D48 are passed to the bit rate adaption function RA1. The resulting output
+ * is then passed to the upper layer (application) via the configured .rx_cb(). Though,
+ * in certain states of the TA's FSM, bits D1..D48 are ignored and the upper layer
+ * gets a sequence of binary '0' or '1'.
+ *
+ * \param[in] ta TA instance to feed the given frame into.
+ * \param[in] in pointer to a [decoded] V.110 frame.
+ * \returns 0 in case of success; negative on error. */
+int osmo_v110_ta_frame_in(struct osmo_v110_ta *ta, const struct osmo_v110_decoded_frame *in)
+{
+ return osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_RX_FRAME_IND, (void *)in);
+}
+
+/*! Pull a [decoded] V.110 frame out of the given TA instance.
+ *
+ * This function, like its _in counterpart, is intended to be used by the lower layers
+ * transmitting V.110 frames over some medium. The caller of this function is responsible
+ * for encoding the output frame using osmo_v110_encode_frame() or osmo_csd_*_encode_frame().
+ *
+ * Bits E1/E2/E3 are set in accordance with the configured synchronous user rate.
+ * Bits E4/E5/E6/E7 are unconditionally set to binary '1'.
+ *
+ * Bits D1..D48 are set depending on the state of TA's FSM:
+ *
+ * - In data transfer mode, the user bits are obtained from the upper layer (application)
+ * via the configured .tx_cb(), and then passed to the bit rate adaption function RA1,
+ * which generates bits D1..D48.
+ * - In other modes, bits D1..D48 are all set to binary '0' or '1'.
+ *
+ * \param[in] ta TA instance to pull a frame from.
+ * \param[out] out where to store a [decoded] V.110 frame.
+ * \returns 0 in case of success; negative on error. */
+int osmo_v110_ta_frame_out(struct osmo_v110_ta *ta, struct osmo_v110_decoded_frame *out)
+{
+ return osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_TX_FRAME_RTS, (void *)out);
+}
+
+/*! Indicate a synchronization establishment event.
+ *
+ * This function is intended to be called when the lower layer
+ * achieves synchronization to the frame clock.
+ *
+ * \param[in] ta TA instance to indicate the event to.
+ * \returns 0 in case of success; negative on error. */
+int osmo_v110_ta_sync_ind(struct osmo_v110_ta *ta)
+{
+ return osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_SYNC_IND, NULL);
+}
+
+/*! Indicate a synchronization loss event.
+ *
+ * This function is intended to be called when the lower layer
+ * experiences a loss of synchronization with the frame clock.
+ *
+ * \param[in] ta TA instance to indicate the event to.
+ * \returns 0 in case of success; negative on error. */
+int osmo_v110_ta_desync_ind(struct osmo_v110_ta *ta)
+{
+ return osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_DESYNC_IND, NULL);
+}
+
+/*! Get the V.24 status bit-mask of the given TA instance.
+ * \param[in] ta TA instance to get the circuit bit-mask.
+ * \returns bitmask of OSMO_V110_TA_C_*. */
+unsigned int osmo_v110_ta_get_status(const struct osmo_v110_ta *ta)
+{
+ return ta->state.v24_flags;
+}
+
+/*! Set the V.24 status bit-mask of the given TA instance.
+ * \param[in] ta TA instance to update the circuit state.
+ * \param[in] status bit-mask of OSMO_V110_TA_C_*.
+ * \returns 0 on success; negative on error. */
+static int v110_ta_set_status(struct osmo_v110_ta *ta, unsigned int status)
+{
+ const unsigned int old_status = ta->state.v24_flags;
+ int rc = 0;
+
+ ta->state.v24_flags = status;
+ if (status != old_status)
+ rc = osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_V24_STATUS_CHG, NULL);
+
+ return rc;
+}
+
+/*! Get state of a V.24 circuit of the given TA instance.
+ * \param[in] ta TA instance to get the circuit state.
+ * \param[in] circuit a V.24 circuit, one of OSMO_V110_TA_C_*.
+ * \returns circuit state: active (true) or inactive (false). */
+bool osmo_v110_ta_get_circuit(const struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_circuit circuit)
+{
+ return V24_FLAGMASK_IS_ON(ta->state.v24_flags, circuit);
+}
+
+/*! Activate/deactivate a V.24 circuit of the given TA instance.
+ * \param[in] ta TA instance to update the circuit state.
+ * \param[in] circuit a V.24 circuit, one of OSMO_V110_TA_C_* (DTE->DCE).
+ * \param[in] active activate (true) or deactivate (false) the circuit.
+ * \returns 0 on success; negative on error. */
+int osmo_v110_ta_set_circuit(struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_circuit circuit, bool active)
+{
+ unsigned int status = ta->state.v24_flags;
+
+ /* permit setting only DTE->DCE circuits */
+ switch (circuit) {
+ case OSMO_V110_TA_C_105:
+ case OSMO_V110_TA_C_108:
+ case OSMO_V110_TA_C_133:
+ break;
+ default:
+ LOGPFSML(ta->fi, LOGL_ERROR,
+ "Setting circuit %s is not permitted (wrong direction?)\n",
+ osmo_v110_ta_circuit_name(circuit));
+ return -EACCES;
+ }
+
+ if (active)
+ V24_FLAGMASK_SET_ON(status, circuit);
+ else
+ V24_FLAGMASK_SET_OFF(status, circuit);
+
+ return v110_ta_set_status(ta, status);
+}
diff --git a/src/pseudotalloc/Makefile.am b/src/pseudotalloc/Makefile.am
index 3c78bba8..030b281b 100644
--- a/src/pseudotalloc/Makefile.am
+++ b/src/pseudotalloc/Makefile.am
@@ -1,4 +1,5 @@
-AM_CFLAGS = -Wall -I. $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CPPFLAGS = -I. -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -Wall
if ENABLE_PSEUDOTALLOC
diff --git a/src/pseudotalloc/pseudotalloc.c b/src/pseudotalloc/pseudotalloc.c
index 25425e57..895c6dd0 100644
--- a/src/pseudotalloc/pseudotalloc.c
+++ b/src/pseudotalloc/pseudotalloc.c
@@ -55,21 +55,21 @@ void talloc_set_name_const(const void *ptr, const char *name)
{
}
-char *talloc_strdup(const void *context, const char *p)
+void *talloc_memdup(const void *ctx, const void *p, size_t size)
{
- char *ptr;
- size_t len;
+ void *ptr;
- if (!p)
- return NULL;
- len = strlen(p);
+ ptr = talloc_size(ctx, size);
+ if (ptr && p)
+ memcpy(ptr, p, size);
+ return ptr;
+}
- ptr = talloc_size(context, len+1);
- if (!ptr)
+char *talloc_strdup(const void *ctx, const char *p)
+{
+ if (!p)
return NULL;
- memcpy(ptr, p, len+1);
-
- return ptr;
+ return talloc_memdup(ctx, p, strlen(p) + 1);
}
void *talloc_pool(const void *context, size_t size)
diff --git a/src/pseudotalloc/talloc.h b/src/pseudotalloc/talloc.h
index d257a981..38935991 100644
--- a/src/pseudotalloc/talloc.h
+++ b/src/pseudotalloc/talloc.h
@@ -50,7 +50,8 @@ int _talloc_free(void *ptr, const char *location);
void *talloc_named_const(const void *context, size_t size, const char *name);
void *talloc_named(const void *context, size_t size, const char *fmt, ...);
void talloc_set_name_const(const void *ptr, const char *name);
-char *talloc_strdup(const void *t, const char *p);
+void *talloc_memdup(const void *ctx, const void *p, size_t size);
+char *talloc_strdup(const void *ctx, const char *p);
void *talloc_pool(const void *context, size_t size);
#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name);
diff --git a/src/sim/Makefile.am b/src/sim/Makefile.am
index 52f3c6ad..0f6be576 100644
--- a/src/sim/Makefile.am
+++ b/src/sim/Makefile.am
@@ -1,9 +1,9 @@
# 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:1:0
+LIBVERSION=3:2:1
-AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
AM_CFLAGS = -fPIC -Wall $(TALLOC_CFLAGS)
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
@@ -15,9 +15,12 @@ lib_LTLIBRARIES = libosmosim.la
libosmosim_la_SOURCES = core.c reader.c class_tables.c \
card_fs_sim.c card_fs_usim.c card_fs_uicc.c \
card_fs_isim.c card_fs_hpsim.c card_fs_tetra.c
-libosmosim_la_LDFLAGS = -version-info $(LIBVERSION)
+libosmosim_la_LDFLAGS = \
+ -version-info $(LIBVERSION) \
+ -no-undefined \
+ $(NULL)
libosmosim_la_LIBADD = \
- $(top_builddir)/src/libosmocore.la \
+ $(top_builddir)/src/core/libosmocore.la \
$(top_builddir)/src/gsm/libosmogsm.la \
$(TALLOC_LIBS)
if ENABLE_PCSC
diff --git a/src/sim/card_fs_hpsim.c b/src/sim/card_fs_hpsim.c
index 4a5f7d9a..2c115b75 100644
--- a/src/sim/card_fs_hpsim.c
+++ b/src/sim/card_fs_hpsim.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
diff --git a/src/sim/card_fs_isim.c b/src/sim/card_fs_isim.c
index f11c0294..1a38da2f 100644
--- a/src/sim/card_fs_isim.c
+++ b/src/sim/card_fs_isim.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
diff --git a/src/sim/card_fs_sim.c b/src/sim/card_fs_sim.c
index 55ce9af7..f07a2370 100644
--- a/src/sim/card_fs_sim.c
+++ b/src/sim/card_fs_sim.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
diff --git a/src/sim/card_fs_tetra.c b/src/sim/card_fs_tetra.c
index 12853a52..597e38b7 100644
--- a/src/sim/card_fs_tetra.c
+++ b/src/sim/card_fs_tetra.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
diff --git a/src/sim/card_fs_uicc.c b/src/sim/card_fs_uicc.c
index 5dcaaa12..87def0e4 100644
--- a/src/sim/card_fs_uicc.c
+++ b/src/sim/card_fs_uicc.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
diff --git a/src/sim/card_fs_usim.c b/src/sim/card_fs_usim.c
index 8f880e75..8cff3fc3 100644
--- a/src/sim/card_fs_usim.c
+++ b/src/sim/card_fs_usim.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
diff --git a/src/sim/class_tables.c b/src/sim/class_tables.c
index 6f541ee2..29c1e40e 100644
--- a/src/sim/class_tables.c
+++ b/src/sim/class_tables.c
@@ -13,17 +13,13 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdint.h>
#include <osmocom/core/utils.h>
#include <osmocom/sim/class_tables.h>
-static const uint8_t iso7816_ins_tbl[] = {
+static const uint8_t iso7816_ins_tbl[256] = {
[0xB0] = 2, /* READ BIN */
[0xD0] = 3, /* WRITE BIN */
[0xD6] = 3, /* UPDATE BIN */
@@ -117,7 +113,7 @@ static const uint8_t uicc_ins_tbl_046[256] = {
[0xD6] = 3, /* UPDATE BINARY */
[0xB2] = 2, /* READ RECORD */
[0xDC] = 3, /* UPDATE RECORD */
- [0xA2] = 4, /* SEEK */
+ [0xA2] = 4, /* SEARCH RECORD */
[0x20] = 3, /* VERIFY PIN */
[0x24] = 3, /* CHANGE PIN */
[0x26] = 3, /* DISABLE PIN */
@@ -177,6 +173,24 @@ static int uicc046_cla_ins_helper(const struct osim_cla_ins_case *cic,
return 0;
}
+static int gp_cla_ins_helper(const struct osim_cla_ins_case *cic,
+ const uint8_t *hdr)
+{
+ uint8_t ins = hdr[1];
+ uint8_t p1 = hdr[2];
+
+ switch (ins) {
+ case 0xE2: /* STORE DATA */
+ switch (p1 & 0x01) {
+ case 1:
+ return 4;
+ default:
+ return 3;
+ }
+ }
+ return 0;
+}
+
/* ETSI TS 102 221, Table 10.5, CLA = 0x8x, 0xCx or 0xEx */
static const uint8_t uicc_ins_tbl_8ce[256] = {
[0xF2] = 2, /* STATUS */
@@ -184,6 +198,7 @@ static const uint8_t uicc_ins_tbl_8ce[256] = {
[0xCB] = 4, /* RETRIEVE DATA */
[0xDB] = 3, /* SET DATA */
[0xAA] = 3, /* TERMINAL CAPABILITY */
+ [0x78] = 4, /* GET IDENTITY */
};
/* ETSI TS 102 221, Table 10.5, CLA = 0x80 */
@@ -192,6 +207,22 @@ static const uint8_t uicc_ins_tbl_80[256] = {
[0xC2] = 4, /* ENVELOPE */
[0x12] = 2, /* FETCH */
[0x14] = 3, /* TERMINAL RESPONSE */
+ [0x76] = 4, /* SUSPEND UICC */
+ [0x7A] = 4, /* EXCHANGE CAPABILITIES */
+};
+
+/* Card Specification v2.3.1*/
+static const uint8_t gp_ins_tbl_8ce[256] = {
+ [0xE4] = 4, /* DELETE */
+ [0xE2] = 0x80, /* STORE DATA */
+ [0xCA] = 4, /* GET DATA */
+ [0xCB] = 4, /* GET DATA */
+ [0xF2] = 4, /* GET STATUS */
+ [0xE6] = 4, /* INSTALL */
+ [0xE8] = 4, /* LOAD */
+ [0xD8] = 4, /* PUT KEY */
+ [0xF0] = 3, /* SET STATUS */
+ [0xC0] = 2, /* GET RESPONSE */
};
static const struct osim_cla_ins_case uicc_ins_case[] = {
@@ -226,6 +257,21 @@ static const struct osim_cla_ins_case uicc_ins_case[] = {
.cla = 0xE0,
.cla_mask = 0xF0,
.ins_tbl = uicc_ins_tbl_8ce,
+ }, {
+ .cla = 0x80,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
+ }, {
+ .cla = 0xC0,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
+ }, {
+ .cla = 0xE0,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
},
};
@@ -273,7 +319,23 @@ static const struct osim_cla_ins_case uicc_sim_ins_case[] = {
.cla = 0xE0,
.cla_mask = 0xF0,
.ins_tbl = uicc_ins_tbl_8ce,
+ }, {
+ .cla = 0x80,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
+ }, {
+ .cla = 0xC0,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
+ }, {
+ .cla = 0xE0,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
},
+
};
const struct osim_cla_ins_card_profile osim_uicc_sim_cic_profile = {
@@ -288,6 +350,13 @@ const uint8_t usim_ins_case[256] = {
[0x88] = 4, /* AUTHENTICATE */
};
+/* https://learn.microsoft.com/en-us/windows-hardware/drivers/smartcard/discovery-process */
+static const uint8_t microsoft_discovery_ins_tbl[256] = {
+ [0xA4] = 4, /* SELECT FILE */
+ [0xCA] = 2, /* GET DATA */
+ [0xC0] = 2, /* GET RESPONSE */
+};
+
int osim_determine_apdu_case(const struct osim_cla_ins_card_profile *prof,
const uint8_t *hdr)
{
@@ -305,12 +374,23 @@ int osim_determine_apdu_case(const struct osim_cla_ins_card_profile *prof,
case 0x80:
return cic->helper(cic, hdr);
case 0x00:
- /* continue with fruther cic, rather than abort
+ /* continue with further cic, rather than abort
* now */
continue;
default:
return rc;
}
}
+ /* special handling for Microsoft who insists to use INS=0xCA in CLA=0x00 which is not
+ * really part of GSM SIM, ETSI UICC or 3GPP USIM specifications, but only ISO7816. Rather than adding
+ * it to each and every card profile, let's add the instructions listed at
+ * https://learn.microsoft.com/en-us/windows-hardware/drivers/smartcard/discovery-process explicitly
+ * here. They will only be used in case no more specific match was found in the actual profile above. */
+ if (cla == 0x00) {
+ rc = microsoft_discovery_ins_tbl[ins];
+ if (rc)
+ return rc;
+ }
+
return 0;
}
diff --git a/src/sim/core.c b/src/sim/core.c
index d11c2d83..fa17e12d 100644
--- a/src/sim/core.c
+++ b/src/sim/core.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
diff --git a/src/sim/reader.c b/src/sim/reader.c
index ae0aba94..982b2eef 100644
--- a/src/sim/reader.c
+++ b/src/sim/reader.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -44,7 +40,7 @@ static int get_sw(struct msgb *resp)
{
int ret;
- if (!msgb_apdu_de(resp) || msgb_apdu_le(resp) < 2)
+ if (!resp->l2h || msgb_apdu_le(resp) < 2)
return -EIO;
ret = msgb_get_u16(resp);
@@ -277,3 +273,24 @@ struct osim_card_hdl *osim_card_open(struct osim_reader_hdl *rh, enum osim_proto
return ch;
}
+
+int osim_card_reset(struct osim_card_hdl *card, bool cold_reset)
+{
+ struct osim_reader_hdl *rh = card->reader;
+
+ return rh->ops->card_reset(card, cold_reset);
+}
+
+int osim_card_close(struct osim_card_hdl *card)
+{
+ struct osim_reader_hdl *rh = card->reader;
+ int rc;
+
+ rc = rh->ops->card_close(card);
+
+ card->reader = NULL;
+ talloc_free(card);
+ rh->card = NULL;
+
+ return rc;
+}
diff --git a/src/sim/reader_pcsc.c b/src/sim/reader_pcsc.c
index 234a9a77..c37072ee 100644
--- a/src/sim/reader_pcsc.c
+++ b/src/sim/reader_pcsc.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -156,6 +152,34 @@ end:
return NULL;
}
+static int pcsc_card_reset(struct osim_card_hdl *card, bool cold_reset)
+{
+ struct pcsc_reader_state *st = card->reader->priv;
+ LONG rc;
+
+ rc = SCardReconnect(st->hCard, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0,
+ cold_reset ? SCARD_UNPOWER_CARD : SCARD_RESET_CARD,
+ &st->dwActiveProtocol);
+ PCSC_ERROR(rc, "SCardReconnect");
+
+ return 0;
+end:
+ return -EIO;
+}
+
+static int pcsc_card_close(struct osim_card_hdl *card)
+{
+ struct pcsc_reader_state *st = card->reader->priv;
+ LONG rc;
+
+ rc = SCardDisconnect(st->hCard, SCARD_UNPOWER_CARD);
+ PCSC_ERROR(rc, "SCardDisconnect");
+
+ return 0;
+end:
+ return -EIO;
+}
+
static int pcsc_transceive(struct osim_reader_hdl *rh, struct msgb *msg)
{
@@ -179,6 +203,8 @@ const struct osim_reader_ops pcsc_reader_ops = {
.name = "PC/SC",
.reader_open = pcsc_reader_open,
.card_open = pcsc_card_open,
+ .card_reset = pcsc_card_reset,
+ .card_close = pcsc_card_close,
.transceive = pcsc_transceive,
};
diff --git a/src/stat_item.c b/src/stat_item.c
deleted file mode 100644
index 40374a7c..00000000
--- a/src/stat_item.c
+++ /dev/null
@@ -1,407 +0,0 @@
-/*! \file stat_item.c
- * utility routines for keeping statistical values */
-/*
- * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2015 by sysmocom - s.f.m.c. GmbH
- *
- * All Rights Reserved
- *
- * SPDX-License-Identifier: GPL-2.0+
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-/*! \addtogroup osmo_stat_item
- * @{
- *
- * This osmo_stat_item module adds instrumentation capabilities to
- * gather measurement and statistical values in a similar fashion to
- * what we have as \ref osmo_counter_group.
- *
- * As opposed to counters, osmo_stat_item do not increment but consist
- * of a configurable-sized FIFO, which can store not only the current
- * (most recent) value, but also historic values.
- *
- * The only supported value type is an int32_t.
- *
- * Getting values from osmo_stat_item is usually done at a high level
- * through the stats API (stats.c). It uses item->stats_next_id to
- * store what has been sent to all enabled reporters. It is also
- * possible to read from osmo_stat_item directly, without modifying
- * its state, by storing next_id outside of osmo_stat_item.
- *
- * Each value stored in the FIFO of an osmo_stat_item has an associated
- * value_id. The value_id is increased with each value, so (until the
- * counter wraps) more recent values will have higher values.
- *
- * When a new value is set, the oldest value in the FIFO gets silently
- * overwritten. Lost values are skipped when getting values from the
- * item.
- *
- */
-
-/* Struct overview:
- * Each osmo_stat_item is attached to an osmo_stat_item_group, which is
- * attached to the global osmo_stat_item_groups list.
- *
- * osmo_stat_item_groups
- * / \
- * group1 group2
- * / \
- * item1 item2
- * |
- * values
- * / \
- * 1 2
- * |-id |-id
- * '-value '-value
- */
-
-#include <stdint.h>
-#include <string.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/stat_item.h>
-
-/*! global list of stat_item groups */
-static LLIST_HEAD(osmo_stat_item_groups);
-
-/*! talloc context from which we allocate */
-static void *tall_stat_item_ctx;
-
-/*! Allocate a new group of counters according to description.
- * Allocate a group of stat items described in \a desc from talloc context \a ctx,
- * giving the new group the index \a idx.
- * \param[in] ctx \ref talloc context
- * \param[in] desc Statistics item group description
- * \param[in] idx Index of new stat item group
- */
-struct osmo_stat_item_group *osmo_stat_item_group_alloc(void *ctx,
- const struct osmo_stat_item_group_desc *desc,
- unsigned int idx)
-{
- unsigned int group_size;
- unsigned long items_size = 0;
- unsigned int item_idx;
- void *items;
-
- struct osmo_stat_item_group *group;
-
- group_size = sizeof(struct osmo_stat_item_group) +
- desc->num_items * sizeof(struct osmo_stat_item *);
-
- if (!ctx)
- ctx = tall_stat_item_ctx;
-
- group = talloc_zero_size(ctx, group_size);
- if (!group)
- return NULL;
-
- group->desc = desc;
- group->idx = idx;
-
- /* Get combined size of all items */
- for (item_idx = 0; item_idx < desc->num_items; item_idx++) {
- unsigned int size;
- size = sizeof(struct osmo_stat_item) +
- sizeof(struct osmo_stat_item_value) *
- desc->item_desc[item_idx].num_values;
- /* Align to pointer size */
- size = (size + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
-
- /* Store offsets into the item array */
- group->items[item_idx] = (void *)items_size;
-
- items_size += size;
- }
-
- items = talloc_zero_size(group, items_size);
- if (!items) {
- talloc_free(group);
- return NULL;
- }
-
- /* Update item pointers */
- for (item_idx = 0; item_idx < desc->num_items; item_idx++) {
- struct osmo_stat_item *item = (struct osmo_stat_item *)
- ((uint8_t *)items + (unsigned long)group->items[item_idx]);
- unsigned int i;
-
- group->items[item_idx] = item;
- item->last_offs = desc->item_desc[item_idx].num_values - 1;
- item->stats_next_id = 1;
- item->desc = &desc->item_desc[item_idx];
-
- for (i = 0; i <= item->last_offs; i++) {
- item->values[i].value = desc->item_desc[item_idx].default_value;
- item->values[i].id = OSMO_STAT_ITEM_NOVALUE_ID;
- }
- }
-
- llist_add(&group->list, &osmo_stat_item_groups);
-
- return group;
-}
-
-/*! Free the memory for the specified group of stat items */
-void osmo_stat_item_group_free(struct osmo_stat_item_group *grp)
-{
- llist_del(&grp->list);
- talloc_free(grp);
-}
-
-/*! Increase the stat_item to the given value.
- * This function adds a new value for the given stat_item at the end of
- * the FIFO.
- * \param[in] item The stat_item whose \a value we want to set
- * \param[in] value The numeric value we want to store at end of FIFO
- */
-void osmo_stat_item_inc(struct osmo_stat_item *item, int32_t value)
-{
- int32_t oldvalue = item->values[item->last_offs].value;
- osmo_stat_item_set(item, oldvalue + value);
-}
-
-/*! Descrease the stat_item to the given value.
- * This function adds a new value for the given stat_item at the end of
- * the FIFO.
- * \param[in] item The stat_item whose \a value we want to set
- * \param[in] value The numeric value we want to store at end of FIFO
- */
-void osmo_stat_item_dec(struct osmo_stat_item *item, int32_t value)
-{
- int32_t oldvalue = item->values[item->last_offs].value;
- osmo_stat_item_set(item, oldvalue - value);
-}
-
-/*! Set the a given stat_item to the given value.
- * This function adds a new value for the given stat_item at the end of
- * the FIFO.
- * \param[in] item The stat_item whose \a value we want to set
- * \param[in] value The numeric value we want to store at end of FIFO
- */
-void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value)
-{
- int32_t id = item->values[item->last_offs].id + 1;
- if (id == OSMO_STAT_ITEM_NOVALUE_ID)
- id++;
-
- item->last_offs += 1;
- if (item->last_offs >= item->desc->num_values)
- item->last_offs = 0;
-
- item->values[item->last_offs].value = value;
- item->values[item->last_offs].id = id;
-}
-
-/*! Retrieve the next value from the osmo_stat_item object.
- * If a new value has been set, it is returned. The next_id is used to decide
- * which value to return.
- * On success, *next_id is updated to refer to the next unread value. If
- * values have been missed due to FIFO overflow, *next_id is incremented by
- * (1 + num_lost).
- * This way, the osmo_stat_item object can be kept stateless from the reader's
- * perspective and therefore be used by several backends simultaneously.
- *
- * \param val the osmo_stat_item object
- * \param next_id identifies the next value to be read
- * \param value a pointer to store the value
- * \returns the increment of the index (0: no value has been read,
- * 1: one value has been taken,
- * (1+n): n values have been skipped, one has been taken)
- */
-int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *next_id,
- int32_t *value)
-{
- const struct osmo_stat_item_value *next_value;
- const struct osmo_stat_item_value *item_value = NULL;
- int id_delta;
- int next_offs;
-
- next_offs = item->last_offs;
- next_value = &item->values[next_offs];
-
- while (next_value->id - *next_id >= 0 &&
- next_value->id != OSMO_STAT_ITEM_NOVALUE_ID)
- {
- item_value = next_value;
-
- next_offs -= 1;
- if (next_offs < 0)
- next_offs = item->desc->num_values - 1;
- if (next_offs == item->last_offs)
- break;
- next_value = &item->values[next_offs];
- }
-
- if (!item_value)
- /* All items have been read */
- return 0;
-
- *value = item_value->value;
-
- id_delta = item_value->id + 1 - *next_id;
-
- *next_id = item_value->id + 1;
-
- if (id_delta > 1) {
- LOGP(DLSTATS, LOGL_ERROR, "%s: %d stats values skipped\n", item->desc->name, id_delta - 1);
- }
-
- return id_delta;
-}
-
-/*! Skip/discard all values of this item and update \a next_id accordingly */
-int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *next_id)
-{
- int discarded = item->values[item->last_offs].id + 1 - *next_id;
- *next_id = item->values[item->last_offs].id + 1;
-
- return discarded;
-}
-
-/*! Skip all values of all items and update \a next_id accordingly */
-int osmo_stat_item_discard_all(int32_t *next_id)
-{
- LOGP(DLSTATS, LOGL_ERROR, "osmo_stat_item_discard_all is deprecated (no-op)\n");
- return 0;
-}
-
-/*! Initialize the stat item module. Call this once from your program.
- * \param[in] tall_ctx Talloc context from which this module allocates */
-int osmo_stat_item_init(void *tall_ctx)
-{
- tall_stat_item_ctx = tall_ctx;
-
- return 0;
-}
-
-/*! Search for item group based on group name and index
- * \param[in] name Name of stats_item_group we want to find
- * \param[in] idx Index of the group we want to find
- * \returns pointer to group, if found; NULL otherwise */
-struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx(
- const char *name, const unsigned int idx)
-{
- struct osmo_stat_item_group *statg;
-
- llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
- if (!statg->desc)
- continue;
-
- if (!strcmp(statg->desc->group_name_prefix, name) &&
- statg->idx == idx)
- return statg;
- }
- return NULL;
-}
-
-/*! Search for item based on group + item name
- * \param[in] statg group in which to search for the item
- * \param[in] name name of item to search within \a statg
- * \returns pointer to item, if found; NULL otherwise */
-const struct osmo_stat_item *osmo_stat_item_get_by_name(
- const struct osmo_stat_item_group *statg, const char *name)
-{
- int i;
- const struct osmo_stat_item_desc *item_desc;
-
- if (!statg->desc)
- return NULL;
-
- for (i = 0; i < statg->desc->num_items; i++) {
- item_desc = &statg->desc->item_desc[i];
-
- if (!strcmp(item_desc->name, name)) {
- return statg->items[i];
- }
- }
- return NULL;
-}
-
-/*! Iterate over all items in group, call user-supplied function on each
- * \param[in] statg stat_item group over whose items to iterate
- * \param[in] handle_item Call-back function, aborts if rc < 0
- * \param[in] data Private data handed through to \a handle_item
- */
-int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg,
- osmo_stat_item_handler_t handle_item, void *data)
-{
- int rc = 0;
- int i;
-
- for (i = 0; i < statg->desc->num_items; i++) {
- struct osmo_stat_item *item = statg->items[i];
- rc = handle_item(statg, item, data);
- if (rc < 0)
- return rc;
- }
-
- return rc;
-}
-
-/*! Iterate over all stat_item groups in system, call user-supplied function on each
- * \param[in] handle_group Call-back function, aborts if rc < 0
- * \param[in] data Private data handed through to \a handle_group
- */
-int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data)
-{
- struct osmo_stat_item_group *statg;
- int rc = 0;
-
- llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
- rc = handle_group(statg, data);
- if (rc < 0)
- return rc;
- }
-
- return rc;
-}
-
-
-/*! Remove all values of a stat item
- * \param[in] item stat item to reset
- */
-void osmo_stat_item_reset(struct osmo_stat_item *item)
-{
- unsigned int i;
-
- item->last_offs = item->desc->num_values - 1;
- item->stats_next_id = 1;
-
- for (i = 0; i <= item->last_offs; i++) {
- item->values[i].value = item->desc->default_value;
- item->values[i].id = OSMO_STAT_ITEM_NOVALUE_ID;
- }
-}
-
-/*! Reset all osmo stat items in a group
- * \param[in] statg stat item group to reset
- */
-void osmo_stat_item_group_reset(struct osmo_stat_item_group *statg)
-{
- int i;
-
- for (i = 0; i < statg->desc->num_items; i++) {
- struct osmo_stat_item *item = statg->items[i];
- osmo_stat_item_reset(item);
- }
-}
-/*! @} */
diff --git a/src/usb/Makefile.am b/src/usb/Makefile.am
index 2dee434b..c7d7a2a2 100644
--- a/src/usb/Makefile.am
+++ b/src/usb/Makefile.am
@@ -1,9 +1,9 @@
# 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=0:0:0
+LIBVERSION=0:1:0
-AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
AM_CFLAGS = -fPIC -Wall $(LIBUSB_CFLAGS) $(TALLOC_CFLAGS)
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
@@ -12,9 +12,12 @@ if ENABLE_LIBUSB
lib_LTLIBRARIES = libosmousb.la
libosmousb_la_SOURCES = osmo_libusb.c
-libosmousb_la_LDFLAGS = -version-info $(LIBVERSION)
+libosmousb_la_LDFLAGS = \
+ -version-info $(LIBVERSION) \
+ -no-undefined \
+ $(NULL)
libosmousb_la_LIBADD = \
- $(top_builddir)/src/libosmocore.la \
+ $(top_builddir)/src/core/libosmocore.la \
$(TALLOC_LIBS) \
$(LIBUSB_LIBS)
diff --git a/src/usb/osmo_libusb.c b/src/usb/osmo_libusb.c
index bb862067..a249d100 100644
--- a/src/usb/osmo_libusb.c
+++ b/src/usb/osmo_libusb.c
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <unistd.h>
@@ -103,7 +99,7 @@ static void osmo_usb_added_cb(int fd, short events, void *user_data)
static void osmo_usb_removed_cb(int fd, void *user_data)
{
struct osmo_fd *ofd = osmo_fd_get_by_fd(fd);
- if (!fd)
+ if (!ofd)
return;
osmo_fd_unregister(ofd);
talloc_free(ofd);
@@ -541,7 +537,8 @@ libusb_device_handle *osmo_libusb_open_claim_interface(void *ctx, libusb_context
addr = libusb_get_device_address(*dev);
path = osmo_libusb_dev_get_path_buf(pathbuf, sizeof(pathbuf), *dev);
if ((ifm->addr && addr == ifm->addr) ||
- (strlen(ifm->path) && !strcmp(path, ifm->path))) {
+ (strlen(ifm->path) && !strcmp(path, ifm->path)) ||
+ (!ifm->addr && !strlen(ifm->path) && !list[1] /* only one device */)) {
rc = libusb_open(*dev, &usb_devh);
if (rc < 0) {
fprintf(stderr, "Cannot open device: %s\n", libusb_error_name(rc));
@@ -739,11 +736,15 @@ int osmo_libusb_get_ep_addrs(libusb_device_handle *devh, unsigned int if_num,
int osmo_libusb_init(libusb_context **pluctx)
{
libusb_context *luctx = NULL;
+ const struct libusb_pollfd **pfds;
+
int rc;
rc = libusb_init(pluctx);
- if (rc != 0)
+ if (rc != 0) {
+ LOGP(DLUSB, LOGL_ERROR, "Error initializing libusb: %s\n", libusb_strerror(rc));
return rc;
+ }
if (pluctx)
luctx = *pluctx;
@@ -754,6 +755,17 @@ int osmo_libusb_init(libusb_context **pluctx)
libusb_set_pollfd_notifiers(luctx, osmo_usb_added_cb, osmo_usb_removed_cb, luctx);
+ /* get the initial file descriptors which were created even before during libusb_init() */
+ pfds = libusb_get_pollfds(luctx);
+ if (pfds) {
+ const struct libusb_pollfd **pfds2 = pfds;
+ const struct libusb_pollfd *pfd;
+ /* synthesize 'add' call-backs. not sure why libusb doesn't do that by itself? */
+ for (pfd = *pfds2; pfd; pfd = *++pfds2)
+ osmo_usb_added_cb(pfd->fd, pfd->events, luctx);
+ libusb_free_pollfds(pfds);
+ }
+
return 0;
}
diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am
index 794b96ca..c314a141 100644
--- a/src/vty/Makefile.am
+++ b/src/vty/Makefile.am
@@ -1,10 +1,10 @@
# 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=13:0:0
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
-AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS)
if ENABLE_VTY
lib_LTLIBRARIES = libosmovty.la
@@ -14,5 +14,5 @@ libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \
fsm_vty.c talloc_ctx_vty.c \
cpu_sched_vty.c tdef_vty.c
libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
-libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la $(TALLOC_LIBS)
+libosmovty_la_LIBADD = $(top_builddir)/src/core/libosmocore.la $(TALLOC_LIBS) $(PTHREAD_LIBS)
endif
diff --git a/src/vty/command.c b/src/vty/command.c
index 4825109e..a3e0e36b 100644
--- a/src/vty/command.c
+++ b/src/vty/command.c
@@ -38,14 +38,18 @@ Boston, MA 02110-1301, USA. */
#include <ctype.h>
#include <time.h>
#include <limits.h>
+#include <signal.h>
#include <sys/time.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <osmocom/vty/vector.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
+#include <osmocom/core/logging.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
#include <osmocom/core/utils.h>
#ifndef MAXPATHLEN
@@ -63,6 +67,9 @@ Boston, MA 02110-1301, USA. */
void *tall_vty_cmd_ctx;
+/* Set by on_dso_load_starttime() for "show uptime". */
+static struct timespec starttime;
+
/* Command vector which includes some level of command lists. Normally
each daemon maintains each own cmdvec. */
vector cmdvec;
@@ -70,6 +77,21 @@ vector cmdvec;
/* Host information structure. */
struct host host;
+struct vty_parent_node {
+ struct llist_head entry;
+
+ /*! private data, specified by creator */
+ void *priv;
+ void *index;
+
+ /*! Node status of this vty */
+ int node;
+
+ /*! When reading from a config file, these are the indenting characters expected for children of
+ * this VTY node. */
+ char *indent;
+};
+
/* Standard command node structures. */
struct cmd_node auth_node = {
AUTH_NODE,
@@ -1085,6 +1107,7 @@ static int config_write_host(struct vty *vty)
static vector cmd_node_vector(vector v, enum node_type ntype)
{
struct cmd_node *cnode = vector_slot(v, ntype);
+ OSMO_ASSERT(cnode != NULL);
return cnode->cmd_vector;
}
@@ -1342,7 +1365,6 @@ static enum match_type cmd_ipv6_prefix_match(const char *str)
int colons = 0, nums = 0, double_colon = 0;
int mask;
const char *sp = NULL;
- char *endptr = NULL;
if (str == NULL)
return PARTLY_MATCH;
@@ -1440,11 +1462,7 @@ static enum match_type cmd_ipv6_prefix_match(const char *str)
if (state < STATE_MASK)
return PARTLY_MATCH;
- mask = strtol(str, &endptr, 10);
- if (*endptr != '\0')
- return NO_MATCH;
-
- if (mask < 0 || mask > 128)
+ if (osmo_str_to_int(&mask, str, 10, 0, 128))
return NO_MATCH;
/* I don't know why mask < 13 makes command match partly.
@@ -1477,19 +1495,52 @@ static enum match_type cmd_ipv6_prefix_match(const char *str)
#error "LONG_MAX not defined!"
#endif
-static int cmd_range_match(const char *range, const char *str)
+/* This function is aimed at quickly guessing & filtering the numeric base a
+ * string can contain, by no means validates the entire value.
+ * Returns 16 if string follows pattern "({+,-}0x[digits])"
+ * Returns 10 if string follows pattern "({+,-}[digits])"
+ * Returns a negative value if something other is detected (error)
+*/
+static int check_base(const char *str)
+{
+ const char *ptr = str;
+ /* Skip any space */
+ while (isspace(*ptr)) ptr++;
+
+ /* Skip optional sign: */
+ if (*ptr == '+' || *ptr == '-')
+ ptr++;
+ if (*ptr == '0') {
+ ptr++;
+ if (*ptr == '\0' || isdigit(*ptr))
+ return 10;
+ if (!(*ptr == 'x' || *ptr == 'X'))
+ return -1;
+ ptr++;
+ if (isxdigit(*ptr))
+ return 16;
+ return -1;
+ }
+ return 10;
+}
+
+int vty_cmd_range_match(const char *range, const char *str)
{
char *p;
char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
char *endptr = NULL;
+ int min_base, max_base, val_base;
if (str == NULL)
return 1;
+ if ((val_base = check_base(str)) < 0)
+ return 0;
+
if (range[1] == '-') {
signed long min = 0, max = 0, val;
- val = strtol(str, &endptr, 10);
+ val = strtol(str, &endptr, val_base);
if (*endptr != '\0')
return 0;
@@ -1501,7 +1552,9 @@ static int cmd_range_match(const char *range, const char *str)
return 0;
strncpy(buf, range, p - range);
buf[p - range] = '\0';
- min = -strtol(buf, &endptr, 10);
+ if ((min_base = check_base(buf)) < 0)
+ return 0;
+ min = -strtol(buf, &endptr, min_base);
if (*endptr != '\0')
return 0;
@@ -1513,7 +1566,9 @@ static int cmd_range_match(const char *range, const char *str)
return 0;
strncpy(buf, range, p - range);
buf[p - range] = '\0';
- max = strtol(buf, &endptr, 10);
+ if ((max_base = check_base(buf)) < 0)
+ return 0;
+ max = strtol(buf, &endptr, max_base);
if (*endptr != '\0')
return 0;
@@ -1525,7 +1580,7 @@ static int cmd_range_match(const char *range, const char *str)
if (str[0] == '-')
return 0;
- val = strtoul(str, &endptr, 10);
+ val = strtoul(str, &endptr, val_base);
if (*endptr != '\0')
return 0;
@@ -1537,7 +1592,9 @@ static int cmd_range_match(const char *range, const char *str)
return 0;
strncpy(buf, range, p - range);
buf[p - range] = '\0';
- min = strtoul(buf, &endptr, 10);
+ if ((min_base = check_base(buf)) < 0)
+ return 0;
+ min = strtoul(buf, &endptr, min_base);
if (*endptr != '\0')
return 0;
@@ -1549,7 +1606,9 @@ static int cmd_range_match(const char *range, const char *str)
return 0;
strncpy(buf, range, p - range);
buf[p - range] = '\0';
- max = strtoul(buf, &endptr, 10);
+ if ((max_base = check_base(buf)) < 0)
+ return 0;
+ max = strtoul(buf, &endptr, max_base);
if (*endptr != '\0')
return 0;
@@ -1557,6 +1616,14 @@ static int cmd_range_match(const char *range, const char *str)
return 0;
}
+ /* Don't allow ranges specified by min and max with different bases */
+ if (min_base != max_base)
+ return 0;
+ /* arg value passed must match the base of the range */
+ if (min_base != val_base)
+ return 0;
+
+ /* Everything's fine */
return 1;
}
@@ -1600,7 +1667,7 @@ cmd_match(const char *str, const char *command,
return VARARG_MATCH;
else if (CMD_RANGE(str))
{
- if (cmd_range_match(str, command))
+ if (vty_cmd_range_match(str, command))
return RANGE_MATCH;
}
#ifdef HAVE_IPV6
@@ -1797,7 +1864,7 @@ is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
}
break;
case RANGE_MATCH:
- if (cmd_range_match
+ if (vty_cmd_range_match
(str, command)) {
if (matched
&& strcmp(matched,
@@ -1890,7 +1957,7 @@ static const char *cmd_entry_function_desc(const char *src, const char *dst)
return dst;
if (CMD_RANGE(dst)) {
- if (cmd_range_match(dst, src))
+ if (vty_cmd_range_match(dst, src))
return dst;
else
return NULL;
@@ -2386,6 +2453,7 @@ static bool vty_pop_parent(struct vty *vty)
llist_del(&parent->entry);
vty->node = parent->node;
vty->priv = parent->priv;
+ vty->index = parent->index;
if (vty->indent)
talloc_free(vty->indent);
vty->indent = parent->indent;
@@ -2596,6 +2664,7 @@ cmd_execute_command_real(vector vline, struct vty *vty,
struct vty_parent_node this_node = {
.node = vty->node,
.priv = vty->priv,
+ .index = vty->index,
.indent = vty->indent,
};
struct vty_parent_node *parent = vty_parent(vty);
@@ -2871,6 +2940,7 @@ int config_from_file(struct vty *vty, FILE * fp)
this_node = (struct vty_parent_node){
.node = vty->node,
.priv = vty->priv,
+ .index = vty->index,
.indent = vty->indent,
};
@@ -2927,7 +2997,7 @@ return_invalid_indent:
/* Configration from terminal */
DEFUN(config_terminal,
config_terminal_cmd,
- "configure terminal",
+ "configure [terminal]",
"Configuration from vty interface\n" "Configuration terminal\n")
{
if (vty_config_lock(vty))
@@ -3018,6 +3088,18 @@ gDEFUN(config_exit,
return CMD_SUCCESS;
}
+DEFUN(shutdown,
+ shutdown_cmd, "shutdown", "Request a shutdown of the program\n")
+{
+ LOGP(DLGLOBAL, LOGL_INFO, "Shutdown requested from telnet\n");
+ vty_out(vty, "%s is shutting down. Bye!%s", host.app_info->name, VTY_NEWLINE);
+
+ /* Same exit path as if it was killed by the service manager */
+ kill(getpid(), SIGTERM);
+
+ return CMD_SUCCESS;
+}
+
/* Show version. */
DEFUN(show_version,
show_version_cmd, "show version", SHOW_STR "Displays program version\n")
@@ -3037,6 +3119,23 @@ DEFUN(show_online_help,
return CMD_SUCCESS;
}
+DEFUN(show_pid,
+ show_pid_cmd, "show pid", SHOW_STR "Displays the process ID\n")
+{
+ vty_out(vty, "%d%s", getpid(), VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_uptime,
+ show_uptime_cmd, "show uptime", SHOW_STR "Displays how long the program has been running\n")
+{
+ vty_out(vty, "%s has been running for ", host.app_info->name);
+ vty_out_uptime(vty, &starttime);
+ vty_out_newline(vty);
+
+ return CMD_SUCCESS;
+}
+
/* Help display function for all node. */
gDEFUN(config_help,
config_help_cmd, "help", "Description of the interactive help system\n")
@@ -3758,16 +3857,7 @@ DEFUN(config_terminal_length, config_terminal_length_cmd,
"Set number of lines on a screen\n"
"Number of lines on screen (0 for no pausing)\n")
{
- int lines;
- char *endptr = NULL;
-
- lines = strtol(argv[0], &endptr, 10);
- if (lines < 0 || lines > 512 || *endptr != '\0') {
- vty_out(vty, "length is malformed%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- vty->lines = lines;
-
+ vty->lines = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -3786,16 +3876,7 @@ DEFUN(service_terminal_length, service_terminal_length_cmd,
"System wide terminal length configuration\n"
"Number of lines of VTY (0 means no line control)\n")
{
- int lines;
- char *endptr = NULL;
-
- lines = strtol(argv[0], &endptr, 10);
- if (lines < 0 || lines > 512 || *endptr != '\0') {
- vty_out(vty, "length is malformed%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- host.lines = lines;
-
+ host.lines = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -4219,6 +4300,11 @@ void host_config_set(const char *filename)
host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
}
+const char *host_config_file(void)
+{
+ return host.config;
+}
+
/*! Deprecated, now happens implicitly when calling install_node().
* Users of the API may still attempt to call this function, hence
* leave it here as a no-op. */
@@ -4341,6 +4427,8 @@ void cmd_init(int terminal)
install_node(&config_node, config_write_host);
/* Each node's basic commands. */
+ install_lib_element(VIEW_NODE, &show_pid_cmd);
+ install_lib_element(VIEW_NODE, &show_uptime_cmd);
install_lib_element(VIEW_NODE, &show_version_cmd);
install_lib_element(VIEW_NODE, &show_online_help_cmd);
if (terminal) {
@@ -4359,6 +4447,7 @@ void cmd_init(int terminal)
install_lib_element(ENABLE_NODE, &config_disable_cmd);
install_lib_element(ENABLE_NODE, &config_terminal_cmd);
install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
+ install_lib_element(ENABLE_NODE, &shutdown_cmd);
}
install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
install_lib_element(ENABLE_NODE, &show_version_cmd);
@@ -4394,6 +4483,11 @@ void cmd_init(int terminal)
srand(time(NULL));
}
+static __attribute__((constructor)) void on_dso_load_starttime(void)
+{
+ osmo_clock_gettime(CLOCK_MONOTONIC, &starttime);
+}
+
/* FIXME: execute this section in the unit test instead */
static __attribute__((constructor)) void on_dso_load(void)
{
diff --git a/src/vty/cpu_sched_vty.c b/src/vty/cpu_sched_vty.c
index 4ccc6274..7198f747 100644
--- a/src/vty/cpu_sched_vty.c
+++ b/src/vty/cpu_sched_vty.c
@@ -25,6 +25,8 @@
#define _GNU_SOURCE
+#include "config.h"
+
#include <string.h>
#include <stdlib.h>
#include <errno.h>
@@ -87,7 +89,7 @@ static struct cmd_node sched_node = {
};
/* returns number of configured CPUs in the system, or negative otherwise */
-static int get_num_cpus() {
+static int get_num_cpus(void) {
static unsigned int num_cpus = 0;
long ln;
@@ -276,7 +278,6 @@ static bool proc_name_exists(const char *name, pid_t *res_pid)
static enum sched_vty_thread_id procname2pid(pid_t *res_pid, const char *str, bool applynow)
{
size_t i, len;
- char *end;
bool is_pid = true;
if (strcmp(str, "all") == 0) {
@@ -297,12 +298,12 @@ static enum sched_vty_thread_id procname2pid(pid_t *res_pid, const char *str, bo
}
}
if (is_pid) {
- errno = 0;
- *res_pid = strtoul(str, &end, 0);
- if ((errno == ERANGE && *res_pid == ULONG_MAX) || (errno && !*res_pid) ||
- str == end) {
+ int64_t val;
+ if (osmo_str_to_int64(&val, str, 0, 0, INT64_MAX))
+ return SCHED_VTY_THREAD_UNKNOWN;
+ *res_pid = (pid_t)val;
+ if (*res_pid != val)
return SCHED_VTY_THREAD_UNKNOWN;
- }
if (!applynow || proc_tid_exists(*res_pid))
return SCHED_VTY_THREAD_ID;
else
@@ -638,8 +639,10 @@ int osmo_cpu_sched_vty_apply_localthread(void)
return 0;
}
+#ifdef HAVE_PTHREAD_GETNAME_NP
if (pthread_getname_np(pthread_self(), name, sizeof(name)) == 0)
has_name = true;
+#endif
/* Get latest matching mask for the thread */
pthread_mutex_lock(&sched_vty_opts->cpu_affinity_li_mutex);
diff --git a/src/vty/fsm_vty.c b/src/vty/fsm_vty.c
index 83a8e791..da6038fa 100644
--- a/src/vty/fsm_vty.c
+++ b/src/vty/fsm_vty.c
@@ -14,16 +14,12 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdlib.h>
#include <string.h>
-#include "../../config.h"
+#include "config.h"
#include <osmocom/vty/command.h>
#include <osmocom/vty/buffer.h>
@@ -65,7 +61,7 @@ void vty_out_fsm2(struct vty *vty, const char *prefix, struct osmo_fsm *fsm)
if (fsm->event_names) {
for (evt_name = fsm->event_names; evt_name->str != NULL; evt_name++) {
vty_out(vty, "%s Event %02u (0x%08x): '%s'%s", prefix, evt_name->value,
- (1 << evt_name->value), evt_name->str, VTY_NEWLINE);
+ (1U << evt_name->value), evt_name->str, VTY_NEWLINE);
}
} else
vty_out(vty, "%s No event names are defined for this FSM! Please fix!%s", prefix, VTY_NEWLINE);
diff --git a/src/vty/logging_vty.c b/src/vty/logging_vty.c
index 3b131c27..678ae686 100644
--- a/src/vty/logging_vty.c
+++ b/src/vty/logging_vty.c
@@ -15,16 +15,12 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdlib.h>
#include <string.h>
-#include "../../config.h"
+#include "config.h"
#include <osmocom/core/talloc.h>
#include <osmocom/core/logging.h>
@@ -386,6 +382,10 @@ DEFUN(logging_level,
tgt->categories[category].enabled = 1;
tgt->categories[category].loglevel = level;
+#if !defined(EMBEDDED)
+ log_cache_update(category, 1, level);
+#endif
+
RET_WITH_UNLOCK(CMD_SUCCESS);
}
@@ -410,6 +410,9 @@ DEFUN(logging_level_set_all, logging_level_set_all_cmd,
cat->enabled = 1;
cat->loglevel = level;
+#if !defined(EMBEDDED)
+ log_cache_update(i, 1, level);
+#endif
}
RET_WITH_UNLOCK(CMD_SUCCESS);
}
@@ -834,9 +837,31 @@ DEFUN(cfg_log_gsmtap, cfg_log_gsmtap_cmd,
RET_WITH_UNLOCK(CMD_SUCCESS);
}
+DEFUN(cfg_no_log_gsmtap, cfg_no_log_gsmtap_cmd,
+ "no log gsmtap [HOSTNAME]",
+ NO_STR LOG_STR "Logging via GSMTAP\n"
+ "Host name to send the GSMTAP logging to (UDP port 4729)\n")
+{
+ const char *hostname = argc ? argv[0] : "127.0.0.1";
+ struct log_target *tgt;
+
+ log_tgt_mutex_lock();
+ tgt = log_target_find(LOG_TGT_TYPE_GSMTAP, hostname);
+ if (tgt == NULL) {
+ vty_out(vty, "%% Unable to find GSMTAP log target for %s%s",
+ hostname, VTY_NEWLINE);
+ RET_WITH_UNLOCK(CMD_WARNING);
+ }
+
+ log_target_destroy(tgt);
+
+ RET_WITH_UNLOCK(CMD_SUCCESS);
+}
+
DEFUN(cfg_log_stderr, cfg_log_stderr_cmd,
- "log stderr",
- LOG_STR "Logging via STDERR of the process\n")
+ "log stderr [blocking-io]",
+ LOG_STR "Logging via STDERR of the process\n"
+ "Use blocking, synchronous I/O\n")
{
struct log_target *tgt;
@@ -852,6 +877,11 @@ DEFUN(cfg_log_stderr, cfg_log_stderr_cmd,
log_add_target(tgt);
}
+ if (argc > 0 && !strcmp(argv[0], "blocking-io"))
+ log_target_file_switch_to_stream(tgt);
+ else
+ log_target_file_switch_to_wqueue(tgt);
+
vty->index = tgt;
vty->node = CFG_LOG_NODE;
@@ -878,8 +908,9 @@ DEFUN(cfg_no_log_stderr, cfg_no_log_stderr_cmd,
}
DEFUN(cfg_log_file, cfg_log_file_cmd,
- "log file .FILENAME",
- LOG_STR "Logging to text file\n" "Filename\n")
+ "log file FILENAME [blocking-io]",
+ LOG_STR "Logging to text file\n" "Filename\n"
+ "Use blocking, synchronous I/O\n")
{
const char *fname = argv[0];
struct log_target *tgt;
@@ -896,6 +927,11 @@ DEFUN(cfg_log_file, cfg_log_file_cmd,
log_add_target(tgt);
}
+ if (argc > 1 && !strcmp(argv[1], "blocking-io"))
+ log_target_file_switch_to_stream(tgt);
+ else
+ log_target_file_switch_to_wqueue(tgt);
+
vty->index = tgt;
vty->node = CFG_LOG_NODE;
@@ -904,7 +940,7 @@ DEFUN(cfg_log_file, cfg_log_file_cmd,
DEFUN(cfg_no_log_file, cfg_no_log_file_cmd,
- "no log file .FILENAME",
+ "no log file FILENAME",
NO_STR LOG_STR "Logging to text file\n" "Filename\n")
{
const char *fname = argv[0];
@@ -979,7 +1015,10 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt)
return 1;
break;
case LOG_TGT_TYPE_STDERR:
- vty_out(vty, "log stderr%s", VTY_NEWLINE);
+ if (tgt->tgt_file.wqueue)
+ vty_out(vty, "log stderr%s", VTY_NEWLINE);
+ else
+ vty_out(vty, "log stderr blocking-io%s", VTY_NEWLINE);
break;
case LOG_TGT_TYPE_SYSLOG:
#ifdef HAVE_SYSLOG_H
@@ -990,7 +1029,10 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt)
#endif
break;
case LOG_TGT_TYPE_FILE:
- vty_out(vty, "log file %s%s", tgt->tgt_file.fname, VTY_NEWLINE);
+ if (tgt->tgt_file.wqueue)
+ vty_out(vty, "log file %s%s", tgt->tgt_file.fname, VTY_NEWLINE);
+ else
+ vty_out(vty, "log file %s blocking-io%s", tgt->tgt_file.fname, VTY_NEWLINE);
break;
case LOG_TGT_TYPE_STRRB:
vty_out(vty, "log alarms %zu%s",
@@ -1161,7 +1203,7 @@ static void gen_vty_logp_cmd_strs(struct cmd_element *cmd)
/*! Register logging related commands to the VTY. Call this once from
* your application if you want to support those commands. */
-void logging_vty_add_cmds()
+void logging_vty_add_cmds(void)
{
install_lib_element_ve(&enable_logging_cmd);
install_lib_element_ve(&disable_logging_cmd);
@@ -1231,4 +1273,5 @@ void logging_vty_add_cmds()
install_lib_element(CONFIG_NODE, &cfg_log_systemd_journal_cmd);
install_lib_element(CONFIG_NODE, &cfg_no_log_systemd_journal_cmd);
install_lib_element(CONFIG_NODE, &cfg_log_gsmtap_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_no_log_gsmtap_cmd);
}
diff --git a/src/vty/stats_vty.c b/src/vty/stats_vty.c
index 630ee325..f940018f 100644
--- a/src/vty/stats_vty.c
+++ b/src/vty/stats_vty.c
@@ -1,5 +1,5 @@
/*
- * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009-2022 by Harald Welte <laforge@gnumonks.org>
* (C) 2009-2014 by Holger Hans Peter Freyther
* (C) 2015 by sysmocom - s.f.m.c. GmbH
* All Rights Reserved
@@ -16,16 +16,12 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdlib.h>
#include <string.h>
-#include "../../config.h"
+#include "config.h"
#include <osmocom/vty/command.h>
#include <osmocom/vty/buffer.h>
@@ -37,11 +33,13 @@
#include <osmocom/core/stats.h>
#include <osmocom/core/counter.h>
#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/stats_tcp.h>
#define CFG_STATS_STR "Configure stats sub-system\n"
#define CFG_REPORTER_STR "Configure a stats reporter\n"
#define SHOW_STATS_STR "Show statistical values\n"
+#define SKIP_ZERO_STR "Skip items with total count zero\n"
#define STATS_STR "Stats related commands\n"
@@ -269,14 +267,20 @@ DEFUN(cfg_stats_reporter_flush_period, cfg_stats_reporter_flush_period_cmd,
}
DEFUN(cfg_stats_reporter_statsd, cfg_stats_reporter_statsd_cmd,
- "stats reporter statsd",
- CFG_STATS_STR CFG_REPORTER_STR "Report to a STATSD server\n")
+ "stats reporter statsd [NAME]",
+ CFG_STATS_STR CFG_REPORTER_STR
+ "Report to a STATSD server\n"
+ "Name of the reporter\n")
{
struct osmo_stats_reporter *srep;
+ const char *name = NULL;
+
+ if (argc > 0)
+ name = argv[0];
- srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL);
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, name);
if (!srep) {
- srep = osmo_stats_reporter_create_statsd(NULL);
+ srep = osmo_stats_reporter_create_statsd(name);
if (!srep) {
vty_out(vty, "%% Unable to create statsd reporter%s",
VTY_NEWLINE);
@@ -293,15 +297,21 @@ DEFUN(cfg_stats_reporter_statsd, cfg_stats_reporter_statsd_cmd,
}
DEFUN(cfg_no_stats_reporter_statsd, cfg_no_stats_reporter_statsd_cmd,
- "no stats reporter statsd",
- NO_STR CFG_STATS_STR CFG_REPORTER_STR "Report to a STATSD server\n")
+ "no stats reporter statsd [NAME]",
+ NO_STR CFG_STATS_STR CFG_REPORTER_STR
+ "Report to a STATSD server\n"
+ "Name of the reporter\n")
{
struct osmo_stats_reporter *srep;
+ const char *name = NULL;
- srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL);
+ if (argc > 0)
+ name = argv[0];
+
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, name);
if (!srep) {
- vty_out(vty, "%% No statsd logging active%s",
- VTY_NEWLINE);
+ vty_out(vty, "%% There is no such statsd reporter with name '%s'%s",
+ name ? name : "", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -311,14 +321,20 @@ DEFUN(cfg_no_stats_reporter_statsd, cfg_no_stats_reporter_statsd_cmd,
}
DEFUN(cfg_stats_reporter_log, cfg_stats_reporter_log_cmd,
- "stats reporter log",
- CFG_STATS_STR CFG_REPORTER_STR "Report to the logger\n")
+ "stats reporter log [NAME]",
+ CFG_STATS_STR CFG_REPORTER_STR
+ "Report to the logger\n"
+ "Name of the reporter\n")
{
struct osmo_stats_reporter *srep;
+ const char *name = NULL;
- srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL);
+ if (argc > 0)
+ name = argv[0];
+
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, name);
if (!srep) {
- srep = osmo_stats_reporter_create_log(NULL);
+ srep = osmo_stats_reporter_create_log(name);
if (!srep) {
vty_out(vty, "%% Unable to create log reporter%s",
VTY_NEWLINE);
@@ -335,15 +351,21 @@ DEFUN(cfg_stats_reporter_log, cfg_stats_reporter_log_cmd,
}
DEFUN(cfg_no_stats_reporter_log, cfg_no_stats_reporter_log_cmd,
- "no stats reporter log",
- NO_STR CFG_STATS_STR CFG_REPORTER_STR "Report to the logger\n")
+ "no stats reporter log [NAME]",
+ NO_STR CFG_STATS_STR CFG_REPORTER_STR
+ "Report to the logger\n"
+ "Name of the reporter\n")
{
struct osmo_stats_reporter *srep;
+ const char *name = NULL;
+
+ if (argc > 0)
+ name = argv[0];
- srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL);
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, name);
if (!srep) {
- vty_out(vty, "%% No log reporting active%s",
- VTY_NEWLINE);
+ vty_out(vty, "%% There is no such log reporter with name '%s'%s",
+ name ? name : "", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -369,27 +391,60 @@ DEFUN(cfg_stats_interval, cfg_stats_interval_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_tcp_stats_interval, cfg_tcp_stats_interval_cmd,
+ "stats-tcp interval <0-65535>",
+ CFG_STATS_STR "Set the tcp socket stats polling interval\n"
+ "Interval in seconds (0 disables the polling interval)\n")
+{
+ int rc;
+ int interval = atoi(argv[0]);
+ rc = osmo_stats_tcp_set_interval(interval);
+ if (rc < 0) {
+ vty_out(vty, "%% Unable to set interval: %s%s",
+ strerror(-rc), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tcp_stats_batch_size, cfg_tcp_stats_batch_size_cmd,
+ "stats-tcp batch-size <1-65535>",
+ CFG_STATS_STR "Set the number of tcp sockets that are processed per stats polling interval\n"
+ "Number of sockets per interval\n")
+{
+ osmo_tcp_stats_config->batch_size = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
DEFUN(show_stats,
show_stats_cmd,
- "show stats",
- SHOW_STR SHOW_STATS_STR)
+ "show stats [skip-zero]",
+ SHOW_STR SHOW_STATS_STR SKIP_ZERO_STR)
{
- vty_out_statistics_full(vty, "");
+ bool skip_zero = false;
+ if (argc > 0)
+ skip_zero = true;
+
+ vty_out_statistics_full2(vty, "", skip_zero);
return CMD_SUCCESS;
}
DEFUN(show_stats_level,
show_stats_level_cmd,
- "show stats level (global|peer|subscriber)",
+ "show stats level (global|peer|subscriber) [skip-zero]",
SHOW_STR SHOW_STATS_STR
"Set the maximum group level\n"
"Show global groups only\n"
"Show global and network peer related groups\n"
- "Show global, peer, and subscriber groups\n")
+ "Show global, peer, and subscriber groups\n" SKIP_ZERO_STR)
{
int level = get_string_value(stats_class_strs, argv[0]);
- vty_out_statistics_partial(vty, "", level);
+ bool skip_zero = false;
+ if (argc > 1)
+ skip_zero = true;
+ vty_out_statistics_partial2(vty, "", level, skip_zero);
return CMD_SUCCESS;
}
@@ -415,13 +470,6 @@ static int asciidoc_handle_counter(struct osmo_counter *counter, void *sctx_)
static void asciidoc_counter_generate(struct vty *vty)
{
- if (osmo_counters_count() == 0)
- {
- vty_out(vty, "// there are no ungrouped osmo_counters%s",
- VTY_NEWLINE);
- return;
- }
-
vty_out(vty, "// ungrouped osmo_counters%s", VTY_NEWLINE);
vty_out(vty, ".ungrouped osmo counters%s", VTY_NEWLINE);
vty_out(vty, "[options=\"header\"]%s", VTY_NEWLINE);
@@ -482,10 +530,11 @@ static int asciidoc_osmo_stat_item_handler(
{
struct vty *vty = sctx_;
- char *name = osmo_asciidoc_escape(item->desc->name);
- char *description = osmo_asciidoc_escape(item->desc->description);
+ const struct osmo_stat_item_desc *desc = osmo_stat_item_get_desc(item);
+ char *name = osmo_asciidoc_escape(desc->name);
+ char *description = osmo_asciidoc_escape(desc->description);
char *group_name_prefix = osmo_asciidoc_escape(statg->desc->group_name_prefix);
- char *unit = osmo_asciidoc_escape(item->desc->unit);
+ char *unit = osmo_asciidoc_escape(desc->unit);
/* | Name | Reference | Description | Unit | */
vty_out(vty, "| %s | <<%s_%s>> | %s | %s%s",
@@ -542,30 +591,48 @@ DEFUN(show_stats_asciidoc_table,
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, "=== 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);
+ if (osmo_counters_count() == 0)
+ {
+ vty_out(vty, "// there are no ungrouped osmo_counters%s",
+ VTY_NEWLINE);
+ } else {
+ 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;
}
+struct rctr_vty_ctx {
+ struct vty *vty;
+ bool skip_zero;
+};
+
static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
{
- struct vty *vty = sctx_;
- vty_out(vty, "%s %u:%s", ctrg->desc->group_description, ctrg->idx, VTY_NEWLINE);
- vty_out_rate_ctr_group_fmt(vty, "%25n: %10c (%S/s %M/m %H/h %D/d) %d", ctrg);
+ struct rctr_vty_ctx *sctx = sctx_;
+ struct vty *vty = sctx->vty;
+ vty_out(vty, "%s %u", ctrg->desc->group_description, ctrg->idx);
+ if (ctrg->name != NULL)
+ vty_out(vty, " (%s)", ctrg->name);
+ vty_out(vty, ":%s", VTY_NEWLINE);
+ vty_out_rate_ctr_group_fmt2(vty, "%25n: %10c (%S/s %M/m %H/h %D/d) %d", ctrg, sctx->skip_zero);
return 0;
}
DEFUN(show_rate_counters,
show_rate_counters_cmd,
- "show rate-counters",
- SHOW_STR "Show all rate counters\n")
+ "show rate-counters [skip-zero]",
+ SHOW_STR "Show all rate counters\n" SKIP_ZERO_STR)
{
- rate_ctr_for_each_group(rate_ctr_group_handler, vty);
+ struct rctr_vty_ctx rctx = { .vty = vty, .skip_zero = false };
+ if (argc > 0)
+ rctx.skip_zero = true;
+ rate_ctr_for_each_group(rate_ctr_group_handler, &rctx);
return CMD_SUCCESS;
}
@@ -584,37 +651,37 @@ static int reset_rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_
return 0;
}
-static int reset_osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *sctx_)
-{
- osmo_stat_item_group_reset(statg);
- return 0;
-}
-
DEFUN(stats_reset,
stats_reset_cmd,
"stats reset",
- STATS_STR "Reset all stats\n")
+ STATS_STR "Reset all rate counter stats\n")
{
rate_ctr_for_each_group(reset_rate_ctr_group_handler, NULL);
- osmo_stat_item_for_each_group(reset_osmo_stat_item_group_handler, NULL);
return CMD_SUCCESS;
}
static int config_write_stats_reporter(struct vty *vty, struct osmo_stats_reporter *srep)
{
- if (srep == NULL)
- return 0;
+ const char *type = NULL;
switch (srep->type) {
case OSMO_STATS_REPORTER_STATSD:
- vty_out(vty, "stats reporter statsd%s", VTY_NEWLINE);
+ type = "statsd";
break;
case OSMO_STATS_REPORTER_LOG:
- vty_out(vty, "stats reporter log%s", VTY_NEWLINE);
+ type = "log";
break;
+ default:
+ /* don't try to save unknown stats reporters to the VTY. Imagine some
+ * application registering a new application specific stats reporter that
+ * this VTY code knows nothing about! */
+ return 0;
}
- vty_out(vty, " disable%s", VTY_NEWLINE);
+ vty_out(vty, "stats reporter %s", type);
+ if (srep->name != NULL)
+ vty_out(vty, " %s", srep->name);
+ vty_out(vty, "%s", VTY_NEWLINE);
if (srep->have_net_config) {
if (srep->dest_addr_str)
@@ -648,6 +715,8 @@ static int config_write_stats_reporter(struct vty *vty, struct osmo_stats_report
if (srep->enabled)
vty_out(vty, " enable%s", VTY_NEWLINE);
+ else
+ vty_out(vty, " disable%s", VTY_NEWLINE);
return 1;
}
@@ -656,13 +725,15 @@ static int config_write_stats(struct vty *vty)
{
struct osmo_stats_reporter *srep;
- /* TODO: loop through all reporters */
- srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL);
- config_write_stats_reporter(vty, srep);
- srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL);
- config_write_stats_reporter(vty, srep);
-
vty_out(vty, "stats interval %d%s", osmo_stats_config->interval, VTY_NEWLINE);
+ if (osmo_tcp_stats_config->interval != TCP_STATS_DEFAULT_INTERVAL)
+ vty_out(vty, "stats-tcp interval %d%s", osmo_tcp_stats_config->interval, VTY_NEWLINE);
+ if (osmo_tcp_stats_config->batch_size != TCP_STATS_DEFAULT_BATCH_SIZE)
+ vty_out(vty, "stats-tcp batch-size %d%s", osmo_tcp_stats_config->batch_size, VTY_NEWLINE);
+
+ /* Loop through all reporters */
+ llist_for_each_entry(srep, &osmo_stats_reporter_list, list)
+ config_write_stats_reporter(vty, srep);
return 1;
}
@@ -671,7 +742,7 @@ static int config_write_stats(struct vty *vty)
* Call this once during your application initialization if you would
* like to have stats VTY commands enabled.
*/
-void osmo_stats_vty_add_cmds()
+void osmo_stats_vty_add_cmds(void)
{
install_lib_element_ve(&show_stats_cmd);
install_lib_element_ve(&show_stats_level_cmd);
@@ -681,6 +752,8 @@ void osmo_stats_vty_add_cmds()
install_lib_element(CONFIG_NODE, &cfg_stats_reporter_log_cmd);
install_lib_element(CONFIG_NODE, &cfg_no_stats_reporter_log_cmd);
install_lib_element(CONFIG_NODE, &cfg_stats_interval_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_tcp_stats_interval_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_tcp_stats_batch_size_cmd);
install_node(&cfg_stats_node, config_write_stats);
diff --git a/src/vty/talloc_ctx_vty.c b/src/vty/talloc_ctx_vty.c
index 39664787..ea8ebe70 100644
--- a/src/vty/talloc_ctx_vty.c
+++ b/src/vty/talloc_ctx_vty.c
@@ -17,17 +17,13 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
#include <regex.h>
#include <string.h>
-#include <talloc.h>
+#include <osmocom/core/talloc.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/vty.h>
@@ -151,6 +147,8 @@ static void talloc_ctx_walk(const char *ctx, const char *depth,
/* Determine a context for report */
if (!strncmp(ctx, "app", 3))
talloc_ctx = host.app_info->tall_ctx;
+ else if (!strcmp(ctx, "global"))
+ talloc_ctx = OTC_GLOBAL;
else if (!strncmp(ctx, "all", 3))
talloc_ctx = NULL;
@@ -167,11 +165,12 @@ static void talloc_ctx_walk(const char *ctx, const char *depth,
}
#define BASE_CMD_STR \
- "show talloc-context (application|all) (full|brief|DEPTH)"
+ "show talloc-context (application|global|all) (full|brief|DEPTH)"
#define BASE_CMD_DESCR \
SHOW_STR "Show talloc memory hierarchy\n" \
"Application's context\n" \
+ "Global context (OTC_GLOBAL)\n" \
"All contexts, if NULL-context tracking is enabled\n" \
"Display a full talloc memory hierarchy\n" \
"Display a brief talloc memory hierarchy\n" \
diff --git a/src/vty/tdef_vty.c b/src/vty/tdef_vty.c
index 0556d8c9..bd209ae0 100644
--- a/src/vty/tdef_vty.c
+++ b/src/vty/tdef_vty.c
@@ -50,10 +50,9 @@
*/
struct osmo_tdef *osmo_tdef_vty_parse_T_arg(struct vty *vty, struct osmo_tdef *tdefs, const char *T_str)
{
- long l;
+ int l;
int T;
struct osmo_tdef *t;
- char *endptr;
const char *T_nr_str;
int sign = 1;
@@ -77,9 +76,7 @@ struct osmo_tdef *osmo_tdef_vty_parse_T_arg(struct vty *vty, struct osmo_tdef *t
return NULL;
}
- errno = 0;
- l = strtol(T_nr_str, &endptr, 10);
- if (errno || *endptr || l > INT_MAX || l < 0) {
+ if (osmo_str_to_int(&l, T_nr_str, 10, 0, INT_MAX)) {
vty_out(vty, "%% Invalid T timer argument (should be 'T1234' or 'X1234'): '%s'%s", T_str, VTY_NEWLINE);
return NULL;
}
@@ -245,7 +242,7 @@ void osmo_tdef_vty_out_all(struct vty *vty, struct osmo_tdef *tdefs, const char
/*! Write current timer configuration arguments to the vty. Skip all entries that reflect their default value.
* The passed prefix string must contain both necessary indent and the VTY command the specific implementation is using.
- * See tdef_vty_test_config_subnode.c and tdef_vty_test_dynamic.c for examples.
+ * See tdef_vty_config_subnode_test.c and tdef_vty_dynamic_test.c for examples.
* \param[in] vty VTY context.
* \param[in] tdefs Array of timers to print, ended with a fully zero-initialized entry.
* \param[in] prefix_fmt Arbitrary string to start each line with, with variable printf like arguments.
diff --git a/src/vty/telnet_interface.c b/src/vty/telnet_interface.c
index cd32e68f..8fa5dbff 100644
--- a/src/vty/telnet_interface.c
+++ b/src/vty/telnet_interface.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <sys/socket.h>
@@ -46,7 +42,7 @@
* process in order to enable interactive command-line introspection,
* interaction and configuration.
*
- * You typically call \ref telnet_init or \ref telnet_init_dynif once
+ * You typically call telnet_init_default once
* from your application code to enable this.
*/
@@ -64,26 +60,14 @@ static struct osmo_fd server_socket = {
.priv_nr = 0,
};
-/*! Initialize telnet based VTY interface listening to 127.0.0.1
- * \param[in] tall_ctx \ref talloc context
- * \param[in] priv private data to be passed to callback
- * \param[in] port TCP port number to bind to
- */
-int telnet_init(void *tall_ctx, void *priv, int port)
-{
- return telnet_init_dynif(tall_ctx, priv, "127.0.0.1", port);
-}
-
-/*! Initialize telnet based VTY interface
- * \param[in] tall_ctx \ref talloc context
- * \param[in] priv private data to be passed to callback
- * \param[in] ip IP to listen to ('::1' for localhost, '::0' for all, ...)
- * \param[in] port TCP port number to bind to
- */
-int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
+/* Helper for deprecating telnet_init_dynif(), which previously held this code */
+static int _telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
{
int rc;
+ if (port < 0)
+ return -EINVAL;
+
tall_telnet_ctx = talloc_named_const(tall_ctx, 1,
"telnet_connection");
@@ -98,22 +82,45 @@ int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
if (rc < 0) {
LOGP(DLGLOBAL, LOGL_ERROR, "Cannot bind telnet at %s %d\n",
ip, port);
- return -1;
+ return rc;
}
LOGP(DLGLOBAL, LOGL_NOTICE, "Available via telnet %s %d\n", ip, port);
return 0;
}
+/*! Initialize telnet based VTY interface listening to 127.0.0.1
+ * \param[in] tall_ctx \ref talloc context
+ * \param[in] priv private data to be passed to callback
+ * \param[in] port TCP port number to bind to
+ * \deprecated use telnet_init_default() instead
+ */
+int telnet_init(void *tall_ctx, void *priv, int port)
+{
+ return _telnet_init_dynif(tall_ctx, priv, "127.0.0.1", port);
+}
+
+/*! Initialize telnet based VTY interface
+ * \param[in] tall_ctx \ref talloc context
+ * \param[in] priv private data to be passed to callback
+ * \param[in] ip IP to listen to ('::1' for localhost, '::0' for all, ...)
+ * \param[in] port TCP port number to bind to
+ * \deprecated use telnet_init_default() instead
+ */
+int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
+{
+ return _telnet_init_dynif(tall_ctx, priv, ip, port);
+}
+
/*! Initializes telnet based VTY interface using the configured bind addr/port.
* \param[in] tall_ctx \ref talloc context
* \param[in] priv private data to be passed to callback
- * \param[in] default_port TCP port number to bind to if not explicitely configured
+ * \param[in] default_port TCP port number to bind to if not explicitly configured
*/
int telnet_init_default(void *tall_ctx, void *priv, int default_port)
{
- return telnet_init_dynif(tall_ctx, priv, vty_get_bind_addr(),
- vty_get_bind_port(default_port));
+ return _telnet_init_dynif(tall_ctx, priv, vty_get_bind_addr(),
+ vty_get_bind_port(default_port));
}
diff --git a/src/vty/utils.c b/src/vty/utils.c
index 0358d9bd..0f5a34e4 100644
--- a/src/vty/utils.c
+++ b/src/vty/utils.c
@@ -18,13 +18,10 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
+#include <stdbool.h>
#include <inttypes.h>
#include <string.h>
#include <ctype.h>
@@ -48,6 +45,7 @@ struct vty_out_context {
struct vty *vty;
const char *prefix;
int max_level;
+ bool skip_zero;
};
static int rate_ctr_handler(
@@ -57,6 +55,9 @@ static int rate_ctr_handler(
struct vty_out_context *vctx = vctx_;
struct vty *vty = vctx->vty;
+ if (vctx->skip_zero && ctr->current == 0)
+ return 0;
+
vty_out(vty, " %s%s: %8" PRIu64 " "
"(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s",
vctx->prefix, desc->description, ctr->current,
@@ -73,17 +74,24 @@ static int rate_ctr_handler(
* \param[in] vty The VTY to which it should be printed
* \param[in] prefix Any additional log prefix ahead of each line
* \param[in] ctrg Rate counter group to be printed
+ * \param[in] skip_zero Skip all zero-valued counters
*/
-void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
- struct rate_ctr_group *ctrg)
+void vty_out_rate_ctr_group2(struct vty *vty, const char *prefix,
+ struct rate_ctr_group *ctrg, bool skip_zero)
{
- struct vty_out_context vctx = {vty, prefix};
+ struct vty_out_context vctx = {vty, prefix, 0, skip_zero};
vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE);
rate_ctr_for_each_counter(ctrg, rate_ctr_handler, &vctx);
}
+void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
+ struct rate_ctr_group *ctrg)
+{
+ vty_out_rate_ctr_group2(vty, prefix, ctrg, false);
+}
+
static char *
pad_append_str(char *s, const char *a, int minwidth)
{
@@ -107,7 +115,12 @@ static int rate_ctr_handler_fmt(
struct vty_out_context *vctx = vctx_;
struct vty *vty = vctx->vty;
const char *fmt = vctx->prefix;
- char *s = talloc_strdup(vty, "");
+ char *s;
+
+ if (vctx->skip_zero && ctr->current == 0)
+ return 0;
+
+ s = talloc_strdup(vty, "");
OSMO_ASSERT(s);
while (*fmt) {
@@ -209,14 +222,20 @@ static int rate_ctr_handler_fmt(
* \param[in] vty The VTY to which it should be printed
* \param[in] ctrg Rate counter group to be printed
* \param[in] fmt A format which may contain the above directives.
+ * \param[in] skip_zero Skip all zero-valued counters
*/
-void vty_out_rate_ctr_group_fmt(struct vty *vty, const char *fmt,
- struct rate_ctr_group *ctrg)
+void vty_out_rate_ctr_group_fmt2(struct vty *vty, const char *fmt,
+ struct rate_ctr_group *ctrg, bool skip_zero)
{
- struct vty_out_context vctx = {vty, fmt};
+ struct vty_out_context vctx = {vty, fmt, 0, skip_zero};
rate_ctr_for_each_counter(ctrg, rate_ctr_handler_fmt, &vctx);
}
+void vty_out_rate_ctr_group_fmt(struct vty *vty, const char *fmt,
+ struct rate_ctr_group *ctrg)
+{
+ vty_out_rate_ctr_group_fmt2(vty, fmt, ctrg, false);
+}
static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *vctx_)
{
struct vty_out_context *vctx = vctx_;
@@ -225,12 +244,10 @@ static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *vctx_)
if (ctrg->desc->class_id > vctx->max_level)
return 0;
- if (ctrg->idx)
- vty_out(vty, "%s%s (%d):%s", vctx->prefix,
- ctrg->desc->group_description, ctrg->idx, VTY_NEWLINE);
- else
- vty_out(vty, "%s%s:%s", vctx->prefix,
- ctrg->desc->group_description, VTY_NEWLINE);
+ vty_out(vty, "%s%s (%d)", vctx->prefix, ctrg->desc->group_description, ctrg->idx);
+ if (ctrg->name)
+ vty_out(vty, "('%s')", ctrg->name);
+ vty_out(vty, ":%s", VTY_NEWLINE);
rate_ctr_for_each_counter(ctrg, rate_ctr_handler, vctx);
@@ -249,14 +266,15 @@ static int osmo_stat_item_handler(
{
struct vty_out_context *vctx = vctx_;
struct vty *vty = vctx->vty;
- const char *unit =
- item->desc->unit != OSMO_STAT_ITEM_NO_UNIT ?
- item->desc->unit : "";
+ const struct osmo_stat_item_desc *desc = osmo_stat_item_get_desc(item);
+ int32_t value = osmo_stat_item_get_last(item);
+ const char *unit = (desc->unit != OSMO_STAT_ITEM_NO_UNIT) ? desc->unit : "";
+
+ if (vctx->skip_zero && value == 0)
+ return 0;
vty_out(vty, " %s%s: %8" PRIi32 " %s%s",
- vctx->prefix, item->desc->description,
- osmo_stat_item_get_last(item),
- unit, VTY_NEWLINE);
+ vctx->prefix, desc->description, value, unit, VTY_NEWLINE);
return 0;
}
@@ -265,17 +283,24 @@ static int osmo_stat_item_handler(
* \param[in] vty The VTY to which it should be printed
* \param[in] prefix Any additional log prefix ahead of each line
* \param[in] statg Stat item group to be printed
+ * \param[in] skip_zero Skip all zero-valued counters
*/
-void vty_out_stat_item_group(struct vty *vty, const char *prefix,
- struct osmo_stat_item_group *statg)
+void vty_out_stat_item_group2(struct vty *vty, const char *prefix,
+ struct osmo_stat_item_group *statg, bool skip_zero)
{
- struct vty_out_context vctx = {vty, prefix};
+ struct vty_out_context vctx = {vty, prefix, 0, skip_zero};
vty_out(vty, "%s%s:%s", prefix, statg->desc->group_description,
VTY_NEWLINE);
osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, &vctx);
}
+void vty_out_stat_item_group(struct vty *vty, const char *prefix,
+ struct osmo_stat_item_group *statg)
+{
+ return vty_out_stat_item_group2(vty, prefix, statg, false);
+}
+
static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *vctx_)
{
struct vty_out_context *vctx = vctx_;
@@ -284,13 +309,10 @@ static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void
if (statg->desc->class_id > vctx->max_level)
return 0;
- if (statg->idx)
- vty_out(vty, "%s%s (%d):%s", vctx->prefix,
- statg->desc->group_description, statg->idx,
- VTY_NEWLINE);
- else
- vty_out(vty, "%s%s:%s", vctx->prefix,
- statg->desc->group_description, VTY_NEWLINE);
+ vty_out(vty, "%s%s (%d)", vctx->prefix, statg->desc->group_description, statg->idx);
+ if (statg->name)
+ vty_out(vty, "('%s')", statg->name);
+ vty_out(vty, ":%s", VTY_NEWLINE);
osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, vctx);
@@ -308,21 +330,22 @@ static int handle_counter(struct osmo_counter *counter, void *vctx_)
struct vty_out_context *vctx = vctx_;
struct vty *vty = vctx->vty;
const char *description = counter->description;
+ unsigned long value = osmo_counter_get(counter);
+
+ if (vctx->skip_zero && value == 0)
+ return 0;
if (!counter->description)
description = counter->name;
- vty_out(vty, " %s%s: %8lu%s",
- vctx->prefix, description,
- osmo_counter_get(counter), VTY_NEWLINE);
+ vty_out(vty, " %s%s: %8lu%s", vctx->prefix, description, value, VTY_NEWLINE);
return 0;
}
-void vty_out_statistics_partial(struct vty *vty, const char *prefix,
- int max_level)
+void vty_out_statistics_partial2(struct vty *vty, const char *prefix, int max_level, bool skip_zero)
{
- struct vty_out_context vctx = {vty, prefix, max_level};
+ struct vty_out_context vctx = {vty, prefix, max_level, skip_zero};
vty_out(vty, "%sUngrouped counters:%s", prefix, VTY_NEWLINE);
osmo_counters_for_each(handle_counter, &vctx);
@@ -330,9 +353,19 @@ void vty_out_statistics_partial(struct vty *vty, const char *prefix,
osmo_stat_item_for_each_group(osmo_stat_item_group_handler, &vctx);
}
+void vty_out_statistics_partial(struct vty *vty, const char *prefix, int max_level)
+{
+ return vty_out_statistics_partial2(vty, prefix, max_level, false);
+}
+
+void vty_out_statistics_full2(struct vty *vty, const char *prefix, bool skip_zero)
+{
+ vty_out_statistics_partial2(vty, prefix, INT_MAX, skip_zero);
+}
+
void vty_out_statistics_full(struct vty *vty, const char *prefix)
{
- vty_out_statistics_partial(vty, prefix, INT_MAX);
+ vty_out_statistics_full2(vty, prefix, false);
}
/*! Generate a VTY command string from value_string */
@@ -382,6 +415,7 @@ char *vty_cmd_string_from_valstr(void *ctx, const struct value_string *vals,
if (ret < 0)
goto err;
OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ (void)len; /* suppress warnings about len being set but not used */
err:
str[size-1] = '\0';
return str;
diff --git a/src/vty/vector.c b/src/vty/vector.c
index f9e5ec3e..34b161d8 100644
--- a/src/vty/vector.c
+++ b/src/vty/vector.c
@@ -16,11 +16,6 @@
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Zebra; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
diff --git a/src/vty/vty.c b/src/vty/vty.c
index f4e8e80f..3a549b43 100644
--- a/src/vty/vty.c
+++ b/src/vty/vty.c
@@ -66,6 +66,7 @@
#include <osmocom/vty/command.h>
#include <osmocom/vty/buffer.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
#include <osmocom/core/utils.h>
#ifndef MAXPATHLEN
@@ -207,6 +208,12 @@ static void vty_auth(struct vty *vty)
}
}
+void vty_flush(struct vty *vty)
+{
+ if (vty->obuf)
+ buffer_flush_all(vty->obuf, vty->fd);
+}
+
/*! Close a given vty interface. */
void vty_close(struct vty *vty)
{
@@ -332,6 +339,25 @@ int vty_out_newline(struct vty *vty)
return 0;
}
+/*! calculates the time difference of a give timespec to the current time
+ * and prints in a human readable format (days, hours, minutes, seconds).
+ */
+int vty_out_uptime(struct vty *vty, const struct timespec *starttime)
+{
+ struct timespec now;
+ struct timespec uptime;
+
+ osmo_clock_gettime(CLOCK_MONOTONIC, &now);
+ timespecsub(&now, starttime, &uptime);
+
+ int d = uptime.tv_sec / (3600 * 24);
+ int h = uptime.tv_sec / 3600 % 24;
+ int m = uptime.tv_sec / 60 % 60;
+ int s = uptime.tv_sec % 60;
+
+ return vty_out(vty, "%dd %dh %dm %ds", d, h, m, s);
+}
+
/*! return the current index of a given VTY */
void *vty_current_index(struct vty *vty)
{
@@ -459,6 +485,7 @@ static int vty_command(struct vty *vty)
static const char telnet_backward_char = 0x08;
static const char telnet_space_char = ' ';
+static const char telnet_escape_char = 0x1B;
/* Basic function to write buffer to vty. */
static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
@@ -858,6 +885,19 @@ static void vty_down_level(struct vty *vty)
vty->cp = 0;
}
+/* When '^L' is typed, clear all lines above the current one. */
+static void vty_clear_screen(struct vty *vty)
+{
+ vty_out(vty, "%c%s%c%s",
+ telnet_escape_char,
+ "[2J", /* Erase Screen */
+ telnet_escape_char,
+ "[H" /* Cursor Home */
+ );
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+}
+
/* When '^Z' is received from vty, move down to the enable mode. */
static void vty_end_config(struct vty *vty)
{
@@ -1402,6 +1442,9 @@ int vty_read(struct vty *vty)
case CONTROL('K'):
vty_kill_line(vty);
break;
+ case CONTROL('L'):
+ vty_clear_screen(vty);
+ break;
case CONTROL('N'):
vty_next_line(vty);
break;
@@ -1462,9 +1505,13 @@ int vty_read(struct vty *vty)
return 0;
}
-/* Read up configuration file */
-static int
-vty_read_file(FILE *confp, void *priv)
+/* Read up configuration from a file stream */
+/*! Read up VTY configuration from a file stream
+ * \param[in] confp file pointer of the stream for the configuration file
+ * \param[in] priv private data to be passed to \ref vty_read_file
+ * \returns Zero on success, non-zero on error
+ */
+int vty_read_config_filep(FILE *confp, void *priv)
{
int ret;
struct vty *vty;
@@ -1870,7 +1917,7 @@ int vty_read_config_file(const char *file_name, void *priv)
if (!cfile)
return -ENOENT;
- rc = vty_read_file(cfile, priv);
+ rc = vty_read_config_filep(cfile, priv);
fclose(cfile);
host_config_set(file_name);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0f587cef..8b1731da 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,7 +1,7 @@
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS)
AM_LDFLAGS = -no-install
-LDADD = $(top_builddir)/src/libosmocore.la $(TALLOC_LIBS) $(PTHREAD_LIBS)
+LDADD = $(top_builddir)/src/core/libosmocore.la $(TALLOC_LIBS) $(PTHREAD_LIBS)
if ENABLE_SERCOM_STUB
noinst_LIBRARIES = libsercomstub.a
@@ -9,16 +9,18 @@ LDADD += $(top_builddir)/tests/libsercomstub.a
endif
check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \
- smscb/smscb_test bits/bitrev_test a5/a5_test \
- conv/conv_test auth/milenage_test lapd/lapd_test \
+ bits/bitrev_test a5/a5_test \
+ conv/conv_test auth/milenage_test auth/tuak_test \
+ lapd/lapd_test \
gsm0808/gsm0808_test gsm0408/gsm0408_test \
gprs/gprs_test kasumi/kasumi_test gea/gea_test \
logging/logging_test codec/codec_test \
+ logging/logging_test_gsmtap \
loggingrb/loggingrb_test strrb/strrb_test \
- comp128/comp128_test smscb/gsm0341_test \
+ comp128/comp128_test \
bitvec/bitvec_test msgb/msgb_test bits/bitcomp_test \
bits/bitfield_test \
- tlv/tlv_test gsup/gsup_test oap/oap_test \
+ tlv/tlv_test oap/oap_test \
write_queue/wqueue_test socket/socket_test \
coding/coding_test conv/conv_gsm0503_test \
abis/abis_test endian/endian_test sercomm/sercomm_test \
@@ -28,9 +30,9 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \
oap/oap_client_test gsm29205/gsm29205_test \
logging/logging_vty_test \
vty/vty_transcript_test \
- tdef/tdef_test tdef/tdef_vty_test_config_root \
- tdef/tdef_vty_test_config_subnode \
- tdef/tdef_vty_test_dynamic \
+ tdef/tdef_test tdef/tdef_vty_config_root_test \
+ tdef/tdef_vty_config_subnode_test \
+ tdef/tdef_vty_dynamic_test \
sockaddr_str/sockaddr_str_test \
use_count/use_count_test \
context/context_test \
@@ -42,7 +44,21 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \
bsslap/bsslap_test \
bssmap_le/bssmap_le_test \
it_q/it_q_test \
+ time_cc/time_cc_test \
gsm48/rest_octets_test \
+ base64/base64_test \
+ iuup/iuup_test \
+ smscb/smscb_test \
+ smscb/gsm0341_test \
+ smscb/cbsp_test \
+ auth/xor2g_test \
+ v110/frame_test \
+ v110/ra1_test \
+ v110/ta_test \
+ gsm44021/frame_csd_test \
+ osmo_io/osmo_io_test \
+ soft_uart/soft_uart_test \
+ rlp/rlp_test \
$(NULL)
if ENABLE_MSGFILE
@@ -71,43 +87,60 @@ endif
if !EMBEDDED
check_PROGRAMS += \
+ gsup/gsup_test \
stats/stats_test \
- exec/exec_test
+ stats/stats_vty_test \
+ exec/exec_test \
+ $(NULL)
endif
if ENABLE_GB
check_PROGRAMS += gb/bssgp_fc_test gb/gprs_bssgp_test gb/gprs_bssgp_rim_test gb/gprs_ns_test gb/gprs_ns2_test fr/fr_test
endif
+base64_base64_test_SOURCES = base64/base64_test.c
+
utils_utils_test_SOURCES = utils/utils_test.c
-utils_utils_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+utils_utils_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
stats_stats_test_SOURCES = stats/stats_test.c
-stats_stats_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+stats_stats_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+stats_stats_test_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/core
+
+stats_stats_vty_test_SOURCES = stats/stats_vty_test.c
+stats_stats_vty_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
a5_a5_test_SOURCES = a5/a5_test.c
-a5_a5_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libgsmint.la
+a5_a5_test_LDADD = $(top_builddir)/src/gsm/libgsmint.la $(LDADD)
kasumi_kasumi_test_SOURCES = kasumi/kasumi_test.c
-kasumi_kasumi_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libgsmint.la
+kasumi_kasumi_test_LDADD = $(top_builddir)/src/gsm/libgsmint.la $(LDADD)
comp128_comp128_test_SOURCES = comp128/comp128_test.c
-comp128_comp128_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+comp128_comp128_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
auth_milenage_test_SOURCES = auth/milenage_test.c
-auth_milenage_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+auth_milenage_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+auth_tuak_test_SOURCES = auth/tuak_test.c
+auth_tuak_test_LDADD = $(top_builddir)/src/gsm/libgsmint.la $(LDADD)
+auth_tuak_test_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src
+
+auth_xor2g_test_SOURCES = auth/xor2g_test.c
+auth_xor2g_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
abis_abis_test_SOURCES = abis/abis_test.c
-abis_abis_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+abis_abis_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
ctrl_ctrl_test_SOURCES = ctrl/ctrl_test.c
-ctrl_ctrl_test_LDADD = $(LDADD) \
+ctrl_ctrl_test_LDADD = \
$(top_builddir)/src/ctrl/libosmoctrl.la \
$(top_builddir)/src/gsm/libosmogsm.la \
- $(top_builddir)/src/vty/libosmovty.la
+ $(top_builddir)/src/vty/libosmovty.la \
+ $(LDADD)
gea_gea_test_SOURCES = gea/gea_test.c
-gea_gea_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gea_gea_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
bits_bitrev_test_SOURCES = bits/bitrev_test.c
@@ -118,105 +151,118 @@ bits_bitcomp_test_SOURCES = bits/bitcomp_test.c
bits_bitfield_test_SOURCES = bits/bitfield_test.c
conv_conv_test_SOURCES = conv/conv_test.c conv/conv.c
-conv_conv_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libgsmint.la
+conv_conv_test_LDADD = $(top_builddir)/src/gsm/libgsmint.la $(LDADD)
gsm0502_gsm0502_test_SOURCES = gsm0502/gsm0502_test.c
-gsm0502_gsm0502_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsm0502_gsm0502_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
dtx_dtx_gsm0503_test_SOURCES = dtx/dtx_gsm0503_test.c
-dtx_dtx_gsm0503_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la \
- $(top_builddir)/src/coding/libosmocoding.la
+dtx_dtx_gsm0503_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la \
+ $(top_builddir)/src/coding/libosmocoding.la \
+ $(LDADD)
conv_conv_gsm0503_test_SOURCES = conv/conv_gsm0503_test.c conv/conv.c conv/gsm0503_test_vectors.c
-conv_conv_gsm0503_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libgsmint.la
+conv_conv_gsm0503_test_LDADD = $(top_builddir)/src/gsm/libgsmint.la $(LDADD)
conv_conv_gsm0503_test_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/tests/conv
gsm0808_gsm0808_test_SOURCES = gsm0808/gsm0808_test.c
-gsm0808_gsm0808_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsm0808_gsm0808_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
gsm29205_gsm29205_test_SOURCES = gsm29205/gsm29205_test.c
-gsm29205_gsm29205_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsm29205_gsm29205_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
gsm0408_gsm0408_test_SOURCES = gsm0408/gsm0408_test.c
-gsm0408_gsm0408_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsm0408_gsm0408_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
gsm48_rest_octets_test_SOURCES = gsm48/rest_octets_test.c
-gsm48_rest_octets_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsm48_rest_octets_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
gprs_gprs_test_SOURCES = gprs/gprs_test.c
-gprs_gprs_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gprs_gprs_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
lapd_lapd_test_SOURCES = lapd/lapd_test.c
-lapd_lapd_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+lapd_lapd_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la \
+ $(top_builddir)/src/isdn/libosmoisdn.la \
+ $(LDADD)
msgb_msgb_test_SOURCES = msgb/msgb_test.c
msgfile_msgfile_test_SOURCES = msgfile/msgfile_test.c
smscb_smscb_test_SOURCES = smscb/smscb_test.c
-smscb_smscb_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+smscb_smscb_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
smscb_gsm0341_test_SOURCES = smscb/gsm0341_test.c
-smscb_gsm0341_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+smscb_gsm0341_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+smscb_cbsp_test_SOURCES = smscb/cbsp_test.c
+smscb_cbsp_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
sms_sms_test_SOURCES = sms/sms_test.c
-sms_sms_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+sms_sms_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
timer_timer_test_SOURCES = timer/timer_test.c
timer_clk_override_test_SOURCES = timer/clk_override_test.c
ussd_ussd_test_SOURCES = ussd/ussd_test.c
-ussd_ussd_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+ussd_ussd_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
gb_bssgp_fc_test_SOURCES = gb/bssgp_fc_test.c
-gb_bssgp_fc_test_LDADD = $(LDADD) $(top_builddir)/src/gb/libosmogb.la \
+gb_bssgp_fc_test_LDADD = $(top_builddir)/src/gb/libosmogb.la \
$(top_builddir)/src/vty/libosmovty.la \
- $(top_builddir)/src/gsm/libosmogsm.la
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(LDADD)
gb_gprs_bssgp_test_SOURCES = gb/gprs_bssgp_test.c
-gb_gprs_bssgp_test_LDADD = $(LDADD) $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DLSYM) \
- $(top_builddir)/src/vty/libosmovty.la \
+gb_gprs_bssgp_test_LDADD = $(top_builddir)/src/vty/libosmovty.la \
$(top_builddir)/src/gsm/libosmogsm.la \
- $(top_builddir)/src/gb/libosmogb.la
+ $(top_builddir)/src/gb/libosmogb.la \
+ $(LDADD) \
+ $(LIBRARY_DLSYM)
gb_gprs_bssgp_rim_test_SOURCES = gb/gprs_bssgp_rim_test.c
-gb_gprs_bssgp_rim_test_LDADD = $(LDADD) $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DLSYM) \
- $(top_builddir)/src/gb/libosmogb.la
+gb_gprs_bssgp_rim_test_LDADD = $(top_builddir)/src/gb/libosmogb.la \
+ $(LDADD) \
+ $(LIBRARY_DLSYM)
gb_gprs_ns_test_SOURCES = gb/gprs_ns_test.c
-gb_gprs_ns_test_LDADD = $(LDADD) $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DLSYM) \
+gb_gprs_ns_test_LDADD = $(top_builddir)/src/gb/libosmogb.la \
$(top_builddir)/src/vty/libosmovty.la \
- $(top_builddir)/src/gsm/libosmogsm.la
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(LDADD) \
+ $(LIBRARY_DLSYM)
gb_gprs_ns2_test_SOURCES = gb/gprs_ns2_test.c
-gb_gprs_ns2_test_LDADD = $(LDADD) $(LIBRARY_DLSYM) \
- $(top_builddir)/src/vty/libosmovty.la \
+gb_gprs_ns2_test_LDADD = $(top_builddir)/src/vty/libosmovty.la \
$(top_builddir)/src/gsm/libosmogsm.la \
- $(top_builddir)/src/libosmocore.la \
- $(top_builddir)/src/gb/libosmogb-test.la
-if ENABLE_LIBMNL
-gb_gprs_ns2_test_LDADD += $(LIBMNL_LIBS)
-endif
+ $(top_builddir)/src/core/libosmocore.la \
+ $(top_builddir)/src/gb/libosmogb-test.la \
+ $(LDADD) \
+ $(LIBRARY_DLSYM)
logging_logging_test_SOURCES = logging/logging_test.c
logging_logging_vty_test_SOURCES = logging/logging_vty_test.c
-logging_logging_vty_test_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la
+logging_logging_vty_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
+
+logging_logging_test_gsmtap_SOURCES = logging/logging_test_gsmtap.c
vty_vty_transcript_test_SOURCES = vty/vty_transcript_test.c
-vty_vty_transcript_test_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la
+vty_vty_transcript_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
fr_fr_test_SOURCES = fr/fr_test.c
-fr_fr_test_LDADD = $(LDADD) $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DLSYM) \
+fr_fr_test_LDADD = $(top_builddir)/src/gb/libosmogb.la \
$(top_builddir)/src/vty/libosmovty.la \
- $(top_builddir)/src/gsm/libosmogsm.la
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(LDADD) \
+ $(LIBRARY_DLSYM)
codec_codec_test_SOURCES = codec/codec_test.c
-codec_codec_test_LDADD = $(LDADD) $(top_builddir)/src/codec/libosmocodec.la
+codec_codec_test_LDADD = $(top_builddir)/src/codec/libosmocodec.la $(LDADD)
codec_codec_ecu_fr_test_SOURCES = codec/codec_ecu_fr_test.c
-codec_codec_ecu_fr_test_LDADD = $(LDADD) $(top_builddir)/src/codec/libosmocodec.la
+codec_codec_ecu_fr_test_LDADD = $(top_builddir)/src/codec/libosmocodec.la $(LDADD)
loggingrb_loggingrb_test_SOURCES = loggingrb/loggingrb_test.c
loggingrb_loggingrb_test_LDADD = $(LDADD)
@@ -224,30 +270,31 @@ loggingrb_loggingrb_test_LDADD = $(LDADD)
strrb_strrb_test_SOURCES = strrb/strrb_test.c
vty_vty_test_SOURCES = vty/vty_test.c
-vty_vty_test_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la
+vty_vty_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
sim_sim_test_SOURCES = sim/sim_test.c
-sim_sim_test_LDADD = $(LDADD) $(top_builddir)/src/sim/libosmosim.la \
- $(top_builddir)/src/gsm/libosmogsm.la
+sim_sim_test_LDADD = $(top_builddir)/src/sim/libosmosim.la \
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(LDADD)
tlv_tlv_test_SOURCES = tlv/tlv_test.c
-tlv_tlv_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+tlv_tlv_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
gsup_gsup_test_SOURCES = gsup/gsup_test.c
-gsup_gsup_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsup_gsup_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
oap_oap_test_SOURCES = oap/oap_test.c
-oap_oap_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+oap_oap_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
oap_oap_client_test_SOURCES = oap/oap_client_test.c
-oap_oap_client_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+oap_oap_client_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
fsm_fsm_test_SOURCES = fsm/fsm_test.c
fsm_fsm_test_LDADD = \
- $(LDADD) \
$(top_builddir)/src/ctrl/libosmoctrl.la \
$(top_builddir)/src/gsm/libosmogsm.la \
- $(top_builddir)/src/vty/libosmovty.la
+ $(top_builddir)/src/vty/libosmovty.la \
+ $(LDADD)
fsm_fsm_dealloc_test_SOURCES = fsm/fsm_dealloc_test.c
fsm_fsm_dealloc_test_LDADD = $(LDADD)
@@ -257,10 +304,11 @@ write_queue_wqueue_test_SOURCES = write_queue/wqueue_test.c
socket_socket_test_SOURCES = socket/socket_test.c
coding_coding_test_SOURCES = coding/coding_test.c
-coding_coding_test_LDADD = $(LDADD) \
+coding_coding_test_LDADD = \
$(top_builddir)/src/gsm/libosmogsm.la \
$(top_builddir)/src/codec/libosmocodec.la \
- $(top_builddir)/src/coding/libosmocoding.la
+ $(top_builddir)/src/coding/libosmocoding.la \
+ $(LDADD)
endian_endian_test_SOURCES = endian/endian_test.c
@@ -269,22 +317,22 @@ sercomm_sercomm_test_SOURCES = sercomm/sercomm_test.c
prbs_prbs_test_SOURCES = prbs/prbs_test.c
gsm23003_gsm23003_test_SOURCES = gsm23003/gsm23003_test.c
-gsm23003_gsm23003_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsm23003_gsm23003_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
gsm23236_gsm23236_test_SOURCES = gsm23236/gsm23236_test.c
-gsm23236_gsm23236_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsm23236_gsm23236_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
tdef_tdef_test_SOURCES = tdef/tdef_test.c
tdef_tdef_test_LDADD = $(LDADD)
-tdef_tdef_vty_test_config_root_SOURCES = tdef/tdef_vty_test_config_root.c
-tdef_tdef_vty_test_config_root_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la
+tdef_tdef_vty_config_root_test_SOURCES = tdef/tdef_vty_config_root_test.c
+tdef_tdef_vty_config_root_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
-tdef_tdef_vty_test_config_subnode_SOURCES = tdef/tdef_vty_test_config_subnode.c
-tdef_tdef_vty_test_config_subnode_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la
+tdef_tdef_vty_config_subnode_test_SOURCES = tdef/tdef_vty_config_subnode_test.c
+tdef_tdef_vty_config_subnode_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
-tdef_tdef_vty_test_dynamic_SOURCES = tdef/tdef_vty_test_dynamic.c
-tdef_tdef_vty_test_dynamic_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la
+tdef_tdef_vty_dynamic_test_SOURCES = tdef/tdef_vty_dynamic_test.c
+tdef_tdef_vty_dynamic_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
sockaddr_str_sockaddr_str_test_SOURCES = sockaddr_str/sockaddr_str_test.c
sockaddr_str_sockaddr_str_test_LDADD = $(LDADD)
@@ -299,23 +347,51 @@ exec_exec_test_SOURCES = exec/exec_test.c
exec_exec_test_LDADD = $(LDADD)
i460_mux_i460_mux_test_SOURCES = i460_mux/i460_mux_test.c
-i460_mux_i460_mux_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+i460_mux_i460_mux_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la $(LDADD)
bitgen_bitgen_test_SOURCES = bitgen/bitgen_test.c
bitgen_bitgen_test_LDADD = $(LDADD)
gad_gad_test_SOURCES = gad/gad_test.c
-gad_gad_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gad_gad_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
bsslap_bsslap_test_SOURCES = bsslap/bsslap_test.c
-bsslap_bsslap_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+bsslap_bsslap_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
bssmap_le_bssmap_le_test_SOURCES = bssmap_le/bssmap_le_test.c
-bssmap_le_bssmap_le_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+bssmap_le_bssmap_le_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
it_q_it_q_test_SOURCES = it_q/it_q_test.c
it_q_it_q_test_LDADD = $(LDADD)
+time_cc_time_cc_test_SOURCES = time_cc/time_cc_test.c
+time_cc_time_cc_test_LDADD = $(LDADD)
+
+iuup_iuup_test_SOURCES = iuup/iuup_test.c
+iuup_iuup_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+v110_frame_test_SOURCES = v110/frame_test.c
+v110_frame_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la $(LDADD)
+
+v110_ra1_test_SOURCES = v110/ra1_test.c
+v110_ra1_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la $(LDADD)
+
+v110_ta_test_SOURCES = v110/ta_test.c
+v110_ta_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la $(LDADD)
+
+gsm44021_frame_csd_test_SOURCES = gsm44021/frame_csd_test.c
+gsm44021_frame_csd_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la \
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(LDADD)
+
+osmo_io_osmo_io_test_SOURCES = osmo_io/osmo_io_test.c
+
+soft_uart_soft_uart_test_SOURCES = soft_uart/soft_uart_test.c
+
+rlp_rlp_test_SOURCES = rlp/rlp_test.c
+rlp_rlp_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
:;{ \
@@ -336,19 +412,26 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
timer/timer_test.ok sms/sms_test.ok ussd/ussd_test.ok \
- smscb/smscb_test.ok bits/bitrev_test.ok a5/a5_test.ok \
+ bits/bitrev_test.ok a5/a5_test.ok \
conv/conv_test.ok auth/milenage_test.ok ctrl/ctrl_test.ok \
- lapd/lapd_test.ok gsm0408/gsm0408_test.ok \
+ auth/tuak_test.ok \
+ auth/xor2g_test.ok \
+ lapd/lapd_test.ok \
+ gsm0408/gsm0408_test.ok gsm0408/gsm0408_test.err \
gsm0808/gsm0808_test.ok gb/bssgp_fc_tests.err \
gb/bssgp_fc_tests.ok gb/bssgp_fc_tests.sh \
- gb/gprs_bssgp_test.ok gb/gprs_ns_test.ok gea/gea_test.ok \
+ gb/gprs_bssgp_test.ok gea/gea_test.ok \
gb/gprs_bssgp_rim_test.ok \
gb/gprs_ns2_vty.vty gb/osmoappdesc.py gb/osmo-ns-dummy.cfg \
+ gb/gprs_ns_test.ok \
+ gb/gprs_ns_test.err \
gb/gprs_ns2_test.ok \
+ gb/gprs_ns2_test.err \
gprs/gprs_test.ok kasumi/kasumi_test.ok \
msgfile/msgfile_test.ok msgfile/msgconfig.cfg \
logging/logging_test.ok logging/logging_test.err \
logging/logging_vty_test.vty \
+ logging/logging_test_gsmtap.err \
fr/fr_test.ok loggingrb/logging_test.ok \
loggingrb/logging_test.err strrb/strrb_test.ok \
codec/codec_test.ok \
@@ -370,6 +453,7 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
comp128/comp128_test.ok bits/bitfield_test.ok \
utils/utils_test.ok utils/utils_test.err \
stats/stats_test.ok stats/stats_test.err \
+ stats/stats_vty_test.vty \
bitvec/bitvec_test.ok msgb/msgb_test.ok bits/bitcomp_test.ok \
sim/sim_test.ok tlv/tlv_test.ok abis/abis_test.ok \
gsup/gsup_test.ok gsup/gsup_test.err \
@@ -388,10 +472,11 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
oap/oap_client_test.ok oap/oap_client_test.err \
vty/vty_transcript_test.vty \
tdef/tdef_test.ok \
+ tdef/tdef_test.err \
tdef/tdef_test_range_64bit.ok \
- tdef/tdef_vty_test_config_root.vty \
- tdef/tdef_vty_test_config_subnode.vty \
- tdef/tdef_vty_test_dynamic.vty \
+ tdef/tdef_vty_config_root_test.vty \
+ tdef/tdef_vty_config_subnode_test.vty \
+ tdef/tdef_vty_dynamic_test.vty \
sockaddr_str/sockaddr_str_test.ok \
use_count/use_count_test.ok use_count/use_count_test.err \
context/context_test.ok \
@@ -404,12 +489,26 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
bsslap/bsslap_test.ok \
bssmap_le/bssmap_le_test.ok \
it_q/it_q_test.ok \
+ time_cc/time_cc_test.ok \
gsm48/rest_octets_test.ok \
+ base64/base64_test.ok \
+ iuup/iuup_test.ok \
+ iuup/iuup_test.err \
+ smscb/smscb_test.ok \
+ smscb/gsm0341_test.ok \
+ smscb/cbsp_test.ok \
+ v110/frame_test.ok \
+ v110/ra1_test.ok \
+ v110/ta_test.err \
+ gsm44021/frame_csd_test.ok \
+ osmo_io/osmo_io_test.ok osmo_io/osmo_io_test.err \
+ soft_uart/soft_uart_test.ok \
+ rlp/rlp_test.ok \
+ socket/socket_sctp_test.ok socket/socket_sctp_test.err \
$(NULL)
if ENABLE_LIBSCTP
if ENABLE_SCTP_TESTS
-EXTRA_DIST += socket/socket_sctp_test.ok socket/socket_sctp_test.err
check_PROGRAMS += socket/socket_sctp_test
socket_socket_sctp_test_SOURCES = socket/socket_sctp_test.c
endif
@@ -421,14 +520,229 @@ noinst_HEADERS = conv/conv.h
TESTSUITE = $(srcdir)/testsuite
+update_exp: $(check_PROGRAMS)
+ a5/a5_test \
+ >$(srcdir)/a5/a5_test.ok
+ abis/abis_test \
+ >$(srcdir)/abis/abis_test.ok
+if ENABLE_CTRL
+ ctrl/ctrl_test \
+ >$(srcdir)/ctrl/ctrl_test.ok
+endif
+ kasumi/kasumi_test \
+ >$(srcdir)/kasumi/kasumi_test.ok
+ bits/bitrev_test \
+ >$(srcdir)/bits/bitrev_test.ok
+ bitvec/bitvec_test \
+ >$(srcdir)/bitvec/bitvec_test.ok
+ bits/bitcomp_test \
+ >$(srcdir)/bits/bitcomp_test.ok
+ bits/bitfield_test \
+ >$(srcdir)/bits/bitfield_test.ok
+ conv/conv_test \
+ >$(srcdir)/conv/conv_test.ok
+ conv/conv_gsm0503_test \
+ >$(srcdir)/conv/conv_gsm0503_test.ok
+ coding/coding_test \
+ >$(srcdir)/coding/coding_test.ok
+ msgb/msgb_test \
+ >$(srcdir)/msgb/msgb_test.ok
+ gea/gea_test \
+ >$(srcdir)/gea/gea_test.ok
+if ENABLE_MSGFILE
+ cp $(srcdir)/msgfile/msgconfig.cfg .
+ msgfile/msgfile_test \
+ >$(srcdir)/msgfile/msgfile_test.ok
+endif
+ sms/sms_test \
+ >$(srcdir)/sms/sms_test.ok
+ smscb/smscb_test \
+ >$(srcdir)/smscb/smscb_test.ok
+ smscb/gsm0341_test \
+ >$(srcdir)/smscb/gsm0341_test.ok
+ smscb/cbsp_test \
+ >$(srcdir)/smscb/cbsp_test.ok
+ ussd/ussd_test \
+ >$(srcdir)/ussd/ussd_test.ok
+ auth/milenage_test \
+ >$(srcdir)/auth/milenage_test.ok
+ auth/tuak_test \
+ >$(srcdir)/auth/tuak_test.ok
+ comp128/comp128_test \
+ >$(srcdir)/comp128/comp128_test.ok
+ lapd/lapd_test \
+ >$(srcdir)/lapd/lapd_test.ok
+ gsm0502/gsm0502_test \
+ >$(srcdir)/gsm0502/gsm0502_test.ok
+ dtx/dtx_gsm0503_test \
+ >$(srcdir)/dtx/dtx_gsm0503_test.ok
+ gsm0808/gsm0808_test \
+ >$(srcdir)/gsm0808/gsm0808_test.ok
+ gsm29205/gsm29205_test \
+ >$(srcdir)/gsm29205/gsm29205_test.ok
+ gsm0408/gsm0408_test \
+ 2>$(srcdir)/gsm0408/gsm0408_test.err \
+ 1>$(srcdir)/gsm0408/gsm0408_test.ok
+ gsm48/rest_octets_test \
+ >$(srcdir)/gsm48/rest_octets_test.ok
+ gprs/gprs_test \
+ >$(srcdir)/gprs/gprs_test.ok
+ logging/logging_test \
+ >$(srcdir)/logging/logging_test.ok \
+ 2>$(srcdir)/logging/logging_test.err
+ logging/logging_test_gsmtap \
+ 2>&1 |grep -v "enqueueing message failed" >$(srcdir)/logging/logging_test_gsmtap.err
+ codec/codec_test \
+ >$(srcdir)/codec/codec_test.ok
+ codec/codec_ecu_fr_test \
+ >$(srcdir)/codec/codec_ecu_fr_test.ok
+if ENABLE_GB
+ fr/fr_test \
+ >$(srcdir)/fr/fr_test.ok
+endif
+ loggingrb/loggingrb_test \
+ >$(srcdir)/loggingrb/logging_test.ok \
+ 2>$(srcdir)/loggingrb/logging_test.err
+ strrb/strrb_test \
+ >$(srcdir)/strrb/strrb_test.ok
+if ENABLE_VTY
+ cp $(srcdir)/vty/*.cfg .
+ vty/vty_test \
+ >$(srcdir)/vty/vty_test.ok \
+ 2>$(srcdir)/vty/vty_test.err
+endif
+if ENABLE_GB
+ gb/gprs_bssgp_test \
+ >$(srcdir)/gb/gprs_bssgp_test.ok
+ gb/gprs_bssgp_rim_test \
+ >$(srcdir)/gb/gprs_bssgp_rim_test.ok
+ gb/gprs_ns_test \
+ >$(srcdir)/gb/gprs_ns_test.ok \
+ 2>$(srcdir)/gb/gprs_ns_test.err
+ gb/gprs_ns2_test \
+ >$(srcdir)/gb/gprs_ns2_test.ok \
+ 2>$(srcdir)/gb/gprs_ns2_test.err
+endif
+if ENABLE_UTILITIES
+ utils/utils_test \
+ >$(srcdir)/utils/utils_test.ok
+endif
+if !EMBEDDED
+ stats/stats_test \
+ >$(srcdir)/stats/stats_test.ok \
+ 2>$(srcdir)/stats/stats_test.err
+endif
+ write_queue/wqueue_test \
+ >$(srcdir)/write_queue/wqueue_test.ok
+if ENABLE_GB
+ $(srcdir)/gb/bssgp_fc_tests.sh gb \
+ >$(srcdir)/gb/bssgp_fc_tests.ok \
+ 2>$(srcdir)/gb/bssgp_fc_tests.err
+endif
+if ENABLE_PCSC
+ sim/sim_test \
+ >$(srcdir)/sim/sim_test.ok
+endif
+ timer/timer_test \
+ >$(srcdir)/timer/timer_test.ok
+ timer/clk_override_test \
+ >$(srcdir)/timer/clk_override_test.ok
+ tlv/tlv_test \
+ >$(srcdir)/tlv/tlv_test.ok
+if !EMBEDDED
+ gsup/gsup_test \
+ >$(srcdir)/gsup/gsup_test.ok \
+ 2>$(srcdir)/gsup/gsup_test.err
+endif
+if ENABLE_CTRL
+ fsm/fsm_test \
+ >$(srcdir)/fsm/fsm_test.ok \
+ 2>$(srcdir)/fsm/fsm_test.err
+ fsm/fsm_dealloc_test \
+ 2>$(srcdir)/fsm/fsm_dealloc_test.err
+endif
+ oap/oap_test \
+ >$(srcdir)/oap/oap_test.ok
+ oap/oap_client_test \
+ >$(srcdir)/oap/oap_client_test.ok \
+ 2>$(srcdir)/oap/oap_client_test.err
+ socket/socket_test \
+ >$(srcdir)/socket/socket_test.ok \
+ 2>$(srcdir)/socket/socket_test.err
+ socket/socket_sctp_test \
+ >$(srcdir)/socket/socket_sctp_test.ok \
+ 2>$(srcdir)/socket/socket_sctp_test.err
+ $(srcdir)/osmo-auc-gen/osmo-auc-gen_test.sh ../utils/osmo-auc-gen \
+ >$(srcdir)/osmo-auc-gen/osmo-auc-gen_test.ok \
+ 2>$(srcdir)/osmo-auc-gen/osmo-auc-gen_test.err
+ endian/endian_test \
+ >$(srcdir)/endian/endian_test.ok
+ sercomm/sercomm_test \
+ >$(srcdir)/sercomm/sercomm_test.ok
+ prbs/prbs_test \
+ >$(srcdir)/prbs/prbs_test.ok
+ gsm23003/gsm23003_test \
+ >$(srcdir)/gsm23003/gsm23003_test.ok
+ gsm23236/gsm23236_test \
+ >$(srcdir)/gsm23236/gsm23236_test.ok
+ tdef/tdef_test \
+ >$(srcdir)/tdef/tdef_test.ok \
+ 2>$(srcdir)/tdef/tdef_test.err
+ sockaddr_str/sockaddr_str_test \
+ >$(srcdir)/sockaddr_str/sockaddr_str_test.ok
+ use_count/use_count_test \
+ >$(srcdir)/use_count/use_count_test.ok \
+ 2>$(srcdir)/use_count/use_count_test.err
+ context/context_test \
+ >$(srcdir)/context/context_test.ok
+if !EMBEDDED
+ exec/exec_test \
+ >$(srcdir)/exec/exec_test.ok \
+ 2>$(srcdir)/exec/exec_test.err
+endif
+ i460_mux/i460_mux_test \
+ >$(srcdir)/i460_mux/i460_mux_test.ok
+ bitgen/bitgen_test \
+ >$(srcdir)/bitgen/bitgen_test.ok
+ gad/gad_test \
+ >$(srcdir)/gad/gad_test.ok
+ bsslap/bsslap_test \
+ >$(srcdir)/bsslap/bsslap_test.ok
+ bssmap_le/bssmap_le_test \
+ >$(srcdir)/bssmap_le/bssmap_le_test.ok
+ it_q/it_q_test \
+ >$(srcdir)/it_q/it_q_test.ok
+ time_cc/time_cc_test \
+ >$(srcdir)/time_cc/time_cc_test.ok
+ iuup/iuup_test \
+ >$(srcdir)/iuup/iuup_test.ok \
+ 2>$(srcdir)/iuup/iuup_test.err
+ v110/frame_test \
+ >$(srcdir)/v110/frame_test.ok
+ v110/ra1_test \
+ >$(srcdir)/v110/ra1_test.ok
+ v110/ta_test \
+ 2>$(srcdir)/v110/ta_test.err
+ gsm44021/frame_csd_test \
+ >$(srcdir)/gsm44021/frame_csd_test.ok
+ osmo_io/osmo_io_test \
+ >$(srcdir)/osmo_io/osmo_io_test.ok \
+ 2>$(srcdir)/osmo_io/osmo_io_test.err
+ soft_uart/soft_uart_test \
+ >$(srcdir)/soft_uart/soft_uart.ok
+ rlp/rlp_test \
+ >$(srcdir)/rlp/rlp_test.ok \
+ $(NULL)
+
+
check-local: atconfig $(TESTSUITE)
[ -e /proc/cpuinfo ] && cat /proc/cpuinfo
- $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
+ $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) ENABLE_URING=$(ENABLE_URING) ENABLE_URING_TESTS=$(ENABLE_URING_TESTS)
$(MAKE) $(AM_MAKEFLAGS) ext-tests
installcheck-local: atconfig $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \
- $(TESTSUITEFLAGS)
+ $(TESTSUITEFLAGS) ENABLE_URING=$(ENABLE_URING) ENABLE_URING_TESTS=$(ENABLE_URING_TESTS)
clean-local:
test ! -f '$(TESTSUITE)' || \
@@ -458,7 +772,8 @@ endif
# pass -u to osmo_verify_transcript_vty.py by doing:
# make vty-test U=-u
-vty-test-ns2:
+if ENABLE_GB
+vty-test-ns2: $(top_builddir)/utils/osmo-ns-dummy
$(MAKE) -C $(top_builddir)/utils osmo-ns-dummy
osmo_verify_transcript_vty.py -v \
-p 42042 \
@@ -466,32 +781,44 @@ vty-test-ns2:
$(U) $(srcdir)/gb/gprs_ns2*.vty
osmotestvty.py -p $(abs_top_srcdir)/tests/gb -w $(abs_top_builddir)/tests/gb -v
osmotestconfig.py -p $(abs_top_srcdir)/tests/gb -w $(abs_top_builddir)/tests/gb -v
+else
+vty-test-ns2:
+ echo "Not running vty-test-ns2 because osmo-ns-dummy is not built (--disable-gb)"
+endif
-vty-test-logging:
+vty-test-logging: $(top_builddir)/tests/logging/logging_vty_test
osmo_verify_transcript_vty.py -v \
-p 42042 \
-r "$(top_builddir)/tests/logging/logging_vty_test" \
$(U) $(srcdir)/logging/*.vty
-vty-test-vty:
+vty-test-vty: $(top_builddir)/tests/vty/vty_transcript_test
osmo_verify_transcript_vty.py -v \
-p 42042 \
-r "$(top_builddir)/tests/vty/vty_transcript_test" \
$(U) $(srcdir)/vty/*.vty
-vty-test-tdef:
+vty-test-tdef: $(top_builddir)/tests/tdef/tdef_vty_config_root_test \
+ $(top_builddir)/tests/tdef/tdef_vty_config_subnode_test \
+ $(top_builddir)/tests/tdef/tdef_vty_dynamic_test
osmo_verify_transcript_vty.py -v \
-p 42042 \
- -r "$(top_builddir)/tests/tdef/tdef_vty_test_config_root" \
- $(U) $(srcdir)/tdef/tdef_vty_test_config_root.vty
+ -r "$(top_builddir)/tests/tdef/tdef_vty_config_root_test" \
+ $(U) $(srcdir)/tdef/tdef_vty_config_root_test.vty
osmo_verify_transcript_vty.py -v \
-p 42042 \
- -r "$(top_builddir)/tests/tdef/tdef_vty_test_config_subnode" \
- $(U) $(srcdir)/tdef/tdef_vty_test_config_subnode.vty
+ -r "$(top_builddir)/tests/tdef/tdef_vty_config_subnode_test" \
+ $(U) $(srcdir)/tdef/tdef_vty_config_subnode_test.vty
+ osmo_verify_transcript_vty.py -v \
+ -p 42042 \
+ -r "$(top_builddir)/tests/tdef/tdef_vty_dynamic_test" \
+ $(U) $(srcdir)/tdef/tdef_vty_dynamic_test.vty
+
+vty-test-stats: $(top_builddir)/tests/stats/stats_vty_test
osmo_verify_transcript_vty.py -v \
-p 42042 \
- -r "$(top_builddir)/tests/tdef/tdef_vty_test_dynamic" \
- $(U) $(srcdir)/tdef/tdef_vty_test_dynamic.vty
+ -r "$(top_builddir)/tests/stats/stats_vty_test" \
+ $(U) $(srcdir)/stats/*.vty
# don't run vty tests concurrently so that the ports don't conflict
vty-test:
@@ -499,6 +826,7 @@ vty-test:
$(MAKE) vty-test-vty
$(MAKE) vty-test-tdef
$(MAKE) vty-test-ns2
+ $(MAKE) vty-test-stats
ctrl-test:
echo "No CTRL tests exist currently"
diff --git a/tests/a5/a5_test.c b/tests/a5/a5_test.c
index 69f10355..74586a68 100644
--- a/tests/a5/a5_test.c
+++ b/tests/a5/a5_test.c
@@ -66,7 +66,7 @@ static inline bool test_a53(const char * kc, uint32_t count, const char * block1
_a5_3(key, count, dlout, NULL, false);
_a5_3(key, count, NULL, ulout, false);
- return print_a5(3, 8, "DL", dlout, block1) & print_a5(3, 8, "UL", ulout, block2);
+ return print_a5(3, 8, "DL", dlout, block1) && print_a5(3, 8, "UL", ulout, block2);
}
static inline bool test_a54(const char * kc, uint32_t count, const char * block1, const char * block2)
@@ -78,7 +78,7 @@ static inline bool test_a54(const char * kc, uint32_t count, const char * block1
_a5_4(key, count, dlout, NULL, false);
_a5_4(key, count, NULL, ulout, false);
- return print_a5(4, 8, "DL", dlout, block1) & print_a5(4, 8, "UL", ulout, block2);
+ return print_a5(4, 8, "DL", dlout, block1) && print_a5(4, 8, "UL", ulout, block2);
}
diff --git a/tests/abis/abis_test.c b/tests/abis/abis_test.c
index ca6daed8..ac470fc7 100644
--- a/tests/abis/abis_test.c
+++ b/tests/abis/abis_test.c
@@ -1,6 +1,6 @@
/*
* (C) 2012 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2017 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/application.h>
@@ -171,7 +167,7 @@ static inline void chk_descr(struct msgb *msg, const char *f_id, const char *f_v
}
}
-static void test_sw_descr()
+static void test_sw_descr(void)
{
const char *f_id = "TEST.L0L", *f_ver = "0.1.666~deadbeeffacefeed-dirty";
uint8_t chain[] = { 0x42, 0x12, 0x00, 0x03, 0x01, 0x02, 0x03, 0x13, 0x00, 0x03, 0x03, 0x04, 0x05, 0x42, 0x12,
@@ -197,6 +193,52 @@ static void test_sw_descr()
msgb_free(msg);
}
+/* Test decode IPAC_DLCX_IND obtained from SYS#5915 */
+static void test_dec_ipac_dlc_indx(void)
+{
+/* Radio Signalling Link (RSL)
+ 0111 111. = Message discriminator: ip.access Vendor Specific messages (63)
+ .... ...0 = T bit: Not considered transparent by BTS
+ .111 0110 = Message type: ip.access DLCX INDication (0x76)
+ Channel number IE
+ Element identifier: Channel Number (0x01)
+ 0000 1... = C-bits: Bm + ACCH (1)
+ .... .110 = Time slot number (TN): 6
+ Element identifier: Connection Identifier (0xf8)
+ ip.access Connection ID: 0
+ Element identifier: Connection Statistics (0xf6)
+ [1 byte length here, val = 28 (0x1c)]
+ Packets Sent: 1202
+ Octets Sent: 45052
+ Packets Received: 556
+ Octets Received: 24580
+ Packets Lost: 0
+ Inter-arrival Jitter: 0
+ Average Tx Delay: 0
+ Cause IE
+ Element identifier: Cause (0x1a)
+ Length: 1
+ 0... .... = Extension: No Extension
+ .000 .... = Class: Normal event (0)
+ .000 1111 = Cause Value: normal event, unspecified (15)
+*/
+ const uint8_t hex[] = {
+ 0x7e, 0x76, 0x01, 0x0e, 0xf8, 0x00, 0x00, 0xf6, 0x1c, 0x00, 0x00, 0x04, 0xb2, 0x00, 0x00, 0xaf,
+ 0xfc, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x01, 0x0f
+ };
+ struct abis_rsl_dchan_hdr *dh = (struct abis_rsl_dchan_hdr *)&hex[0];
+
+ struct tlv_parsed tp;
+ int rc;
+
+ printf("Testing decoding IPAC_DLCX_IND\n");
+
+ rc = rsl_tlv_parse(&tp, dh->data, sizeof(hex) - sizeof(*dh));
+
+ OSMO_ASSERT(rc == 3);
+}
+
int main(int argc, char **argv)
{
void *ctx = talloc_named_const(NULL, 0, "abis_test");
@@ -206,6 +248,7 @@ int main(int argc, char **argv)
test_simple_sw_config();
test_simple_sw_short();
test_dual_sw_config();
+ test_dec_ipac_dlc_indx();
printf("OK.\n");
diff --git a/tests/abis/abis_test.ok b/tests/abis/abis_test.ok
index e6b626be..2ee647bb 100644
--- a/tests/abis/abis_test.ok
+++ b/tests/abis/abis_test.ok
@@ -38,4 +38,5 @@ len: 13
file_id: 09 07 05
file_ver: 06 07 08
test_dual_sw_config(): OK
+Testing decoding IPAC_DLCX_IND
OK.
diff --git a/tests/auth/tuak_test.c b/tests/auth/tuak_test.c
new file mode 100644
index 00000000..a00ab2ce
--- /dev/null
+++ b/tests/auth/tuak_test.c
@@ -0,0 +1,309 @@
+
+#include <stdint.h>
+#include <osmocom/core/utils.h>
+#include "gsm/tuak/tuak.h"
+
+/* user-friendly test specification, uses hex-strings for all parameters for
+ * copy+pasting from the spec. */
+struct tuak_testspec {
+ const char *name;
+ struct {
+ const char *k;
+ const char *rand;
+ const char *sqn;
+ const char *amf;
+ const char *top;
+ unsigned int keccak_iterations;
+ } in;
+ struct {
+ const char *topc;
+ const char *f1;
+ const char *f1star;
+ const char *f2;
+ const char *f3;
+ const char *f4;
+ const char *f5;
+ const char *f5star;
+ } out;
+};
+
+static const struct tuak_testspec testspecs[] = {
+ {
+ .name = "TS 35.233 Section 6.3 Test Set 1",
+ .in = {
+ .k = "abababababababababababababababab",
+ .rand = "42424242424242424242424242424242",
+ .sqn = "111111111111",
+ .amf = "ffff",
+ .top = "5555555555555555555555555555555555555555555555555555555555555555",
+ .keccak_iterations = 1,
+ },
+ .out = {
+ .topc = "bd04d9530e87513c5d837ac2ad954623a8e2330c115305a73eb45d1f40cccbff",
+ .f1 = "f9a54e6aeaa8618d",
+ .f1star = "e94b4dc6c7297df3",
+ .f2 = "657acd64",
+ .f3 = "d71a1e5c6caffe986a26f783e5c78be1",
+ .f4 = "be849fa2564f869aecee6f62d4337e72",
+ .f5 = "719f1e9b9054",
+ .f5star = "e7af6b3d0e38",
+ },
+ }, {
+ .name = "TS 35.233 Section 6.4 Test Set 2",
+ .in = {
+ .k = "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0",
+ .rand = "0123456789abcdef0123456789abcdef",
+ .sqn = "0123456789ab",
+ .amf = "abcd",
+ .top = "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
+ .keccak_iterations = 1,
+ },
+ .out = {
+ .topc = "305425427e18c503c8a4b294ea72c95d0c36c6c6b29d0c65de5974d5977f8524",
+ .f1 = "c0b8c2d4148ec7aa5f1d78a97e4d1d58",
+ .f1star = "ef81af7290f7842c6ceafa537fa0745b",
+ .f2 = "e9d749dc4eea0035",
+ .f3 = "a4cb6f6529ab17f8337f27baa8234d47",
+ .f4 = "2274155ccf4199d5e2abcbf621907f90",
+ .f5 = "480a9345cc1e",
+ .f5star = "f84eb338848c",
+ },
+ }, {
+ .name = "TS 35.233 Section 6.5 Test Set 3",
+ .in = {
+ .k = "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0",
+ .rand = "0123456789abcdef0123456789abcdef",
+ .sqn = "0123456789ab",
+ .amf = "abcd",
+ .top = "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
+ .keccak_iterations = 1,
+ },
+ .out = {
+ .topc = "305425427e18c503c8a4b294ea72c95d0c36c6c6b29d0c65de5974d5977f8524",
+ .f1 = "d97b75a1776065271b1e212bc3b1bf173f438b21e6c64a55a96c372e085e5cc5",
+ .f1star = "427bbf07c6e3a86c54f8c5216499f3909a6fd4a164c9fe235b1550258111b821",
+ .f2 = "07021c73e7635c7d",
+ .f3 = "4d59ac796834eb85d11fa148a5058c3c",
+ .f4 = "126d47500136fdc5ddfd14f19ebf16749ce4b6435323fbb5715a3a796a6082bd",
+ .f5 = "1d6622c4e59a",
+ .f5star = "f84eb338848c",
+ },
+ }, {
+ .name = "TS 35.233 Section 6.6 Test Set 4",
+ .in = {
+ .k = "b8da837a50652d6ac7c97da14f6acc61",
+ .rand = "6887e55425a966bd86c9661a5fa72be8",
+ .sqn = "0dea2ee2c5af",
+ .amf = "df1e",
+ .top = "0952be13556c32ebc58195d9dd930493e12a9003669988ffde5fa1f0fe35cc01",
+ .keccak_iterations = 1,
+ },
+ .out = {
+ .topc = "2bc16eb657a68e1f446f08f57c0efb1d493527a2e652ce281eb6ca0e4487760a",
+ .f1 = "749214087958dd8f58bfcdf869d8ae3f",
+ .f1star = "619e865afe80e382aee13063f9dfb56d",
+ .f2 = "4041ce438e3e38e8aa96562eed83ac43",
+ .f3 = "3e3bc01bea0cd914c4c2c83ce2d92757",
+ .f4 = "666a8e6f577b1aa77b7fd53cebb8a3d6",
+ .f5 = "1f880d005119",
+ .f5star = "45e617d77fe5",
+ },
+ }, {
+ .name = "TS 35.233 Section 6.7 Test Set 5",
+ .in = {
+ .k = "1574ca56881d05c189c82880f789c9cd4244955f4426aa2b69c29f15770e5aa5",
+ .rand = "c570aac68cde651fb1e3088322498bef",
+ .sqn = "c89bb71f3a41",
+ .amf = "297d",
+ .top = "e59f6eb10ea406813f4991b0b9e02f181edf4c7e17b480f66d34da35ee88c95e",
+ .keccak_iterations = 1,
+ },
+ .out = {
+ .topc = "3c6052e41532a28a47aa3cbb89f223e8f3aaa976aecd48bc3e7d6165a55eff62",
+ .f1 = "d7340dad02b4cb01",
+ .f1star = "c6021e2e66accb15",
+ .f2 = "84d89b41db1867ffd4c7ba1d82163f4d526a20fbae5418fbb526940b1eeb905c",
+ .f3 = "d419676afe5ab58c1d8bee0d43523a4d2f52ef0b31a4676a0c334427a988fe65",
+ .f4 = "205533e505661b61d05cc0eac87818f4",
+ .f5 = "d7b3d2d4980a",
+ .f5star = "ca9655264986",
+ },
+ }, {
+ .name = "TS 35.233 Section 6.8 Test Set 6",
+ .in = {
+ .k = "1574ca56881d05c189c82880f789c9cd4244955f4426aa2b69c29f15770e5aa5",
+ .rand = "c570aac68cde651fb1e3088322498bef",
+ .sqn = "c89bb71f3a41",
+ .amf = "297d",
+ .top = "e59f6eb10ea406813f4991b0b9e02f181edf4c7e17b480f66d34da35ee88c95e",
+ .keccak_iterations = 2,
+ },
+ .out = {
+ .topc = "b04a66f26c62fcd6c82de22a179ab65506ecf47f56245cd149966cfa9cec7a51",
+ .f1 = "90d2289ed1ca1c3dbc2247bb480d431ac71d2e4a7677f6e997cfddb0cbad88b7",
+ .f1star = "427355dbac30e825063aba61b556e87583abac638e3ab01c4c884ad9d458dc2f",
+ .f2 = "d67e6e64590d22eecba7324afa4af4460c93f01b24506d6e12047d789a94c867",
+ .f3 = "ede57edfc57cdffe1aae75066a1b7479bbc3837438e88d37a801cccc9f972b89",
+ .f4 = "48ed9299126e5057402fe01f9201cf25249f9c5c0ed2afcf084755daff1d3999",
+ .f5 = "6aae8d18c448",
+ .f5star = "8c5f33b61f4e",
+ },
+ },
+};
+
+
+struct tuak_testset {
+ const char *name;
+ struct {
+ uint8_t k[32];
+ uint8_t k_len_bytes;
+ uint8_t rand[16];
+ uint8_t sqn[6];
+ uint8_t amf[2];
+ uint8_t top[32];
+ unsigned int keccak_iterations;
+ } in;
+ struct {
+ uint8_t topc[32];
+ uint8_t mac_a[32];
+ uint8_t mac_s[32];
+ uint8_t mac_len_bytes;
+
+ uint8_t res[32];
+ uint8_t res_len_bytes;
+
+ uint8_t ck[32];
+ uint8_t ck_len_bytes;
+ uint8_t ik[32];
+ uint8_t ik_len_bytes;
+ uint8_t ak[6];
+ uint8_t f5star[6];
+ } out;
+};
+
+static void expect_equal(const char *name, const uint8_t *actual, const uint8_t *expected, size_t len)
+{
+ if (!memcmp(actual, expected, len)) {
+ printf("\t%s: %s\r\n", name, osmo_hexdump_nospc(actual, len));
+ } else {
+ char buf[len*2+1];
+ printf("\t%s: %s != %s\r\n", name, osmo_hexdump_nospc(actual, len),
+ osmo_hexdump_buf(buf, sizeof(buf), expected, len, "", true));
+ }
+}
+
+static void execute_testset(const struct tuak_testset *tset)
+{
+ uint8_t topc[32];
+
+ printf("==> %s\n", tset->name);
+
+ tuak_set_keccak_iterations(tset->in.keccak_iterations);
+ tuak_opc_gen(topc, tset->in.k, tset->in.k_len_bytes, tset->in.top);
+ expect_equal("TOPc", topc, tset->out.topc, sizeof(topc));
+
+ if (tset->out.mac_len_bytes) {
+ uint8_t mac_a[32];
+ uint8_t mac_s[32];
+
+ tuak_f1(topc, tset->in.k, tset->in.k_len_bytes, tset->in.rand, tset->in.sqn, tset->in.amf,
+ mac_a, tset->out.mac_len_bytes, tset->in.keccak_iterations);
+ expect_equal("MAC_A", mac_a, tset->out.mac_a, tset->out.mac_len_bytes);
+
+ tuak_f1star(topc, tset->in.k, tset->in.k_len_bytes, tset->in.rand, tset->in.sqn, tset->in.amf,
+ mac_s, tset->out.mac_len_bytes, tset->in.keccak_iterations);
+ expect_equal("MAC_S", mac_s, tset->out.mac_s, tset->out.mac_len_bytes);
+ }
+
+ if (tset->out.ck_len_bytes || tset->out.ik_len_bytes || tset->out.res_len_bytes) {
+ uint8_t res[32];
+ uint8_t ck[32];
+ uint8_t ik[32];
+ uint8_t ak[6];
+
+ tuak_f2345(topc, tset->in.k, tset->in.k_len_bytes, tset->in.rand,
+ tset->out.res_len_bytes ? res : NULL, tset->out.res_len_bytes,
+ tset->out.ck_len_bytes ? ck : NULL, tset->out.ck_len_bytes,
+ tset->out.ik_len_bytes ? ik : NULL, tset->out.ik_len_bytes,
+ ak, tset->in.keccak_iterations);
+
+ if (tset->out.res_len_bytes)
+ expect_equal("RES", res, tset->out.res, tset->out.res_len_bytes);
+
+ if (tset->out.ck_len_bytes)
+ expect_equal("CK", ck, tset->out.ck, tset->out.ck_len_bytes);
+
+ if (tset->out.ik_len_bytes)
+ expect_equal("IK", ik, tset->out.ik, tset->out.ik_len_bytes);
+
+ expect_equal("AK", ak, tset->out.ak, 6);
+ }
+}
+
+/* convert string-testspec to binary-testset and execute it */
+static void execute_testspec(const struct tuak_testspec *tcase)
+{
+ struct tuak_testset _tset, *tset = &_tset;
+
+ tset->name = tcase->name;
+ tset->in.keccak_iterations = tcase->in.keccak_iterations;
+
+ osmo_hexparse(tcase->in.k, tset->in.k, sizeof(tset->in.k));
+ tset->in.k_len_bytes = strlen(tcase->in.k)/2;
+ OSMO_ASSERT(tset->in.k_len_bytes == 16 || tset->in.k_len_bytes == 32);
+
+ osmo_hexparse(tcase->in.rand, tset->in.rand, sizeof(tset->in.rand));
+ OSMO_ASSERT(strlen(tcase->in.rand)/2 == 16);
+
+ osmo_hexparse(tcase->in.sqn, tset->in.sqn, sizeof(tset->in.sqn));
+ OSMO_ASSERT(strlen(tcase->in.sqn)/2 == 6);
+
+ osmo_hexparse(tcase->in.amf, tset->in.amf, sizeof(tset->in.amf));
+ OSMO_ASSERT(strlen(tcase->in.amf)/2 == 2);
+
+ osmo_hexparse(tcase->in.top, tset->in.top, sizeof(tset->in.top));
+ OSMO_ASSERT(strlen(tcase->in.top)/2 == 32);
+
+ osmo_hexparse(tcase->out.topc, tset->out.topc, sizeof(tset->out.topc));
+ OSMO_ASSERT(strlen(tcase->out.topc)/2 == 32);
+
+ osmo_hexparse(tcase->out.f1, tset->out.mac_a, sizeof(tset->out.mac_a));
+ osmo_hexparse(tcase->out.f1star, tset->out.mac_s, sizeof(tset->out.mac_s));
+ OSMO_ASSERT(strlen(tcase->out.f1) == strlen(tcase->out.f1star));
+ tset->out.mac_len_bytes = strlen(tcase->out.f1)/2;
+ OSMO_ASSERT(tset->out.mac_len_bytes == 8 || tset->out.mac_len_bytes == 16 ||
+ tset->out.mac_len_bytes == 32);
+
+ osmo_hexparse(tcase->out.f2, tset->out.res, sizeof(tset->out.res));
+ tset->out.res_len_bytes = strlen(tcase->out.f2)/2;
+ OSMO_ASSERT(tset->out.res_len_bytes == 4 || tset->out.res_len_bytes == 8 ||
+ tset->out.res_len_bytes == 16 || tset->out.res_len_bytes == 32);
+
+ osmo_hexparse(tcase->out.f3, tset->out.ck, sizeof(tset->out.ck));
+ tset->out.ck_len_bytes = strlen(tcase->out.f3)/2;
+ OSMO_ASSERT(tset->out.ck_len_bytes == 16 || tset->out.ck_len_bytes == 32);
+
+ osmo_hexparse(tcase->out.f4, tset->out.ik, sizeof(tset->out.ik));
+ tset->out.ik_len_bytes = strlen(tcase->out.f4)/2;
+ OSMO_ASSERT(tset->out.ik_len_bytes == 16 || tset->out.ik_len_bytes == 32);
+
+ osmo_hexparse(tcase->out.f5, tset->out.ak, sizeof(tset->out.ak));
+ OSMO_ASSERT(strlen(tcase->out.f5)/2 == 6);
+
+ osmo_hexparse(tcase->out.f5star, tset->out.f5star, sizeof(tset->out.f5star));
+ OSMO_ASSERT(strlen(tcase->out.f5star)/2 == 6);
+
+ execute_testset(tset);
+}
+
+int main(int argc, char **argv)
+{
+#if 0
+ for (unsigned int i = 0; i < ARRAY_SIZE(testsets); i++)
+ execute_testset(&testsets[i]);
+#endif
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(testspecs); i++)
+ execute_testspec(&testspecs[i]);
+
+}
diff --git a/tests/auth/tuak_test.ok b/tests/auth/tuak_test.ok
new file mode 100644
index 00000000..976fb59e
--- /dev/null
+++ b/tests/auth/tuak_test.ok
@@ -0,0 +1,48 @@
+==> TS 35.233 Section 6.3 Test Set 1
+ TOPc: bd04d9530e87513c5d837ac2ad954623a8e2330c115305a73eb45d1f40cccbff
+ MAC_A: f9a54e6aeaa8618d
+ MAC_S: e94b4dc6c7297df3
+ RES: 657acd64
+ CK: d71a1e5c6caffe986a26f783e5c78be1
+ IK: be849fa2564f869aecee6f62d4337e72
+ AK: 719f1e9b9054
+==> TS 35.233 Section 6.4 Test Set 2
+ TOPc: 305425427e18c503c8a4b294ea72c95d0c36c6c6b29d0c65de5974d5977f8524
+ MAC_A: c0b8c2d4148ec7aa5f1d78a97e4d1d58
+ MAC_S: ef81af7290f7842c6ceafa537fa0745b
+ RES: e9d749dc4eea0035
+ CK: a4cb6f6529ab17f8337f27baa8234d47
+ IK: 2274155ccf4199d5e2abcbf621907f90
+ AK: 480a9345cc1e
+==> TS 35.233 Section 6.5 Test Set 3
+ TOPc: 305425427e18c503c8a4b294ea72c95d0c36c6c6b29d0c65de5974d5977f8524
+ MAC_A: d97b75a1776065271b1e212bc3b1bf173f438b21e6c64a55a96c372e085e5cc5
+ MAC_S: 427bbf07c6e3a86c54f8c5216499f3909a6fd4a164c9fe235b1550258111b821
+ RES: 07021c73e7635c7d
+ CK: 4d59ac796834eb85d11fa148a5058c3c
+ IK: 126d47500136fdc5ddfd14f19ebf16749ce4b6435323fbb5715a3a796a6082bd
+ AK: 1d6622c4e59a
+==> TS 35.233 Section 6.6 Test Set 4
+ TOPc: 2bc16eb657a68e1f446f08f57c0efb1d493527a2e652ce281eb6ca0e4487760a
+ MAC_A: 749214087958dd8f58bfcdf869d8ae3f
+ MAC_S: 619e865afe80e382aee13063f9dfb56d
+ RES: 4041ce438e3e38e8aa96562eed83ac43
+ CK: 3e3bc01bea0cd914c4c2c83ce2d92757
+ IK: 666a8e6f577b1aa77b7fd53cebb8a3d6
+ AK: 1f880d005119
+==> TS 35.233 Section 6.7 Test Set 5
+ TOPc: 3c6052e41532a28a47aa3cbb89f223e8f3aaa976aecd48bc3e7d6165a55eff62
+ MAC_A: d7340dad02b4cb01
+ MAC_S: c6021e2e66accb15
+ RES: 84d89b41db1867ffd4c7ba1d82163f4d526a20fbae5418fbb526940b1eeb905c
+ CK: d419676afe5ab58c1d8bee0d43523a4d2f52ef0b31a4676a0c334427a988fe65
+ IK: 205533e505661b61d05cc0eac87818f4
+ AK: d7b3d2d4980a
+==> TS 35.233 Section 6.8 Test Set 6
+ TOPc: b04a66f26c62fcd6c82de22a179ab65506ecf47f56245cd149966cfa9cec7a51
+ MAC_A: 90d2289ed1ca1c3dbc2247bb480d431ac71d2e4a7677f6e997cfddb0cbad88b7
+ MAC_S: 427355dbac30e825063aba61b556e87583abac638e3ab01c4c884ad9d458dc2f
+ RES: d67e6e64590d22eecba7324afa4af4460c93f01b24506d6e12047d789a94c867
+ CK: ede57edfc57cdffe1aae75066a1b7479bbc3837438e88d37a801cccc9f972b89
+ IK: 48ed9299126e5057402fe01f9201cf25249f9c5c0ed2afcf084755daff1d3999
+ AK: 6aae8d18c448
diff --git a/tests/auth/xor2g_test.c b/tests/auth/xor2g_test.c
new file mode 100644
index 00000000..82ab25ac
--- /dev/null
+++ b/tests/auth/xor2g_test.c
@@ -0,0 +1,77 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <osmocom/crypt/auth.h>
+#include <osmocom/core/utils.h>
+
+static void dump_auth_vec(struct osmo_auth_vector *vec)
+{
+ printf("RAND:\t%s\n", osmo_hexdump(vec->rand, sizeof(vec->rand)));
+
+ if (vec->auth_types & OSMO_AUTH_TYPE_UMTS) {
+ printf("AUTN:\t%s\n", osmo_hexdump(vec->autn, sizeof(vec->autn)));
+ printf("IK:\t%s\n", osmo_hexdump(vec->ik, sizeof(vec->ik)));
+ printf("CK:\t%s\n", osmo_hexdump(vec->ck, sizeof(vec->ck)));
+ printf("RES:\t%s\n", osmo_hexdump(vec->res, vec->res_len));
+ }
+
+ if (vec->auth_types & OSMO_AUTH_TYPE_GSM) {
+ printf("SRES:\t%s\n", osmo_hexdump(vec->sres, sizeof(vec->sres)));
+ /* According to 3GPP TS 55.205 Sec. 4 the GSM-MILENAGE output is limited to 64 bits.
+ According to 3GPP TS 33.102 Annex. B5 in UMTS security context Kc can be 128 bits.
+ Here we test the former, so make sure we only print interesting Kc bits. */
+ printf("Kc:\t%s\n", osmo_hexdump(vec->kc, OSMO_A5_MAX_KEY_LEN_BYTES/2));
+ }
+}
+
+static struct osmo_sub_auth_data test_aud = {
+ .type = OSMO_AUTH_TYPE_GSM,
+ .algo = OSMO_AUTH_ALG_XOR_2G,
+ .u.gsm = {
+ .ki = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ },
+};
+
+int main(int argc, char **argv)
+{
+ struct osmo_auth_vector _vec;
+ struct osmo_auth_vector *vec = &_vec;
+ uint8_t _rand[16];
+ int rc;
+
+#if 0
+ srand(time(NULL));
+ *(uint32_t *)&_rand[0] = rand();
+ *(uint32_t *)(&_rand[4]) = rand();
+ *(uint32_t *)(&_rand[8]) = rand();
+ *(uint32_t *)(&_rand[12]) = rand();
+#else
+ memset(_rand, 0, sizeof(_rand));
+#endif
+ memset(vec, 0, sizeof(*vec));
+
+ rc = osmo_auth_gen_vec(vec, &test_aud, _rand);
+ if (rc < 0) {
+ fprintf(stderr, "error generating auth vector\n");
+ exit(1);
+ }
+ dump_auth_vec(vec);
+
+ /* test once more with non-zero RAND to see it show in result */
+ for (int i = 0; i < sizeof(_rand); i++)
+ _rand[i] = i << 4;
+
+ rc = osmo_auth_gen_vec(vec, &test_aud, _rand);
+ if (rc < 0) {
+ fprintf(stderr, "error generating auth vector\n");
+ exit(1);
+ }
+ dump_auth_vec(vec);
+
+ exit(0);
+}
diff --git a/tests/auth/xor2g_test.ok b/tests/auth/xor2g_test.ok
new file mode 100644
index 00000000..58becf65
--- /dev/null
+++ b/tests/auth/xor2g_test.ok
@@ -0,0 +1,6 @@
+RAND: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+SRES: 00 01 02 03
+Kc: 04 05 06 07 08 09 0a 0b
+RAND: 00 10 20 30 40 50 60 70 80 90 a0 b0 c0 d0 e0 f0
+SRES: 00 11 22 33
+Kc: 44 55 66 77 88 99 aa bb
diff --git a/tests/base64/base64_test.c b/tests/base64/base64_test.c
new file mode 100644
index 00000000..79ec212a
--- /dev/null
+++ b/tests/base64/base64_test.c
@@ -0,0 +1,55 @@
+#include <osmocom/core/base64.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+static const unsigned char base64_test_dec[64] = {
+ 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,
+ 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,
+ 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,
+ 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,
+ 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,
+ 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,
+ 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,
+ 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
+};
+
+static const unsigned char base64_test_enc[] =
+ "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
+ "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
+
+/*
+ * Checkup routine
+ */
+int main(int argc, char **argv)
+{
+ size_t len;
+ const unsigned char *src;
+ unsigned char buffer[128];
+
+ printf(" Base64 encoding test: ");
+
+ src = base64_test_dec;
+
+ if (osmo_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 ||
+ memcmp(base64_test_enc, buffer, 88) != 0) {
+ printf("failed\n");
+
+ exit(1);
+ }
+
+ printf("passed\n Base64 decoding test: ");
+
+ src = base64_test_enc;
+
+ if (osmo_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 ||
+ memcmp(base64_test_dec, buffer, 64) != 0) {
+ printf("failed\n");
+
+ exit(1);
+ }
+
+ printf("passed\n\n");
+
+ exit(0);
+}
diff --git a/tests/base64/base64_test.ok b/tests/base64/base64_test.ok
new file mode 100644
index 00000000..ff187d9c
--- /dev/null
+++ b/tests/base64/base64_test.ok
@@ -0,0 +1,3 @@
+ Base64 encoding test: passed
+ Base64 decoding test: passed
+
diff --git a/tests/bitgen/bitgen_test.c b/tests/bitgen/bitgen_test.c
index 8657bbef..bd472916 100644
--- a/tests/bitgen/bitgen_test.c
+++ b/tests/bitgen/bitgen_test.c
@@ -14,17 +14,17 @@
for (at_idx = 0; at_idx < len; at_idx++) { \
uint##SIZE##_t read_val = 0; \
memset(buf, 0, sizeof(buf)); \
- osmo_store##SIZE####BE_LE##_ext(val, &buf[at_idx], len); \
+ osmo_store##SIZE##BE_LE##_ext(val, &buf[at_idx], len); \
printf("osmo_store" #SIZE #BE_LE "_ext(0x%" PRIx##SIZE ", &buf[%d], %d) = %s\n", \
val, \
at_idx, len, osmo_hexdump(buf, sizeof(buf))); \
\
- read_val = osmo_load##SIZE####BE_LE##_ext(&buf[at_idx], len); \
+ read_val = osmo_load##SIZE##BE_LE##_ext(&buf[at_idx], len); \
printf("osmo_load" #SIZE #BE_LE "_ext(&buf[%d], %d) = 0x%" PRIx##SIZE "\n", \
at_idx, len, read_val); \
\
if (!strcmp(#BE_LE, "be")) { \
- read_val = osmo_load##SIZE####BE_LE##_ext_2(&buf[at_idx], len); \
+ read_val = osmo_load##SIZE##BE_LE##_ext_2(&buf[at_idx], len); \
printf("osmo_load" #SIZE #BE_LE "_ext_2(&buf[%d], %d) = 0x%" PRIx##SIZE "\n", \
at_idx, len, read_val); \
} \
diff --git a/tests/bitvec/bitvec_test.c b/tests/bitvec/bitvec_test.c
index b4764e68..29c3e33d 100644
--- a/tests/bitvec/bitvec_test.c
+++ b/tests/bitvec/bitvec_test.c
@@ -69,7 +69,7 @@ static inline void test_set(struct bitvec *bv, enum bit_value bit)
printf(" %s [%d]\n\n", lol, bv->cur_bit);
}
-static void test_byte_ops()
+static void test_byte_ops(void)
{
struct bitvec bv;
const uint8_t *in = (const uint8_t *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
@@ -162,7 +162,7 @@ static inline void test_bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits
OSMO_ASSERT(num == result);
}
-static void test_array()
+static void test_array(void)
{
struct bitvec b;
uint8_t d[4096];
@@ -181,7 +181,7 @@ static void test_array()
test_array_item(17, &b, n, array, n * 3);
}
-static void test_used_bytes()
+static void test_used_bytes(void)
{
struct bitvec b;
uint8_t d[32];
@@ -204,7 +204,7 @@ static void test_used_bytes()
}
}
-static void test_tailroom()
+static void test_tailroom(void)
{
struct bitvec b;
uint8_t d[32];
@@ -237,7 +237,8 @@ static void test_bitvec_read_field(void)
#define _bitvec_read_field(idx, len) \
readIndex = idx; \
field = bitvec_read_field(&bv, &readIndex, len); \
- printf("bitvec_read_field(idx=%u, len=%u) => %" PRIx64 "\n", idx, len, field);
+ printf("bitvec_read_field(idx=%u, len=%u) => %" PRIx64 " (%s)\n", \
+ idx, len, field, errno == 0 ? "success" : "error");
_bitvec_read_field(0, 64);
_bitvec_read_field(0, 32);
diff --git a/tests/bitvec/bitvec_test.ok b/tests/bitvec/bitvec_test.ok
index b118502e..d87ac7e0 100644
--- a/tests/bitvec/bitvec_test.ok
+++ b/tests/bitvec/bitvec_test.ok
@@ -171,21 +171,21 @@ bitvec_runlength....
bitvec bytes used.
test bitvec_read_field():
-bitvec_read_field(idx=0, len=64) => deadbeeffeebdaed
-bitvec_read_field(idx=0, len=32) => deadbeef
-bitvec_read_field(idx=0, len=16) => dead
-bitvec_read_field(idx=0, len=8) => de
-bitvec_read_field(idx=0, len=0) => 0
-bitvec_read_field(idx=8, len=8) => ad
-bitvec_read_field(idx=8, len=4) => a
-bitvec_read_field(idx=8, len=0) => 0
-bitvec_read_field(idx=10, len=9) => 16d
-bitvec_read_field(idx=10, len=7) => 5b
-bitvec_read_field(idx=10, len=5) => 16
-bitvec_read_field(idx=10, len=3) => 5
-bitvec_read_field(idx=10, len=1) => 1
-bitvec_read_field(idx=512, len=16) => ffffffffffffffea
-bitvec_read_field(idx=0, len=65) => ffffffffffffffea
-bitvec_read_field(idx=64, len=16) => ffffffffffffffea
+bitvec_read_field(idx=0, len=64) => deadbeeffeebdaed (success)
+bitvec_read_field(idx=0, len=32) => deadbeef (success)
+bitvec_read_field(idx=0, len=16) => dead (success)
+bitvec_read_field(idx=0, len=8) => de (success)
+bitvec_read_field(idx=0, len=0) => 0 (success)
+bitvec_read_field(idx=8, len=8) => ad (success)
+bitvec_read_field(idx=8, len=4) => a (success)
+bitvec_read_field(idx=8, len=0) => 0 (success)
+bitvec_read_field(idx=10, len=9) => 16d (success)
+bitvec_read_field(idx=10, len=7) => 5b (success)
+bitvec_read_field(idx=10, len=5) => 16 (success)
+bitvec_read_field(idx=10, len=3) => 5 (success)
+bitvec_read_field(idx=10, len=1) => 1 (success)
+bitvec_read_field(idx=512, len=16) => 0 (error)
+bitvec_read_field(idx=0, len=65) => 0 (error)
+bitvec_read_field(idx=64, len=16) => 0 (error)
bitvec ok.
diff --git a/tests/bsslap/bsslap_test.c b/tests/bsslap/bsslap_test.c
index f20f7db4..fc5ce754 100644
--- a/tests/bsslap/bsslap_test.c
+++ b/tests/bsslap/bsslap_test.c
@@ -48,7 +48,7 @@ struct bsslap_pdu bsslap_test_pdus[] = {
},
};
-void test_bsslap_enc_dec()
+void test_bsslap_enc_dec(void)
{
struct bsslap_pdu *pdu;
printf("--- %s\n", __func__);
@@ -96,7 +96,7 @@ loop_end:
}
}
-int main()
+int main(int argc, char **argv)
{
test_bsslap_enc_dec();
return 0;
diff --git a/tests/bssmap_le/bssmap_le_test.c b/tests/bssmap_le/bssmap_le_test.c
index f9ebd920..a14c38a3 100644
--- a/tests/bssmap_le/bssmap_le_test.c
+++ b/tests/bssmap_le/bssmap_le_test.c
@@ -118,9 +118,39 @@ struct bssmap_le_pdu bssmap_le_test_pdus[] = {
},
},
},
+ {
+ .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_REQ,
+ .perform_loc_req = {
+ .location_type = {
+ .location_information = BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC,
+ },
+
+ .cell_id = {
+ .id_discr = CELL_IDENT_LAC_AND_CI,
+ .id.lac_and_ci = {
+ .lac = 23,
+ .ci = 42,
+ },
+ },
+
+ .lcs_client_type_present = true,
+ .lcs_client_type = BSSMAP_LE_LCS_CTYPE_EMERG_SVC_UNSPECIFIED,
+
+ .more_items = true,
+
+ .lcs_priority_present = true,
+ .lcs_priority = 0x00, /* highest */
+
+ .lcs_qos_present = true,
+ .lcs_qos = {
+ .ha_ind = 1,
+ .ha_val = 0x12,
+ },
+ },
+ },
};
-void test_bssmap_le_enc_dec()
+void test_bssmap_le_enc_dec(void)
{
struct bssmap_le_pdu *pdu;
printf("--- %s\n", __func__);
@@ -132,7 +162,7 @@ void test_bssmap_le_enc_dec()
.bssmap_le = *pdu,
};
struct bssap_le_pdu dec_pdu;
- struct osmo_bssap_le_err *err;
+ struct osmo_bssap_le_err *err = NULL;
void *loop_ctx;
int rc;
@@ -170,7 +200,7 @@ loop_end:
}
}
-int main()
+int main(int argc, char **argv)
{
test_bssmap_le_enc_dec();
return 0;
diff --git a/tests/bssmap_le/bssmap_le_test.ok b/tests/bssmap_le/bssmap_le_test.ok
index a6f0dee3..8cc77039 100644
--- a/tests/bssmap_le/bssmap_le_test.ok
+++ b/tests/bssmap_le/bssmap_le_test.ok
@@ -9,3 +9,4 @@
[7] CONNECTION ORIENTED INFORMATON: ok (encoded len = 8)
[8] CONNECTION ORIENTED INFORMATON: ok (encoded len = 13)
[9] CONNECTION ORIENTED INFORMATON: ok (encoded len = 10)
+[10] PERFORM LOCATION REQUEST: ok (encoded len = 25)
diff --git a/tests/codec/codec_ecu_fr_test.c b/tests/codec/codec_ecu_fr_test.c
index 4040ce94..4e5b71d7 100644
--- a/tests/codec/codec_ecu_fr_test.c
+++ b/tests/codec/codec_ecu_fr_test.c
@@ -151,7 +151,7 @@ void test_fr_concealment_core(void)
int i, rc;
int j = 0;
- printf("=> Testing FR concealment (simple, consecutive bad frames)\n");
+ printf("=> Testing FR concealment (simple, using ECU abstraction)\n");
while (sample_frame_hex[j] != NULL) {
/* Parse frame from string to hex */
@@ -183,7 +183,7 @@ void test_fr_concealment_core(void)
}
/* Simulate a real life situation: voice frames with a few dropouts */
-void test_fr_concealment_realistic()
+void test_fr_concealment_realistic(void)
{
struct osmo_ecu_fr_state state;
uint8_t frame[GSM_FR_BYTES];
@@ -219,7 +219,7 @@ void test_fr_concealment_realistic()
}
/* Simulate a real life situation: voice frames with a few dropouts, using generic core */
-void test_fr_concealment_realistic_core()
+void test_fr_concealment_realistic_core(void)
{
struct osmo_ecu_state *state = osmo_ecu_init(NULL, OSMO_ECU_CODEC_FR);
uint8_t frame[GSM_FR_BYTES];
diff --git a/tests/codec/codec_ecu_fr_test.ok b/tests/codec/codec_ecu_fr_test.ok
index d925e285..ed5eb9d8 100644
--- a/tests/codec/codec_ecu_fr_test.ok
+++ b/tests/codec/codec_ecu_fr_test.ok
@@ -41,49 +41,49 @@ conceal: 16, result: d0000000000000000000000000000000000000000000000000000000000
conceal: 17, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 18, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 19, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-=> Testing FR concealment (simple, consecutive bad frames)
+=> Testing FR concealment (simple, using ECU abstraction)
Start with: d9ec9be212901f802335598c501f805bad3d4ba01f809b69df5a501f809cd1b4da, XMAXC: [3f, 3f, 3f, 3f]
conceal: 00, result: d9ec9be212901f802335598c501f805bad3d4ba01f809b69df5a501f809cd1b4da XMAXC: [3f, 3f, 3f, 3f]
-conceal: 01, result: d9ec9be212901d802335598c501d805bad3d4ba01d809b69df5a501d809cd1b4da XMAXC: [3b, 3b, 3b, 3b]
-conceal: 02, result: d9ec9be212901b802335598c501b805bad3d4ba01b809b69df5a501b809cd1b4da XMAXC: [37, 37, 37, 37]
+conceal: 01, result: d9ec9be212905d802335598c501d805bad3d4ba01d809b69df5a501d809cd1b4da XMAXC: [3b, 3b, 3b, 3b]
+conceal: 02, result: d9ec9be212901b802335598c501b805bad3d4ba01b809b69df5a507b809cd1b4da XMAXC: [37, 37, 37, 37]
conceal: 03, result: d9ec9be2129019802335598c5019805bad3d4ba019809b69df5a5019809cd1b4da XMAXC: [33, 33, 33, 33]
-conceal: 04, result: d9ec9be2129017802335598c5017805bad3d4ba017809b69df5a5017809cd1b4da XMAXC: [2f, 2f, 2f, 2f]
+conceal: 04, result: d9ec9be2129017802335598c5017805bad3d4ba057809b69df5a5057809cd1b4da XMAXC: [2f, 2f, 2f, 2f]
conceal: 05, result: d9ec9be2129015802335598c5015805bad3d4ba015809b69df5a5015809cd1b4da XMAXC: [2b, 2b, 2b, 2b]
-conceal: 06, result: d9ec9be2129013802335598c5013805bad3d4ba013809b69df5a5013809cd1b4da XMAXC: [27, 27, 27, 27]
+conceal: 06, result: d9ec9be2129013802335598c5073805bad3d4ba073809b69df5a5013809cd1b4da XMAXC: [27, 27, 27, 27]
conceal: 07, result: d9ec9be2129011802335598c5011805bad3d4ba011809b69df5a5011809cd1b4da XMAXC: [23, 23, 23, 23]
-conceal: 08, result: d9ec9be212900f802335598c500f805bad3d4ba00f809b69df5a500f809cd1b4da XMAXC: [1f, 1f, 1f, 1f]
-conceal: 09, result: d9ec9be212900d802335598c500d805bad3d4ba00d809b69df5a500d809cd1b4da XMAXC: [1b, 1b, 1b, 1b]
-conceal: 10, result: d9ec9be212900b802335598c500b805bad3d4ba00b809b69df5a500b809cd1b4da XMAXC: [17, 17, 17, 17]
-conceal: 11, result: d9ec9be2129009802335598c5009805bad3d4ba009809b69df5a5009809cd1b4da XMAXC: [13, 13, 13, 13]
-conceal: 12, result: d9ec9be2129007802335598c5007805bad3d4ba007809b69df5a5007809cd1b4da XMAXC: [f, f, f, f]
-conceal: 13, result: d9ec9be2129005802335598c5005805bad3d4ba005809b69df5a5005809cd1b4da XMAXC: [b, b, b, b]
-conceal: 14, result: d9ec9be2129003802335598c5003805bad3d4ba003809b69df5a5003809cd1b4da XMAXC: [7, 7, 7, 7]
-conceal: 15, result: d9ec9be2129001802335598c5001805bad3d4ba001809b69df5a5001809cd1b4da XMAXC: [3, 3, 3, 3]
-conceal: 16, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-conceal: 17, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-conceal: 18, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-conceal: 19, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
+conceal: 08, result: d9ec9be212904f802335598c500f805bad3d4ba04f809b69df5a500f809cd1b4da XMAXC: [1f, 1f, 1f, 1f]
+conceal: 09, result: d9ec9be212900d802335598c500d805bad3d4ba00d809b69df5a506d809cd1b4da XMAXC: [1b, 1b, 1b, 1b]
+conceal: 10, result: d9ec9be212900b802335598c506b805bad3d4ba00b809b69df5a500b809cd1b4da XMAXC: [17, 17, 17, 17]
+conceal: 11, result: d9ec9be2129009802335598c5009805bad3d4ba049809b69df5a5049809cd1b4da XMAXC: [13, 13, 13, 13]
+conceal: 12, result: d9ec9be2129047802335598c5047805bad3d4ba007809b69df5a5007809cd1b4da XMAXC: [f, f, f, f]
+conceal: 13, result: d9ec9be2129005802335598c5065805bad3d4ba065809b69df5a5065809cd1b4da XMAXC: [b, b, b, b]
+conceal: 14, result: d9ec9be2129063802335598c5003805bad3d4ba003809b69df5a5003809cd1b4da XMAXC: [7, 7, 7, 7]
+conceal: 15, result: d9ec9be2129041802335598c5001805bad3d4ba001809b69df5a5001809cd1b4da XMAXC: [3, 3, 3, 3]
+conceal: 16, result: d9ec9be2129040002335598c5000005bad3d4ba000009b69df5a5060009cd1b4da XMAXC: [0, 0, 0, 0]
+conceal: 17, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
+conceal: 18, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
+conceal: 19, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
Start with: d9ec9be212901d802335598c5013805bad3d4ba01f809b69df5a5019809cd1b4da, XMAXC: [3b, 27, 3f, 33]
conceal: 00, result: d9ec9be212901d802335598c5013805bad3d4ba01f809b69df5a5019809cd1b4da XMAXC: [3b, 27, 3f, 33]
-conceal: 01, result: d9ec9be212901b802335598c5011805bad3d4ba01d809b69df5a5017809cd1b4da XMAXC: [37, 23, 3b, 2f]
-conceal: 02, result: d9ec9be2129019802335598c500f805bad3d4ba01b809b69df5a5015809cd1b4da XMAXC: [33, 1f, 37, 2b]
-conceal: 03, result: d9ec9be2129017802335598c500d805bad3d4ba019809b69df5a5013809cd1b4da XMAXC: [2f, 1b, 33, 27]
-conceal: 04, result: d9ec9be2129015802335598c500b805bad3d4ba017809b69df5a5011809cd1b4da XMAXC: [2b, 17, 2f, 23]
-conceal: 05, result: d9ec9be2129013802335598c5009805bad3d4ba015809b69df5a500f809cd1b4da XMAXC: [27, 13, 2b, 1f]
-conceal: 06, result: d9ec9be2129011802335598c5007805bad3d4ba013809b69df5a500d809cd1b4da XMAXC: [23, f, 27, 1b]
-conceal: 07, result: d9ec9be212900f802335598c5005805bad3d4ba011809b69df5a500b809cd1b4da XMAXC: [1f, b, 23, 17]
-conceal: 08, result: d9ec9be212900d802335598c5003805bad3d4ba00f809b69df5a5009809cd1b4da XMAXC: [1b, 7, 1f, 13]
-conceal: 09, result: d9ec9be212900b802335598c5001805bad3d4ba00d809b69df5a5007809cd1b4da XMAXC: [17, 3, 1b, f]
-conceal: 10, result: d9ec9be2129009802335598c5000005bad3d4ba00b809b69df5a5005809cd1b4da XMAXC: [13, 0, 17, b]
-conceal: 11, result: d9ec9be2129007802335598c5000005bad3d4ba009809b69df5a5003809cd1b4da XMAXC: [f, 0, 13, 7]
-conceal: 12, result: d9ec9be2129005802335598c5000005bad3d4ba007809b69df5a5001809cd1b4da XMAXC: [b, 0, f, 3]
-conceal: 13, result: d9ec9be2129003802335598c5000005bad3d4ba005809b69df5a5000009cd1b4da XMAXC: [7, 0, b, 0]
-conceal: 14, result: d9ec9be2129001802335598c5000005bad3d4ba003809b69df5a5000009cd1b4da XMAXC: [3, 0, 7, 0]
-conceal: 15, result: d9ec9be2129000002335598c5000005bad3d4ba001809b69df5a5000009cd1b4da XMAXC: [0, 0, 3, 0]
-conceal: 16, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-conceal: 17, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-conceal: 18, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-conceal: 19, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
+conceal: 01, result: d9ec9be212901b802335598c5011805bad3d4ba01d809b69df5a5077809cd1b4da XMAXC: [37, 23, 3b, 2f]
+conceal: 02, result: d9ec9be2129019802335598c500f805bad3d4ba05b809b69df5a5055809cd1b4da XMAXC: [33, 1f, 37, 2b]
+conceal: 03, result: d9ec9be2129017802335598c500d805bad3d4ba059809b69df5a5053809cd1b4da XMAXC: [2f, 1b, 33, 27]
+conceal: 04, result: d9ec9be2129015802335598c506b805bad3d4ba077809b69df5a5011809cd1b4da XMAXC: [2b, 17, 2f, 23]
+conceal: 05, result: d9ec9be2129013802335598c5069805bad3d4ba075809b69df5a500f809cd1b4da XMAXC: [27, 13, 2b, 1f]
+conceal: 06, result: d9ec9be2129051802335598c5007805bad3d4ba053809b69df5a500d809cd1b4da XMAXC: [23, f, 27, 1b]
+conceal: 07, result: d9ec9be212904f802335598c5005805bad3d4ba051809b69df5a506b809cd1b4da XMAXC: [1f, b, 23, 17]
+conceal: 08, result: d9ec9be212900d802335598c5063805bad3d4ba00f809b69df5a5069809cd1b4da XMAXC: [1b, 7, 1f, 13]
+conceal: 09, result: d9ec9be212900b802335598c5061805bad3d4ba04d809b69df5a5047809cd1b4da XMAXC: [17, 3, 1b, f]
+conceal: 10, result: d9ec9be2129049802335598c5040005bad3d4ba04b809b69df5a5045809cd1b4da XMAXC: [13, 0, 17, b]
+conceal: 11, result: d9ec9be2129047802335598c5020005bad3d4ba069809b69df5a5063809cd1b4da XMAXC: [f, 0, 13, 7]
+conceal: 12, result: d9ec9be2129065802335598c5060005bad3d4ba067809b69df5a5061809cd1b4da XMAXC: [b, 0, f, 3]
+conceal: 13, result: d9ec9be2129023802335598c5000005bad3d4ba005809b69df5a5000009cd1b4da XMAXC: [7, 0, b, 0]
+conceal: 14, result: d9ec9be2129001802335598c5000005bad3d4ba003809b69df5a5060009cd1b4da XMAXC: [3, 0, 7, 0]
+conceal: 15, result: d9ec9be2129040002335598c5000005bad3d4ba001809b69df5a5000009cd1b4da XMAXC: [0, 0, 3, 0]
+conceal: 16, result: d9ec9be2129000002335598c5000005bad3d4ba040009b69df5a5020009cd1b4da XMAXC: [0, 0, 0, 0]
+conceal: 17, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
+conceal: 18, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
+conceal: 19, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
=> Testing FR concealment (realistic, various bad frames)
Frame No. 000:
@@ -318,7 +318,7 @@ Frame No. 018:
* output: d9689ba5e3d260491b516adb5e4027256e27227ee0351c8e549a5c60492471971b
Frame No. 019:
* input: (bad)
- * output: d00000000000000000000000000000000000000000000000000000000000000000
+ * output: d9689ba5e3d240491b516adb5e0027256e27227e80351c8e549a5c00492471971b
Frame No. 020:
* input: d8e6a2e1d3d2605b1376c8d35280392451391cbc80392a71b6db8aa049238dc8ab
* output: d8e6a2e1d3d2605b1376c8d35280392451391cbc80392a71b6db8aa049238dc8ab
@@ -357,19 +357,19 @@ Frame No. 031:
* output: d9a99361a276403b1a6ad6dcd40026e489c8e3bc40371c4dc564e2c036e28eb963
Frame No. 032:
* input: (bad)
- * output: d00000000000000000000000000000000000000000000000000000000000000000
+ * output: d9a99361a276003b1a6ad6dcd40026e489c8e3bc00371c4dc564e2e036e28eb963
Frame No. 033:
* input: (bad)
- * output: d00000000000000000000000000000000000000000000000000000000000000000
+ * output: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b
Frame No. 034:
* input: (bad)
- * output: d00000000000000000000000000000000000000000000000000000000000000000
+ * output: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b
Frame No. 035:
* input: (bad)
- * output: d00000000000000000000000000000000000000000000000000000000000000000
+ * output: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b
Frame No. 036:
* input: (bad)
- * output: d00000000000000000000000000000000000000000000000000000000000000000
+ * output: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b
Frame No. 037:
* input: d92c8b6d5aee4034ebb22724862047145634a5c0a038e371b8e4a880485c89dd25
* output: d92c8b6d5aee4034ebb22724862047145634a5c0a038e371b8e4a880485c89dd25
diff --git a/tests/coding/coding_test.c b/tests/coding/coding_test.c
index bdfe3002..d536e443 100644
--- a/tests/coding/coding_test.c
+++ b/tests/coding/coding_test.c
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
@@ -28,6 +24,7 @@
#include <osmocom/core/bits.h>
#include <osmocom/core/utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/coding/gsm0503_coding.h>
#define DUMP_U_AT(b, x, u) do { \
@@ -303,7 +300,7 @@ static void test_hr(uint8_t *speech, int len)
memset(bursts_s + 6, 0, 20);
/* Decode, correcting errors */
- rc = gsm0503_tch_hr_decode(result, bursts_s, 0,
+ rc = gsm0503_tch_hr_decode2(result, bursts_s, 0,
&n_errors, &n_bits_total);
CHECK_RC_OR_RET(rc == len, "decoding");
@@ -316,6 +313,51 @@ static void test_hr(uint8_t *speech, int len)
printf("\n");
}
+static void test_facch(const uint8_t *data, bool half_rate)
+{
+ ubit_t bursts_u[116 * 8 * 2] = { 0 };
+ sbit_t bursts_s[116 * 8 * 2] = { 0 };
+ int rc;
+
+ /* Encode the given FACCH message three times (at different offsets) */
+ printf("%s(FACCH/%c): encoding: %s\n",
+ __func__, half_rate ? 'H' : 'F',
+ osmo_hexdump(&data[0], GSM_MACBLOCK_LEN));
+ for (unsigned int i = 0; i < 3; i++) {
+ ubit_t *pos = &bursts_u[116 * 4 * i];
+
+ if (half_rate)
+ rc = gsm0503_tch_hr_facch_encode(pos, &data[0]);
+ else
+ rc = gsm0503_tch_fr_facch_encode(pos, &data[0]);
+ CHECK_RC_OR_RET(rc == 0, "encoding");
+ }
+
+ /* Prepare soft-bits */
+ osmo_ubit2sbit(bursts_s, bursts_u, sizeof(bursts_s));
+
+ /* Decode three FACCH messages (at different offsets) */
+ for (unsigned int i = 0; i < 3; i++) {
+ const sbit_t *pos = &bursts_s[116 * 4 * i];
+ uint8_t result[GSM_MACBLOCK_LEN];
+ int n_errors, n_bits_total;
+
+ if (half_rate)
+ rc = gsm0503_tch_hr_facch_decode(&result[0], pos,
+ &n_errors, &n_bits_total);
+ else
+ rc = gsm0503_tch_fr_facch_decode(&result[0], pos,
+ &n_errors, &n_bits_total);
+ CHECK_RC_OR_RET(rc == GSM_MACBLOCK_LEN, "decoding");
+
+ printf("%s(FACCH/%c): decoded (BER=%d/%d): %s\n",
+ __func__, half_rate ? 'H' : 'F', n_errors, n_bits_total,
+ osmo_hexdump(result, GSM_MACBLOCK_LEN));
+ }
+
+ printf("\n");
+}
+
struct test_macblock {
bool is_egprs;
uint16_t exp_burst_bits;
@@ -399,11 +441,11 @@ static void test_pdtch(const struct test_macblock *tmb, int len)
case 34:
case 54:
l2[len - 1] &= 0x7f;
- result[len - 1] &= 0x7f;
+ result[len - 1] = 0x00;
break;
case 40:
l2[len - 1] &= 0x07;
- result[len - 1] &= 0x07;
+ result[len - 1] = 0x00;
break;
}
@@ -491,7 +533,128 @@ static const sbit_t test_rach_11bit[6][36] = {
uint8_t test_speech_fr[33];
uint8_t test_speech_efr[31];
-uint8_t test_speech_hr[15];
+uint8_t test_speech_hr[14];
+
+struct csd_test_case {
+ const char *name;
+ unsigned int num_bits;
+ int (*enc_fn)(ubit_t *out, const ubit_t *in);
+ int (*dec_fn)(ubit_t *out, const sbit_t *in, int *ne, int *nb);
+ bool half_rate;
+};
+
+static const struct csd_test_case csd_tests[] = {
+ {
+ .name = "TCH/F9.6",
+ .num_bits = 4 * 60,
+ .enc_fn = &gsm0503_tch_fr96_encode,
+ .dec_fn = &gsm0503_tch_fr96_decode,
+ },
+ {
+ .name = "TCH/F4.8",
+ .num_bits = 2 * 60,
+ .enc_fn = &gsm0503_tch_fr48_encode,
+ .dec_fn = &gsm0503_tch_fr48_decode,
+ },
+ {
+ .name = "TCH/H4.8",
+ .num_bits = 4 * 60,
+ .enc_fn = &gsm0503_tch_hr48_encode,
+ .dec_fn = &gsm0503_tch_hr48_decode,
+ .half_rate = true,
+ },
+ {
+ .name = "TCH/F2.4",
+ .num_bits = 2 * 36,
+ .enc_fn = &gsm0503_tch_fr24_encode,
+ .dec_fn = &gsm0503_tch_fr24_decode,
+ },
+ {
+ .name = "TCH/H2.4",
+ .num_bits = 4 * 36,
+ .enc_fn = &gsm0503_tch_hr24_encode,
+ .dec_fn = &gsm0503_tch_hr24_decode,
+ .half_rate = true,
+ },
+ {
+ .name = "TCH/F14.4",
+ .num_bits = 290,
+ .enc_fn = &gsm0503_tch_fr144_encode,
+ .dec_fn = &gsm0503_tch_fr144_decode,
+ },
+};
+
+static void test_csd(const struct csd_test_case *tc, bool facch)
+{
+ const uint8_t patterns[] = { 0x00, 0xaa, 0xff };
+ ubit_t bursts_u[116 * (22 + 8)] = { 0 };
+ sbit_t bursts_s[116 * (22 + 8)] = { 0 };
+ ubit_t data[512];
+ int rc;
+
+ /* Encode three data blocks, each block filled-in with a pattern */
+ for (unsigned int i = 0; i < ARRAY_SIZE(patterns); i++) {
+ for (unsigned int j = 0; j < tc->num_bits; j++)
+ data[j] = (patterns[i] & (1 << (j % 8))) != 0;
+
+ rc = tc->enc_fn(&bursts_u[i * 4 * 116], &data[0]);
+ CHECK_RC_OR_RET(rc == 0, "encoding");
+
+ /* Test FACCH bitstealing */
+ if (facch && i == 1) {
+ memset(&data, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+ if (tc->half_rate)
+ rc = gsm0503_tch_hr_facch_encode(&bursts_u[116 * 4], &data[0]);
+ else
+ rc = gsm0503_tch_fr_facch_encode(&bursts_u[116 * 4], &data[0]);
+ CHECK_RC_OR_RET(rc == 0, "encoding FACCH");
+ }
+ }
+
+ /* Prepare soft-bits */
+ osmo_ubit2sbit(&bursts_s[0], &bursts_u[0], sizeof(bursts_s));
+
+ /* Decode the soft-bits, print decoded blocks */
+ for (unsigned int i = 0; i < ARRAY_SIZE(patterns); i++) {
+ int n_errors, n_bits_total;
+
+ rc = tc->dec_fn(&data[0], &bursts_s[i * 4 * 116],
+ &n_errors, &n_bits_total);
+ CHECK_RC_OR_RET(rc == tc->num_bits, "decoding");
+
+ printf("%s(%s): block #%u (pattern 0x%02x): n_errors=%d / n_bits_total=%d\n",
+ __func__, tc->name, i, patterns[i], n_errors, n_bits_total);
+
+ for (unsigned int j = 0; j < tc->num_bits; j++) {
+ if (j && j % 64 == 0)
+ printf("\n");
+ else if (j && j % 8 == 0)
+ printf(" ");
+ printf("%c", data[j] ? '1' : '0');
+ }
+ printf("\n");
+ }
+
+ /* Test FACCH bitstealing if requested */
+ if (facch) {
+ int n_errors = 0, n_bits_total = 0;
+
+ if (tc->half_rate) {
+ rc = gsm0503_tch_hr_facch_decode(&data[0], &bursts_s[116 * 4],
+ &n_errors, &n_bits_total);
+ } else {
+ rc = gsm0503_tch_fr_facch_decode(&data[0], &bursts_s[116 * 4],
+ &n_errors, &n_bits_total);
+ }
+ CHECK_RC_OR_RET(rc == GSM_MACBLOCK_LEN, "decoding FACCH");
+
+ printf("%s(%s): FACCH/%c (pattern 0x2b): n_errors=%d / n_bits_total=%d\n",
+ __func__, tc->name, tc->half_rate ? 'H' : 'F', n_errors, n_bits_total);
+ printf("%s\n", osmo_hexdump(&data[0], GSM_MACBLOCK_LEN));
+ }
+
+ printf("\n");
+}
int main(int argc, char **argv)
{
@@ -537,7 +700,6 @@ int main(int argc, char **argv)
for (i = 0; i < sizeof(test_speech_hr); i++)
test_speech_hr[i] = i * 17;
- test_speech_hr[0] = 0x00;
test_hr(test_speech_hr, sizeof(test_speech_hr));
for (i = 0; i < len_l2; i++)
@@ -554,6 +716,20 @@ int main(int argc, char **argv)
}
}
+ printf("\nTesting FACCH/F codec:\n");
+ for (i = 0; i < ARRAY_SIZE(test_l2); i++)
+ test_facch(test_l2[i], false);
+ printf("\nTesting FACCH/H codec:\n");
+ for (i = 0; i < ARRAY_SIZE(test_l2); i++)
+ test_facch(test_l2[i], true);
+
+ printf("\nTesting CSD functions (no FACCH):\n");
+ for (i = 0; i < ARRAY_SIZE(csd_tests); i++)
+ test_csd(&csd_tests[i], false);
+ printf("\nTesting CSD functions (with FACCH):\n");
+ for (i = 0; i < ARRAY_SIZE(csd_tests); i++)
+ test_csd(&csd_tests[i], true);
+
printf("Success\n");
return 0;
diff --git a/tests/coding/coding_test.ok b/tests/coding/coding_test.ok
index cb562e9c..6767aeaf 100644
--- a/tests/coding/coding_test.ok
+++ b/tests/coding/coding_test.ok
@@ -172,22 +172,22 @@ S-Bits:
Decoded: 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
tch_fr_decode: n_errors=10 n_bits_total=456 ber=0.02
-Encoding: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee
+Encoding: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd
U-Bits:
-0E1E0E0E1E0E0E1E1E0E1E0E0E0E0E0E0E0E0E1E1E0E0E1E1E1E1E1E1 23 00 E1E1E1E1E1E0E1E1E1E0E0E1E0E1E1E1E0E0E1E0E0E1E1E1E1E0E0E0E
-0E1E1E0E0E0E0E1E0E1E1E1E0E0E1E1E0E0E1E0E1E0E1E0E0E0E1E0E1 23 00 E0E0E0E1E1E1E1E1E0E1E1E1E0E0E0E0E1E1E1E0E0E1E1E0E1E1E1E0E
-E0E0E0E1E1E1E1E1E0E1E0E0E1E1E0E0E1E0E1E0E0E1E1E0E0E0E0E0E 00 23 0E0E0E1E0E1E0E1E0E0E1E1E0E1E0E0E0E1E1E1E0E0E0E0E1E0E1E0E1
-E1E0E0E1E1E1E1E1E1E0E0E1E1E1E0E0E0E1E1E1E1E1E1E1E1E0E1E1E 00 23 0E0E1E0E1E0E0E0E1E0E0E1E1E0E1E1E1E1E0E1E1E0E1E0E0E1E0E1E1
+0E0E0E0E1E0E0E0E0E0E0E1E1E0E1E0E0E0E1E1E1E0E1E0E0E0E1E1E1 23 00 E1E0E1E0E1E1E1E0E1E1E0E0E0E0E1E0E1E0E0E0E1E1E0E1E1E0E1E0E
+1E0E1E0E0E1E0E0E0E1E1E1E0E0E1E1E1E0E1E1E0E1E0E0E1E0E1E0E0 23 00 E1E1E1E1E1E1E0E1E1E0E0E1E1E0E0E0E0E0E0E0E1E1E1E1E0E0E1E1E
+E0E1E1E1E0E0E0E1E1E1E1E1E1E0E0E1E0E1E1E1E0E0E1E0E0E1E1E1E 00 23 0E1E0E0E1E1E0E1E1E1E1E1E1E1E1E1E0E1E0E0E1E1E1E0E0E0E0E1E1
+E0E0E0E1E0E0E0E1E1E0E1E0E0E0E1E0E1E0E1E0E0E0E0E0E1E0E1E0E 00 23 1E0E1E0E0E1E0E1E0E0E0E0E1E0E1E1E1E1E1E0E1E0E1E1E1E1E0E1E0
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE 23 23 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE 23 23 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
S-Bits:
-7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81
-7f 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 81 81 81 81 7f 81
-81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 81
-81 81 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 81
+7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81
+81 81 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81 81 81 81
+81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81
+81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f
81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81
81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81
-Decoded: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee
+Decoded: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd
tch_hr_decode: n_errors=10 n_bits_total=211 ber=0.05
Encoding: 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
@@ -270,7 +270,7 @@ S-Bits:
7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 81
7f 81 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81
Decoded: a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8 42 a3 af 5f c6 36 43 44 ab a3 2f
-pdtch_decode: n_errors=132 n_bits_total=588 ber=0.22
+pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00
Encoding: a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8 42 a3 af 5f c6 36 43 44 ab a3 af 5f c6 36 43 44 03
U-Bits:
@@ -284,7 +284,7 @@ S-Bits:
81 81 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f
7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81
Decoded: a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8 42 a3 af 5f c6 36 43 44 ab a3 af 5f c6 36 43 44 03
-pdtch_decode: n_errors=220 n_bits_total=676 ber=0.33
+pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00
Encoding: a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8 42 a3 af 5f c6 36 43 44 ab a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 28
U-Bits:
@@ -326,7 +326,7 @@ S-Bits:
7f 7f 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81
81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f
Decoded: 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 00 00 00 00 00 00 00 00 00 00 00
-pdtch_decode: n_errors=132 n_bits_total=588 ber=0.22
+pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00
Encoding: 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
U-Bits:
@@ -340,7 +340,7 @@ S-Bits:
81 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f
7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
Decoded: 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-pdtch_decode: n_errors=220 n_bits_total=676 ber=0.33
+pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00
Encoding: 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
U-Bits:
@@ -367,4 +367,222 @@ S-Bits:
81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81
81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81
7f 7f 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f
+
+Testing FACCH/F codec:
+test_facch(FACCH/F): encoding: 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+test_facch(FACCH/F): decoded (BER=0/456): 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+test_facch(FACCH/F): decoded (BER=0/456): 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+test_facch(FACCH/F): decoded (BER=0/456): 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+test_facch(FACCH/F): encoding: a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+test_facch(FACCH/F): decoded (BER=0/456): a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+test_facch(FACCH/F): decoded (BER=0/456): a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+test_facch(FACCH/F): decoded (BER=0/456): a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+
+test_facch(FACCH/F): encoding: 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+test_facch(FACCH/F): decoded (BER=0/456): 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+test_facch(FACCH/F): decoded (BER=0/456): 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+test_facch(FACCH/F): decoded (BER=0/456): 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+
+
+Testing FACCH/H codec:
+test_facch(FACCH/H): encoding: 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+test_facch(FACCH/H): decoded (BER=0/456): 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+test_facch(FACCH/H): decoded (BER=0/456): 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+test_facch(FACCH/H): decoded (BER=0/456): 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+test_facch(FACCH/H): encoding: a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+test_facch(FACCH/H): decoded (BER=0/456): a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+test_facch(FACCH/H): decoded (BER=0/456): a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+test_facch(FACCH/H): decoded (BER=0/456): a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+
+test_facch(FACCH/H): encoding: 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+test_facch(FACCH/H): decoded (BER=0/456): 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+test_facch(FACCH/H): decoded (BER=0/456): 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+test_facch(FACCH/H): decoded (BER=0/456): 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+
+
+Testing CSD functions (no FACCH):
+test_csd(TCH/F9.6): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000
+test_csd(TCH/F9.6): block #1 (pattern 0xaa): n_errors=0 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101
+test_csd(TCH/F9.6): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111
+
+test_csd(TCH/F4.8): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000
+test_csd(TCH/F4.8): block #1 (pattern 0xaa): n_errors=0 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101
+test_csd(TCH/F4.8): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111
+
+test_csd(TCH/H4.8): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000
+test_csd(TCH/H4.8): block #1 (pattern 0xaa): n_errors=0 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101
+test_csd(TCH/H4.8): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111
+
+test_csd(TCH/F2.4): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000
+test_csd(TCH/F2.4): block #1 (pattern 0xaa): n_errors=0 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101
+test_csd(TCH/F2.4): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111
+
+test_csd(TCH/H2.4): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000
+test_csd(TCH/H2.4): block #1 (pattern 0xaa): n_errors=0 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101
+test_csd(TCH/H2.4): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111
+
+test_csd(TCH/F14.4): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00
+test_csd(TCH/F14.4): block #1 (pattern 0xaa): n_errors=0 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01
+test_csd(TCH/F14.4): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11
+
+
+Testing CSD functions (with FACCH):
+test_csd(TCH/F9.6): block #0 (pattern 0x00): n_errors=34 / n_bits_total=456
+00000000 00000000 00000000 00000000 00010010 01100000 00000000 00000000
+00000000 00010011 01111000 00000000 00000000 00000000 00100100 10000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000
+test_csd(TCH/F9.6): block #1 (pattern 0xaa): n_errors=34 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 00101011 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101
+test_csd(TCH/F9.6): block #2 (pattern 0xff): n_errors=14 / n_bits_total=456
+11111111 11111111 11111111 11110111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111
+test_csd(TCH/F9.6): FACCH/F (pattern 0x2b): n_errors=0 / n_bits_total=456
+2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+test_csd(TCH/F4.8): block #0 (pattern 0x00): n_errors=38 / n_bits_total=456
+00000000 00000000 01010000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000
+test_csd(TCH/F4.8): block #1 (pattern 0xaa): n_errors=37 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101
+test_csd(TCH/F4.8): block #2 (pattern 0xff): n_errors=13 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111
+test_csd(TCH/F4.8): FACCH/F (pattern 0x2b): n_errors=0 / n_bits_total=456
+2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+test_csd(TCH/H4.8): block #0 (pattern 0x00): n_errors=35 / n_bits_total=456
+00000000 00000000 00000000 00000000 00111000 10000000 00000000 00000000
+00000001 00000000 00000000 00000000 00000000 00000000 00011000 10000000
+00000000 00000000 00000000 00000000 00000000 00001000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000
+test_csd(TCH/H4.8): block #1 (pattern 0xaa): n_errors=38 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 00101011 01010101
+01010101 01010101 01010101 00111010 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101
+test_csd(TCH/H4.8): block #2 (pattern 0xff): n_errors=3 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111
+test_csd(TCH/H4.8): FACCH/H (pattern 0x2b): n_errors=0 / n_bits_total=456
+2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+test_csd(TCH/F2.4): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000
+test_csd(TCH/F2.4): block #1 (pattern 0xaa): n_errors=138 / n_bits_total=456
+11000010 10010010 10010010 10010010 10010010 10010010 10010101 00011000
+01001010
+test_csd(TCH/F2.4): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111
+test_csd(TCH/F2.4): FACCH/F (pattern 0x2b): n_errors=0 / n_bits_total=456
+2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+test_csd(TCH/H2.4): block #0 (pattern 0x00): n_errors=38 / n_bits_total=456
+00000000 00000000 00000101 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000
+test_csd(TCH/H2.4): block #1 (pattern 0xaa): n_errors=32 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101
+test_csd(TCH/H2.4): block #2 (pattern 0xff): n_errors=2 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111
+test_csd(TCH/H2.4): FACCH/H (pattern 0x2b): n_errors=0 / n_bits_total=456
+2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+test_csd(TCH/F14.4): block #0 (pattern 0x00): n_errors=27 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00111100 01000000 00000000
+00000000 00000000 00000000 00111000 10011000 00000000 10000000 00000000
+00000000 00000000 00001111 10001101 01110000 00000000 00000000 00000000
+00000000 00000000 00001001 11000000 00000000 00000000 00000000 00000000
+00000000 00000010 00100010 01000000 00
+test_csd(TCH/F14.4): block #1 (pattern 0xaa): n_errors=35 / n_bits_total=456
+01010101 01010100 10110111 01010101 01010101 00011000 10010001 11000110
+11011100 11011101 11000110 01010101 01010101 01010101 10100100 10010001
+11000110 00001010 01010101 01010101 11101101 11010101 01010100 01010101
+01010101 01010101 00101001 00100101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01
+test_csd(TCH/F14.4): block #2 (pattern 0xff): n_errors=10 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11
+test_csd(TCH/F14.4): FACCH/F (pattern 0x2b): n_errors=0 / n_bits_total=456
+2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
Success
diff --git a/tests/conv/conv_gsm0503_test.ok b/tests/conv/conv_gsm0503_test.ok
index 764bd436..dba52bdf 100644
--- a/tests/conv/conv_gsm0503_test.ok
+++ b/tests/conv/conv_gsm0503_test.ok
@@ -6,6 +6,46 @@
[..] Encoding / Decoding cycle : OK
[..] Encoding / Decoding cycle : OK
+[+] Testing: gsm0503_tch_f24
+[.] Input length : ret = 72 exp = 72 -> OK
+[.] Output length : ret = 456 exp = 456 -> OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
+[+] Testing: gsm0503_tch_h24
+[.] Input length : ret = 72 exp = 72 -> OK
+[.] Output length : ret = 228 exp = 228 -> OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
+[+] Testing: gsm0503_tch_f48
+[.] Input length : ret = 148 exp = 148 -> OK
+[.] Output length : ret = 456 exp = 456 -> OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
+[+] Testing: gsm0503_tch_f96
+[.] Input length : ret = 240 exp = 240 -> OK
+[.] Output length : ret = 456 exp = 456 -> OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
+[+] Testing: gsm0503_tch_f144
+[.] Input length : ret = 290 exp = 290 -> OK
+[.] Output length : ret = 456 exp = 456 -> OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
[+] Testing: gsm0503_rach
[.] Input length : ret = 14 exp = 14 -> OK
[.] Output length : ret = 36 exp = 36 -> OK
diff --git a/tests/ctrl/ctrl_test.c b/tests/ctrl/ctrl_test.c
index b46e9ac5..1d4d4d74 100644
--- a/tests/ctrl/ctrl_test.c
+++ b/tests/ctrl/ctrl_test.c
@@ -117,11 +117,14 @@ static void assert_test(struct ctrl_handle *ctrl, struct ctrl_connection *ccon,
} else {
struct msgb *sent_msg = msgb_dequeue(&ccon->write_queue.msg_queue);
OSMO_ASSERT(sent_msg);
- msgb_put_u8(sent_msg, 0);
- printf("replied: '%s'\n", osmo_escape_str((char*)msgb_l2(sent_msg), -1));
+ char *strbuf = talloc_size(sent_msg, msgb_l2len(sent_msg) + 1);
+ memcpy(strbuf, msgb_l2(sent_msg), msgb_l2len(sent_msg));
+ strbuf[msgb_l2len(sent_msg)] = '\0';
+
+ printf("replied: '%s'\n", osmo_escape_str(strbuf, -1));
OSMO_ASSERT(t->reply_str);
- OSMO_ASSERT(!strcmp(t->reply_str, (char*)msgb_l2(sent_msg)));
+ OSMO_ASSERT(!strcmp(t->reply_str, strbuf));
msgb_free(sent_msg);
}
osmo_wqueue_clear(&ccon->write_queue);
@@ -331,7 +334,7 @@ static const struct one_test test_messages_list[] = {
},
};
-static void test_messages()
+static void test_messages(void)
{
struct ctrl_handle *ctrl;
struct ctrl_connection *ccon;
@@ -387,7 +390,7 @@ static int verify_test_defer(struct ctrl_cmd *cmd, const char *value, void *data
return 0;
}
-static void test_deferred_cmd()
+static void test_deferred_cmd(void)
{
struct ctrl_handle *ctrl;
struct ctrl_connection *ccon;
diff --git a/tests/dtx/dtx_gsm0503_test.c b/tests/dtx/dtx_gsm0503_test.c
index f3003133..21d1751e 100644
--- a/tests/dtx/dtx_gsm0503_test.c
+++ b/tests/dtx/dtx_gsm0503_test.c
@@ -15,18 +15,18 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <osmocom/core/utils.h>
+#include <osmocom/coding/gsm0503_coding.h>
#include <osmocom/coding/gsm0503_amr_dtx.h>
+/* Length of payload bits in a Normal Burst */
+#define BURST_PLEN (57 * 2 + 2)
+
char sample_afs_sid_frame[] =
{
"111111110000000011001100101010100100010011111111001000100111011110011001001100111100110010011001111011100100010011111111001000100111011110011001001100111100110010011001111011100100010011111111001000100111011110011001001100111100110010011001111011100100010011111111001000100111011110011001001100111100110010011001111011100100010011111111001000100111011110011001001100111100110010011001111011100100010011111111001000100111011110011001001100111100110010011001"
@@ -72,7 +72,7 @@ char sample_sid_update_inh_frame[] =
"xBxBxBxBxBxBxBxBxBxBxBxBxBxBxBxBx0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1"
};
-unsigned int string_to_ubit(ubit_t * ubits, char *string)
+unsigned int string_to_sbit(sbit_t *sbits, char *string)
{
unsigned int len;
unsigned int i;
@@ -80,7 +80,7 @@ unsigned int string_to_ubit(ubit_t * ubits, char *string)
len = strlen(string);
for (i = 0; i < len; i++) {
- ubits[i] = string[i] & 1;
+ sbits[i] = string[i] == '1' ? -127 : 127;
}
return len;
@@ -88,28 +88,161 @@ unsigned int string_to_ubit(ubit_t * ubits, char *string)
void test_gsm0503_detect_afs_dtx_frame(char *string)
{
- ubit_t ubits[512];
+ sbit_t sbits[512];
uint8_t dtx_frame_type;
int n_errors;
int n_bits_total;
+ int mode_id = -1;
- string_to_ubit(ubits, string);
- dtx_frame_type = gsm0503_detect_afs_dtx_frame(&n_errors, &n_bits_total, ubits);
- printf(" ==> %s, n_errors=%i, n_bits_total=%i\n", gsm0503_amr_dtx_frame_name(dtx_frame_type),
- n_errors, n_bits_total);
+ string_to_sbit(sbits, string);
+ dtx_frame_type = gsm0503_detect_afs_dtx_frame2(&n_errors, &n_bits_total, &mode_id, sbits);
+ printf(" ==> %s, n_errors=%d, n_bits_total=%d, mode_id=%d\n",
+ gsm0503_amr_dtx_frame_name(dtx_frame_type),
+ n_errors, n_bits_total, mode_id);
}
void test_gsm0503_detect_ahs_dtx_frame(char *string)
{
- ubit_t ubits[512];
+ sbit_t sbits[512];
uint8_t dtx_frame_type;
int n_errors;
int n_bits_total;
+ int mode_id = -1;
+
+ string_to_sbit(sbits, string);
+ dtx_frame_type = gsm0503_detect_ahs_dtx_frame2(&n_errors, &n_bits_total, &mode_id, sbits);
+ printf(" ==> %s, n_errors=%d, n_bits_total=%d, mode_id=%d\n",
+ gsm0503_amr_dtx_frame_name(dtx_frame_type),
+ n_errors, n_bits_total, mode_id);
+}
+
+static void test_gsm0503_tch_afhs_decode_dtx(const sbit_t *bursts, size_t offset,
+ enum gsm0503_amr_dtx_frames *amr_last_dtx,
+ bool full_rate, const char *test_desc)
+{
+ uint8_t tch_data[128]; /* just to be safe */
+ int n_errors = 0, n_bits_total = 0;
+ int rc;
+
+ printf("Running %s(at offset=%zu): testing %s\n", __func__, offset, test_desc);
+
+ /* Dummy (not really important) values */
+ uint8_t codec[4] = { 0, 1, 2, 3 };
+ int codecs = ARRAY_SIZE(codec);
+ uint8_t ul_cmr = 0;
+ uint8_t ul_ft = 0;
+
+ if (full_rate) {
+ rc = gsm0503_tch_afs_decode_dtx(&tch_data[0], &bursts[offset], false,
+ codec, codecs, &ul_ft, &ul_cmr,
+ &n_errors, &n_bits_total,
+ (uint8_t *)amr_last_dtx);
+ } else {
+ rc = gsm0503_tch_ahs_decode_dtx(&tch_data[0], &bursts[offset], false, false,
+ codec, codecs, &ul_ft, &ul_cmr,
+ &n_errors, &n_bits_total,
+ (uint8_t *)amr_last_dtx);
+ }
+ printf(" ==> gsm0503_tch_a%cs_decode_dtx() yields '%s' (rc=%d, BER %d/%d)\n",
+ full_rate ? 'f' : 'h', gsm0503_amr_dtx_frame_name(*amr_last_dtx),
+ rc, n_errors, n_bits_total);
+ if (rc > 0)
+ printf(" ====> tch_data[] = { %s }\n", osmo_hexdump_nospc(tch_data, rc));
+}
+
+static void test_gsm0503_tch_afhs_decode_dtx_sid_update(void)
+{
+ enum gsm0503_amr_dtx_frames amr_last_dtx = AMR_OTHER;
+ sbit_t bursts[BURST_PLEN * 12]; /* 12 bursts */
+ int rc;
+
+ /* 456 soft-bits containing an AFS_SID_UPDATE frame (captured on the air) */
+ const char *afs_sid_update = \
+ "94 81 83 76 7b 81 6b 7f 76 8c 81 81 81 86 71 7f 75 81 6d 7a 81 6b 7f 78 8a 87 70 75 8e"
+ "81 8d 7f 81 70 72 81 7f 85 86 7f 93 81 8a 74 7f 71 89 8a 75 7f 7f 78 8c 81 8b 7f 81 7f"
+ "7f 7f 70 8a 8b 7f 90 81 81 81 8a 77 7f 7f 70 81 70 71 86 8e 7f 81 7f 81 75 72 87 8c 76"
+ "7f 72 8e 81 81 81 81 92 7f 8c 81 92 7f 8c 89 7f 81 7f 8f 8b 77 76 86 8c 78 73 88 81 8b"
+ "81 7f 8c 85 77 7b 8d 81 81 81 8b 7f 81 7f 8e 81 8e 7f 8a 8a 7f 93 85 6b 7f 7f 72 81 6f"
+ "76 89 81 81 81 8a 73 7f 72 88 87 73 7f 73 81 7f 81 7f 92 87 73 78 81 6f 7f 71 81 76 77"
+ "6f 81 7f 81 71 7f 6e 81 75 77 83 81 81 90 7f 8b 88 76 76 8a 8d 76 74 81 7f 92 81 81 8b"
+ "78 72 81 77 76 81 6c 7c 8b 81 81 8d 7f 8b 81 8e 74 7f 7f 72 81 7f 81 74 7f 71 81 75 7f"
+ "8e 81 81 8c 72 79 85 8c 78 75 8c 8a 7f 90 81 8e 77 77 81 70 7f 7f 71 81 7f 81 7f 8e 89"
+ "7f 8f 81 8f 7f 8c 8d 7f 81 7f 81 6f 7f 71 8a 87 7f 81 6f 77 81 7f 8d 88 73 79 8a 8a 7f"
+ "7f 7f 7f 7f 76 8b 81 8c 77 7c 8a 81 91 7f 81 76 79 81 71 7f 7f 6f 84 8e 78 7f 7f 7f 74"
+ "88 86 7b 77 81 6f 7f 7f 7f 7f 7f 75 81 70 7f 76 89 81 81 81 8d 78 74 84 81 8e 7f 8d 8a"
+ "7f 79 8c 87 7f 81 7f 81 6f 7f 75 8d 8a 7f 81 7f 92 81 81 85 76 7f 6f 8c 88 6c 7f 73 91"
+ "81 8d 71 7f 7f 73 8d 88 7f 81 7f 91 86 6f 7f 73 8e 81 8d 79 78 81 72 74 8c 86 72 7f 77"
+ "6e 81 7f 81 77 76 81 72 74 81 6f 7f 6f 8d 81 91 7f 81 6d 7f 6d 81 6c 7f 6c 81 7f 81 7f"
+ "8c 8b 7f 8e 89 74 74 8c 81 81 81 81 81 92 7f 8e 8b 7f 93 81 8f 7f 90 81 8d 74 7b 8b 89";
- string_to_ubit(ubits, string);
- dtx_frame_type = gsm0503_detect_ahs_dtx_frame(&n_errors, &n_bits_total, ubits);
- printf(" ==> %s, n_errors=%i, n_bits_total=%i\n", gsm0503_amr_dtx_frame_name(dtx_frame_type),
- n_errors, n_bits_total);
+ memset(&bursts[0], 0, sizeof(bursts));
+ rc = osmo_hexparse(afs_sid_update, (uint8_t *)&bursts[BURST_PLEN * 4], BURST_PLEN * 8);
+ OSMO_ASSERT(rc == BURST_PLEN * 4);
+
+ /* Test detection of AFS_SID_UPDATE (marker) */
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 0, &amr_last_dtx, true /* AFS */,
+ "detection of AFS_SID_UPDATE");
+
+ /* Test decoding of AFS_SID_UPDATE_CN (actual SID) */
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 4, &amr_last_dtx, true /* AFS */,
+ "decoding of AFS_SID_UPDATE");
+
+ /* 456 soft-bits containing an AHS_SID_UPDATE frame (captured on the air) */
+ const char *ahs_sid_update = \
+ "81 67 7f 7f 7f 71 8f 88 6f 73 81 7e 81 6b 7f 7e 7d 6f 8f 8a 72 76 92 81 82 81 8f 6d 6f"
+ "81 7f 92 8c 7f 97 81 8e 6f 7f 7c 7f 6e 81 7e 81 6e 73 81 7f 93 8d 6f 7f 6c 81 6b 7f 72"
+ "7c 7c 7d 7f 6f 8f 81 94 7f 92 8d 6e 7d 7d 7f 6c 8b 8e 73 71 81 7f 92 90 7f 81 6e 6e 81"
+ "7f 94 8e 70 7f 6e 8c 8d 77 7f 6a 81 7f 81 70 6d 81 6c 71 8c 91 7f 90 8e 73 6e 81 6d 7f"
+ "81 8b 71 6e 81 7f 82 7c 81 7f 81 6d 73 81 6c 6d 81 6d 7f 6e 81 7e 81 6b 7f 7f 7f 6b 81"
+ "6e 6f 81 68 7f 71 91 81 82 81 8e 70 7f 7c 7d 7f 70 81 7f 91 8f 7f 81 6c 7f 71 81 6d 74"
+ "6f 8f 81 92 7f 82 7f 91 8b 7f 81 6b 7f 6d 81 6b 6f 81 6f 6e 90 81 81 92 7f 94 81 95 7f"
+ "96 81 96 70 7f 72 8f 81 95 7f 81 6f 70 81 7f 90 92 7f 81 6c 70 81 6b 7f 6f 8d 8d 7f 81"
+ "77 81 6a 7e 7e 73 92 8c 7f 81 6a 7f 6c 8e 8e 6e 7f 71 8e 8d 7e 81 6d 7f 6c 81 6d 6c 81"
+ "7f 94 81 92 7f 97 81 92 6e 7f 70 8c 8b 73 73 91 81 93 7f 81 70 72 81 7d 81 71 70 81 7f"
+ "7d 7f 6d 90 8d 73 76 92 81 92 6f 7d 7d 70 91 81 8f 73 75 8c 90 7f 94 81 91 70 7f 7d 7e"
+ "70 8d 8d 73 7f 7c 7e 6a 81 7e 81 6d 7f 6a 81 6f 7f 7f 71 8e 81 82 81 81 81 96 72 7e 7d"
+ "81 8d 7f 81 68 7f 7e 7c 7b 7f 6c 81 6a 7f 7f 71 8f 8d 7f 81 6c 72 8e 88 70 70 81 6d 70"
+ "8d 90 7f 81 7e 95 81 94 7f 92 8b 6e 7f 7f 70 8c 8c 73 75 91 81 91 6d 7d 7e 7b 7c 7d 71"
+ "6c 89 91 7f 81 7f 95 81 93 7f 95 90 7f 81 6d 70 81 6f 75 8c 8e 75 71 81 6e 70 8d 8d 7f"
+ "91 92 7f 81 7f 94 8d 70 71 81 6e 6d 81 6e 75 8e 81 93 70 7f 70 8f 8c 7f 81 6d 6f 81 6a";
+
+ memset(&bursts[0], 0, sizeof(bursts));
+ rc = osmo_hexparse(ahs_sid_update, (uint8_t *)&bursts[BURST_PLEN * 2], BURST_PLEN * 10);
+ OSMO_ASSERT(rc == BURST_PLEN * 4);
+
+ /* Test detection and decoding of AHS_SID_UPDATE */
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 0, &amr_last_dtx, false /* AHS */,
+ "detection/decoding of AHS_SID_UPDATE");
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 2, &amr_last_dtx, false /* AHS */,
+ "detection/decoding of AHS_SID_UPDATE");
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 4, &amr_last_dtx, false /* AHS */,
+ "detection/decoding of AHS_SID_UPDATE");
+}
+
+static void test_gsm0503_tch_afhs_decode_dtx_facch(void)
+{
+ enum gsm0503_amr_dtx_frames amr_last_dtx;
+ sbit_t bursts[BURST_PLEN * 8]; /* 8 bursts */
+ unsigned int i;
+
+ /* Set stealing bits to provoke FACCH/[FH] detection */
+ for (i = 0; i < 8; i++) {
+ sbit_t *burst = &bursts[BURST_PLEN * i];
+ memset(&burst[0], 0, BURST_PLEN);
+ burst[i >> 2 ? 57 : 58] = -127;
+ }
+
+ amr_last_dtx = AFS_SID_UPDATE;
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 0,
+ &amr_last_dtx, true /* AFS */,
+ "tagging of FACCH/F");
+ OSMO_ASSERT(amr_last_dtx == AMR_OTHER);
+
+ amr_last_dtx = AHS_SID_UPDATE;
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 0,
+ &amr_last_dtx, false /* AHS */,
+ "tagging of FACCH/H");
+ OSMO_ASSERT(amr_last_dtx == AMR_OTHER);
}
int main(int argc, char **argv)
@@ -126,5 +259,8 @@ int main(int argc, char **argv)
test_gsm0503_detect_ahs_dtx_frame(sample_sid_first_inh_frame);
test_gsm0503_detect_ahs_dtx_frame(sample_sid_update_inh_frame);
+ test_gsm0503_tch_afhs_decode_dtx_sid_update();
+ test_gsm0503_tch_afhs_decode_dtx_facch();
+
return EXIT_SUCCESS;
}
diff --git a/tests/dtx/dtx_gsm0503_test.ok b/tests/dtx/dtx_gsm0503_test.ok
index 77a49360..477d4312 100644
--- a/tests/dtx/dtx_gsm0503_test.ok
+++ b/tests/dtx/dtx_gsm0503_test.ok
@@ -1,11 +1,27 @@
FR AMR DTX FRAMES:
- ==> AFS_SID_FIRST, n_errors=0, n_bits_total=212
- ==> AFS_SID_UPDATE (marker), n_errors=0, n_bits_total=212
- ==> AFS_ONSET, n_errors=0, n_bits_total=228
+ ==> AFS_SID_FIRST, n_errors=0, n_bits_total=212, mode_id=-1
+ ==> AFS_SID_UPDATE (marker), n_errors=0, n_bits_total=212, mode_id=-1
+ ==> AFS_ONSET, n_errors=0, n_bits_total=228, mode_id=0
HR AMR DTX FRAMES:
- ==> AHS_SID_UPDATE (marker), n_errors=0, n_bits_total=212
- ==> AHS_SID_FIRST_P1, n_errors=0, n_bits_total=212
- ==> AHS_SID_FIRST_P2, n_errors=0, n_bits_total=114
- ==> AHS_ONSET, n_errors=0, n_bits_total=114
- ==> AHS_SID_FIRST_INH, n_errors=0, n_bits_total=212
- ==> AHS_SID_UPDATE_INH, n_errors=0, n_bits_total=212
+ ==> AHS_SID_UPDATE (marker), n_errors=0, n_bits_total=212, mode_id=-1
+ ==> AHS_SID_FIRST_P1, n_errors=0, n_bits_total=212, mode_id=-1
+ ==> AHS_SID_FIRST_P2, n_errors=0, n_bits_total=114, mode_id=0
+ ==> AHS_ONSET, n_errors=0, n_bits_total=114, mode_id=0
+ ==> AHS_SID_FIRST_INH, n_errors=0, n_bits_total=212, mode_id=-1
+ ==> AHS_SID_UPDATE_INH, n_errors=0, n_bits_total=212, mode_id=-1
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=0): testing detection of AFS_SID_UPDATE
+ ==> gsm0503_tch_afs_decode_dtx() yields 'AFS_SID_UPDATE (marker)' (rc=0, BER 0/212)
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=464): testing decoding of AFS_SID_UPDATE
+ ==> gsm0503_tch_afs_decode_dtx() yields 'AFS_SID_UPDATE_CN (audio)' (rc=5, BER 0/212)
+ ====> tch_data[] = { 26e9b1d2b0 }
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=0): testing detection/decoding of AHS_SID_UPDATE
+ ==> gsm0503_tch_ahs_decode_dtx() yields 'AMR_OTHER (audio)' (rc=-1, BER 109/212)
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=232): testing detection/decoding of AHS_SID_UPDATE
+ ==> gsm0503_tch_ahs_decode_dtx() yields 'AHS_SID_UPDATE_CN (audio)' (rc=5, BER 0/424)
+ ====> tch_data[] = { 6003ccb270 }
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=464): testing detection/decoding of AHS_SID_UPDATE
+ ==> gsm0503_tch_ahs_decode_dtx() yields 'AMR_OTHER (audio)' (rc=-1, BER 111/212)
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=0): testing tagging of FACCH/F
+ ==> gsm0503_tch_afs_decode_dtx() yields 'AMR_OTHER (audio)' (rc=-1, BER 456/456)
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=0): testing tagging of FACCH/H
+ ==> gsm0503_tch_ahs_decode_dtx() yields 'AMR_OTHER (audio)' (rc=-1, BER 456/456)
diff --git a/tests/fr/fr_test.c b/tests/fr/fr_test.c
index 4d472b55..cdcdb434 100644
--- a/tests/fr/fr_test.c
+++ b/tests/fr/fr_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#define _GNU_SOURCE
@@ -56,7 +52,7 @@ int socket(int domain, int type, int protocol)
return fd;
}
-void bssgp_prim_cb()
+void bssgp_prim_cb(void)
{
}
diff --git a/tests/fsm/fsm_dealloc_test.c b/tests/fsm/fsm_dealloc_test.c
index f4be93dd..246ba563 100644
--- a/tests/fsm/fsm_dealloc_test.c
+++ b/tests/fsm/fsm_dealloc_test.c
@@ -290,7 +290,7 @@ void obj_set_other(struct obj *a, struct obj *b)
obj_add_other(b, a);
}
-static struct scene *scene_alloc()
+static struct scene *scene_alloc(void)
{
struct scene *s = talloc_zero(ctx, struct scene);
s->use_count.talloc_object = s;
@@ -419,7 +419,7 @@ static void trigger_tests(void *loop_ctx)
}
}
-void test_osmo_fsm_term_safely()
+void test_osmo_fsm_term_safely(void)
{
fprintf(stderr, "\n\n%s()\n", __func__);
osmo_fsm_term_safely(true);
@@ -428,7 +428,7 @@ void test_osmo_fsm_term_safely()
fprintf(stderr, "\n\n%s() done\n", __func__);
}
-void test_osmo_fsm_set_dealloc_ctx()
+void test_osmo_fsm_set_dealloc_ctx(void)
{
fprintf(stderr, "\n\n%s()\n", __func__);
void *dealloc_ctx = talloc_named_const(ctx, 0, "fsm_dealloc");
diff --git a/tests/fsm/fsm_test.c b/tests/fsm/fsm_test.c
index 2cfdacd2..3b839412 100644
--- a/tests/fsm/fsm_test.c
+++ b/tests/fsm/fsm_test.c
@@ -41,6 +41,18 @@ static const struct value_string test_fsm_event_names[] = {
{ 0, NULL }
};
+static void test_fsm_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ LOGPFSM(fi, "%s() prev_state=%s\n",
+ __func__, osmo_fsm_state_name(fi->fsm, prev_state));
+}
+
+static void test_fsm_onleave(struct osmo_fsm_inst *fi, uint32_t next_state)
+{
+ LOGPFSM(fi, "%s() next_state=%s\n",
+ __func__, osmo_fsm_state_name(fi->fsm, next_state));
+}
+
static void test_fsm_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
@@ -86,17 +98,23 @@ static struct osmo_fsm_state test_fsm_states[] = {
.out_state_mask = (1 << ST_ONE),
.name = "NULL",
.action = test_fsm_null,
+ .onenter = test_fsm_onenter,
+ .onleave = test_fsm_onleave,
},
[ST_ONE]= {
.in_event_mask = (1 << EV_B),
.out_state_mask = (1 << ST_TWO),
.name = "ONE",
.action= test_fsm_one,
+ .onenter = test_fsm_onenter,
+ .onleave = test_fsm_onleave,
},
[ST_TWO]= {
.in_event_mask = 0,
.name = "TWO",
.action = NULL,
+ .onenter = test_fsm_onenter,
+ .onleave = test_fsm_onleave,
},
};
@@ -172,7 +190,7 @@ static struct osmo_fsm_inst *foo(void)
return fi;
}
-static void test_id_api()
+static void test_id_api(void)
{
struct osmo_fsm_inst *fi;
@@ -280,7 +298,7 @@ const struct timeval fake_time_start_time = { 123, 456 };
osmo_timers_update(); \
} while (0)
-void fake_time_start()
+void fake_time_start(void)
{
struct timespec *clock_override;
@@ -301,7 +319,7 @@ static int timer_cb(struct osmo_fsm_inst *fi)
return 0;
}
-static void test_state_chg_keep_timer()
+static void test_state_chg_keep_timer(void)
{
struct osmo_fsm_inst *fi;
@@ -349,7 +367,7 @@ static void test_state_chg_keep_timer()
fprintf(stderr, "--- %s() done\n", __func__);
}
-static void test_state_chg_T()
+static void test_state_chg_T(void)
{
struct osmo_fsm_inst *fi;
@@ -386,6 +404,74 @@ static void test_state_chg_T()
fprintf(stderr, "--- %s() done\n", __func__);
}
+/* Test setting a state timeout with second granularity */
+static void test_state_chg_Ts(void)
+{
+ struct osmo_fsm_inst *fi;
+
+ fprintf(stderr, "\n--- %s()\n", __func__);
+
+ fsm.timer_cb = &timer_cb;
+ timeout_fired = -1;
+ fake_time_start();
+
+ fi = osmo_fsm_inst_alloc(&fsm, g_ctx, NULL, LOGL_DEBUG, NULL);
+ OSMO_ASSERT(fi);
+
+ osmo_fsm_inst_state_chg(fi, ST_ONE, 8, 4242);
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(3, 0); /* +3s */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(2, 500000); /* +2.5s */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(2, 500000); /* +2.5s */
+ OSMO_ASSERT(timeout_fired == 4242);
+
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, NULL);
+
+ fprintf(stderr, "--- %s() done\n", __func__);
+}
+
+/* Test setting a state timeout with millisecond granularity */
+static void test_state_chg_Tms(void)
+{
+ struct osmo_fsm_inst *fi;
+
+ fprintf(stderr, "\n--- %s()\n", __func__);
+
+ fsm.timer_cb = &timer_cb;
+ timeout_fired = -1;
+ fake_time_start();
+
+ fi = osmo_fsm_inst_alloc(&fsm, g_ctx, NULL, LOGL_DEBUG, NULL);
+ OSMO_ASSERT(fi);
+
+ osmo_fsm_inst_state_chg_ms(fi, ST_ONE, 1337, 4242); /* 1s 337ms */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(0, 500000); /* +500ms, 500ms total */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(0, 250000); /* +250ms, 750ms total */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(0, 350000); /* +350ms, 1s 100ms total */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(0, 200000); /* +200ms, 1s 300ms total */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(0, 37000); /* +37ms, 1s 337ms total */
+ OSMO_ASSERT(timeout_fired == 4242);
+
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, NULL);
+
+ fprintf(stderr, "--- %s() done\n", __func__);
+}
+
static const struct log_info_cat default_categories[] = {
[DMAIN] = {
.name = "DMAIN",
@@ -434,6 +520,8 @@ int main(int argc, char **argv)
test_id_api();
test_state_chg_keep_timer();
test_state_chg_T();
+ test_state_chg_Ts();
+ test_state_chg_Tms();
osmo_fsm_unregister(&fsm);
exit(0);
diff --git a/tests/fsm/fsm_test.err b/tests/fsm/fsm_test.err
index 4cc5ca40..fffb9136 100644
--- a/tests/fsm/fsm_test.err
+++ b/tests/fsm/fsm_test.err
@@ -3,9 +3,13 @@ Test_FSM(my_id){NULL}: Allocated
Test_FSM(my_id){NULL}: Received Event EV_B
Test_FSM(my_id){NULL}: Event EV_B not permitted
Test_FSM(my_id){NULL}: Received Event EV_A
+Test_FSM(my_id){NULL}: test_fsm_onleave() next_state=ONE
Test_FSM(my_id){NULL}: State change to ONE (no timeout)
+Test_FSM(my_id){ONE}: test_fsm_onenter() prev_state=NULL
Test_FSM(my_id){ONE}: Received Event EV_B
+Test_FSM(my_id){ONE}: test_fsm_onleave() next_state=TWO
Test_FSM(my_id){ONE}: State change to TWO (T2342, 1s)
+Test_FSM(my_id){TWO}: test_fsm_onenter() prev_state=ONE
Test_FSM(my_id){TWO}: Timeout of T2342
Timer
Test_FSM(my_id){TWO}: Deallocated
@@ -83,16 +87,24 @@ Test_FSM(arbitrary_id){NULL}: Deallocated
--- test_state_chg_keep_timer()
Test_FSM{NULL}: Allocated
+Test_FSM{NULL}: test_fsm_onleave() next_state=ONE
Test_FSM{NULL}: State change to ONE (no timeout)
+Test_FSM{ONE}: test_fsm_onenter() prev_state=NULL
+Test_FSM{ONE}: test_fsm_onleave() next_state=TWO
Test_FSM{ONE}: State change to TWO (no timeout)
+Test_FSM{TWO}: test_fsm_onenter() prev_state=ONE
Test_FSM{TWO}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
Test_FSM{TWO}: Freeing instance
Test_FSM{TWO}: Deallocated
Total time passed: 0.000000 s
Test_FSM{NULL}: Allocated
+Test_FSM{NULL}: test_fsm_onleave() next_state=ONE
Test_FSM{NULL}: State change to ONE (T10, 10s)
+Test_FSM{ONE}: test_fsm_onenter() prev_state=NULL
Total time passed: 2.000342 s
+Test_FSM{ONE}: test_fsm_onleave() next_state=TWO
Test_FSM{ONE}: State change to TWO (keeping T10, 7.999s remaining)
+Test_FSM{TWO}: test_fsm_onenter() prev_state=ONE
Total time passed: 2.000342 s
Total time passed: 9.999999 s
Total time passed: 10.000000 s
@@ -104,15 +116,55 @@ Test_FSM{TWO}: Deallocated
--- test_state_chg_T()
Test_FSM{NULL}: Allocated
+Test_FSM{NULL}: test_fsm_onleave() next_state=ONE
Test_FSM{NULL}: State change to ONE (T42, 23s)
+Test_FSM{ONE}: test_fsm_onenter() prev_state=NULL
+Test_FSM{ONE}: test_fsm_onleave() next_state=TWO
Test_FSM{ONE}: State change to TWO (no timeout)
+Test_FSM{TWO}: test_fsm_onenter() prev_state=ONE
Test_FSM{TWO}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
Test_FSM{TWO}: Freeing instance
Test_FSM{TWO}: Deallocated
Test_FSM{NULL}: Allocated
+Test_FSM{NULL}: test_fsm_onleave() next_state=ONE
Test_FSM{NULL}: State change to ONE (T42, 23s)
+Test_FSM{ONE}: test_fsm_onenter() prev_state=NULL
+Test_FSM{ONE}: test_fsm_onleave() next_state=TWO
Test_FSM{ONE}: State change to TWO (no timeout)
+Test_FSM{TWO}: test_fsm_onenter() prev_state=ONE
Test_FSM{TWO}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
Test_FSM{TWO}: Freeing instance
Test_FSM{TWO}: Deallocated
--- test_state_chg_T() done
+
+--- test_state_chg_Ts()
+Total time passed: 0.000000 s
+Test_FSM{NULL}: Allocated
+Test_FSM{NULL}: test_fsm_onleave() next_state=ONE
+Test_FSM{NULL}: State change to ONE (T4242, 8s)
+Test_FSM{ONE}: test_fsm_onenter() prev_state=NULL
+Total time passed: 3.000000 s
+Total time passed: 5.500000 s
+Total time passed: 8.000000 s
+Test_FSM{ONE}: Timeout of T4242
+Test_FSM{ONE}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
+Test_FSM{ONE}: Freeing instance
+Test_FSM{ONE}: Deallocated
+--- test_state_chg_Ts() done
+
+--- test_state_chg_Tms()
+Total time passed: 0.000000 s
+Test_FSM{NULL}: Allocated
+Test_FSM{NULL}: test_fsm_onleave() next_state=ONE
+Test_FSM{NULL}: State change to ONE (T4242, 1337ms)
+Test_FSM{ONE}: test_fsm_onenter() prev_state=NULL
+Total time passed: 0.500000 s
+Total time passed: 0.750000 s
+Total time passed: 1.100000 s
+Total time passed: 1.300000 s
+Total time passed: 1.337000 s
+Test_FSM{ONE}: Timeout of T4242
+Test_FSM{ONE}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
+Test_FSM{ONE}: Freeing instance
+Test_FSM{ONE}: Deallocated
+--- test_state_chg_Tms() done
diff --git a/tests/gad/gad_test.c b/tests/gad/gad_test.c
index ca8c4fa8..19be6a61 100644
--- a/tests/gad/gad_test.c
+++ b/tests/gad/gad_test.c
@@ -4,7 +4,7 @@
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/gad.h>
-void test_gad_lat_lon_dec_enc_stability()
+void test_gad_lat_lon_dec_enc_stability(void)
{
uint32_t lat_enc;
uint32_t lon_enc;
@@ -49,7 +49,7 @@ struct osmo_gad gad_test_values[] = {
},
};
-void test_gad_enc_dec()
+void test_gad_enc_dec(void)
{
int i;
printf("--- %s\n", __func__);
@@ -115,7 +115,7 @@ loop_end:
}
}
-void test_gad_to_str()
+void test_gad_to_str(void)
{
int i;
printf("--- %s\n", __func__);
@@ -134,7 +134,7 @@ void test_gad_to_str()
}
}
-int main()
+int main(int argc, char **argv)
{
test_gad_lat_lon_dec_enc_stability();
test_gad_enc_dec();
diff --git a/tests/gb/gprs_bssgp_rim_test.c b/tests/gb/gprs_bssgp_rim_test.c
index e2d4f661..bb533f6d 100644
--- a/tests/gb/gprs_bssgp_rim_test.c
+++ b/tests/gb/gprs_bssgp_rim_test.c
@@ -60,7 +60,7 @@ void dump_rim_ri(struct bssgp_rim_routing_info *ri)
}
}
-static void test_bssgp_parse_rim_ri()
+static void test_bssgp_parse_rim_ri(void)
{
int rc;
struct bssgp_rim_routing_info result;
@@ -94,7 +94,7 @@ static void test_bssgp_parse_rim_ri()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_create_rim_ri()
+static void test_bssgp_create_rim_ri(void)
{
int rc;
struct bssgp_rim_routing_info ri;
@@ -194,7 +194,7 @@ void dump_bssgp_ran_inf_req_rim_cont(struct bssgp_ran_inf_req_rim_cont *rim_cont
}
}
-static void test_bssgp_dec_ran_inf_req_rim_cont_nacc()
+static void test_bssgp_dec_ran_inf_req_rim_cont_nacc(void)
{
int rc;
struct bssgp_ran_inf_req_rim_cont rim_cont_dec;
@@ -212,7 +212,7 @@ static void test_bssgp_dec_ran_inf_req_rim_cont_nacc()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_enc_ran_inf_req_rim_cont_nacc()
+static void test_bssgp_enc_ran_inf_req_rim_cont_nacc(void)
{
int rc;
struct bssgp_ran_inf_req_rim_cont rim_cont = { };
@@ -321,7 +321,7 @@ static void dump_bssgp_ran_inf_rim_cont(struct bssgp_ran_inf_rim_cont *rim_cont)
}
}
-static void test_bssgp_dec_ran_inf_rim_cont_nacc()
+static void test_bssgp_dec_ran_inf_rim_cont_nacc(void)
{
int rc;
struct bssgp_ran_inf_rim_cont rim_cont_dec;
@@ -346,7 +346,7 @@ static void test_bssgp_dec_ran_inf_rim_cont_nacc()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_dec_ran_inf_rim_cont_err_nacc()
+static void test_bssgp_dec_ran_inf_rim_cont_err_nacc(void)
{
int rc;
struct bssgp_ran_inf_rim_cont rim_cont_dec;
@@ -364,7 +364,7 @@ static void test_bssgp_dec_ran_inf_rim_cont_err_nacc()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_enc_ran_inf_rim_cont_nacc()
+static void test_bssgp_enc_ran_inf_rim_cont_nacc(void)
{
int rc;
struct bssgp_ran_inf_rim_cont rim_cont = { };
@@ -415,7 +415,7 @@ static void test_bssgp_enc_ran_inf_rim_cont_nacc()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_enc_ran_inf_rim_cont_err_nacc()
+static void test_bssgp_enc_ran_inf_rim_cont_err_nacc(void)
{
int rc;
struct bssgp_ran_inf_rim_cont rim_cont = { };
@@ -458,7 +458,7 @@ static void dump_bssgp_ran_inf_ack_rim_cont(struct bssgp_ran_inf_ack_rim_cont *r
}
}
-static void test_bssgp_dec_ran_inf_ack_rim_cont()
+static void test_bssgp_dec_ran_inf_ack_rim_cont(void)
{
int rc;
struct bssgp_ran_inf_ack_rim_cont rim_cont_dec;
@@ -474,7 +474,7 @@ static void test_bssgp_dec_ran_inf_ack_rim_cont()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_enc_ran_inf_ack_rim_cont()
+static void test_bssgp_enc_ran_inf_ack_rim_cont(void)
{
int rc;
struct bssgp_ran_inf_ack_rim_cont rim_cont = { };
@@ -513,7 +513,7 @@ void dump_bssgp_ran_inf_err_rim_cont(struct bssgp_ran_inf_err_rim_cont *rim_cont
}
}
-static void test_bssgp_dec_ran_inf_err_rim_cont()
+static void test_bssgp_dec_ran_inf_err_rim_cont(void)
{
int rc;
struct bssgp_ran_inf_err_rim_cont rim_cont_dec;
@@ -530,7 +530,7 @@ static void test_bssgp_dec_ran_inf_err_rim_cont()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_enc_ran_inf_err_rim_cont()
+static void test_bssgp_enc_ran_inf_err_rim_cont(void)
{
int rc;
struct bssgp_ran_inf_err_rim_cont rim_cont = { };
@@ -578,7 +578,7 @@ void dump_bssgp_ran_inf_app_err_rim_cont(struct bssgp_ran_inf_app_err_rim_cont *
}
}
-static void test_bssgp_dec_ran_inf_app_err_rim_cont_nacc()
+static void test_bssgp_dec_ran_inf_app_err_rim_cont_nacc(void)
{
int rc;
struct bssgp_ran_inf_app_err_rim_cont rim_cont_dec;
@@ -597,7 +597,7 @@ static void test_bssgp_dec_ran_inf_app_err_rim_cont_nacc()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_enc_ran_inf_app_err_rim_cont_nacc()
+static void test_bssgp_enc_ran_inf_app_err_rim_cont_nacc(void)
{
int rc;
struct bssgp_ran_inf_app_err_rim_cont rim_cont = { };
@@ -623,7 +623,7 @@ static void test_bssgp_enc_ran_inf_app_err_rim_cont_nacc()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_dec_ran_inf_req_app_cont_nacc()
+static void test_bssgp_dec_ran_inf_req_app_cont_nacc(void)
{
int rc;
struct bssgp_ran_inf_req_app_cont_nacc app_cont_dec;
@@ -639,7 +639,7 @@ static void test_bssgp_dec_ran_inf_req_app_cont_nacc()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_enc_ran_inf_req_app_cont_nacc()
+static void test_bssgp_enc_ran_inf_req_app_cont_nacc(void)
{
int rc;
struct bssgp_ran_inf_req_app_cont_nacc app_cont = { };
@@ -662,7 +662,7 @@ static void test_bssgp_enc_ran_inf_req_app_cont_nacc()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_dec_ran_inf_app_cont_nacc()
+static void test_bssgp_dec_ran_inf_app_cont_nacc(void)
{
int rc;
struct bssgp_ran_inf_app_cont_nacc app_cont_dec;
@@ -682,7 +682,7 @@ static void test_bssgp_dec_ran_inf_app_cont_nacc()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_enc_ran_inf_app_cont_nacc()
+static void test_bssgp_enc_ran_inf_app_cont_nacc(void)
{
int rc;
struct bssgp_ran_inf_app_cont_nacc app_cont = { };
@@ -721,7 +721,7 @@ static void test_bssgp_enc_ran_inf_app_cont_nacc()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_dec_app_err_cont_nacc()
+static void test_bssgp_dec_app_err_cont_nacc(void)
{
int rc;
struct bssgp_app_err_cont_nacc app_cont_dec;
@@ -737,7 +737,7 @@ static void test_bssgp_dec_app_err_cont_nacc()
printf("----- %s END\n", __func__);
}
-static void test_bssgp_enc_app_err_cont_nacc()
+static void test_bssgp_enc_app_err_cont_nacc(void)
{
int rc;
struct bssgp_app_err_cont_nacc app_cont = { };
diff --git a/tests/gb/gprs_bssgp_test.c b/tests/gb/gprs_bssgp_test.c
index 43fc056c..775ae43b 100644
--- a/tests/gb/gprs_bssgp_test.c
+++ b/tests/gb/gprs_bssgp_test.c
@@ -173,7 +173,7 @@ static void test_bssgp_status(void)
printf("----- %s END\n", __func__);
}
-static void test_bssgp_bad_reset()
+static void test_bssgp_bad_reset(void)
{
struct msgb *msg;
uint16_t bvci_be = htons(2);
@@ -256,7 +256,7 @@ static void test_bssgp_flow_control_bvc(void)
printf("----- %s END\n", __func__);
}
-static void test_bssgp_msgb_copy()
+static void test_bssgp_msgb_copy(void)
{
struct msgb *msg, *msg2;
uint16_t bvci_be = htons(2);
diff --git a/tests/gb/gprs_ns2_test.c b/tests/gb/gprs_ns2_test.c
index cc1b2f1f..0221a8d6 100644
--- a/tests/gb/gprs_ns2_test.c
+++ b/tests/gb/gprs_ns2_test.c
@@ -41,6 +41,7 @@ int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
static struct log_info info = {};
static struct osmo_wqueue *unitdata = NULL;
static struct osmo_gprs_ns2_prim last_nse_recovery = {};
+static struct osmo_gprs_ns2_prim last_nse_mtu_change = {};
static int ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
@@ -54,8 +55,12 @@ static int ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
msgb_free(oph->msg);
}
}
- if (oph->primitive == GPRS_NS2_PRIM_STATUS && nsp->u.status.cause == GPRS_NS2_AFF_CAUSE_RECOVERY) {
- last_nse_recovery = *nsp;
+ if (oph->primitive == GPRS_NS2_PRIM_STATUS) {
+ if (nsp->u.status.cause == GPRS_NS2_AFF_CAUSE_RECOVERY) {
+ last_nse_recovery = *nsp;
+ } else if (nsp->u.status.cause == GPRS_NS2_AFF_CAUSE_MTU_CHANGE) {
+ last_nse_mtu_change = *nsp;
+ }
}
return 0;
}
@@ -302,6 +307,72 @@ void test_block_unblock_nsvc(void *ctx)
printf("--- Finish NSE block unblock nsvc\n");
}
+/* setup NSE with 2x NSVCs.
+ * block 1st NSVC
+ * block 2nd NSVC
+ * unblock 1st NSVC */
+void test_block_unblock_nsvc2(void *ctx)
+{
+ struct gprs_ns2_inst *nsi;
+ struct gprs_ns2_vc_bind *bind[2];
+ struct gprs_ns2_nse *nse;
+ struct gprs_ns2_vc *nsvc[2];
+ struct gprs_ns_hdr *nsh;
+ struct msgb *msg;
+ char idbuf[32];
+ int i;
+
+ printf("--- Testing NSE block unblock nsvc2\n");
+ printf("---- Create NSE + Binds\n");
+ nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
+ bind[0] = dummy_bind(nsi, "bblock1");
+ bind[1] = dummy_bind(nsi, "bblock2");
+ nse = gprs_ns2_create_nse(nsi, 1001, GPRS_NS2_LL_UDP, GPRS_NS2_DIALECT_STATIC_RESETBLOCK);
+ OSMO_ASSERT(nse);
+
+ for (i=0; i<2; i++) {
+ printf("---- Create NSVC[i]\n");
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-dummy-%i", nse->nsei, i);
+ nsvc[i] = ns2_vc_alloc(bind[i], nse, false, GPRS_NS2_VC_MODE_BLOCKRESET, idbuf);
+ OSMO_ASSERT(nsvc[i]);
+ nsvc[i]->fi->state = 3; /* HACK: 3 = GPRS_NS2_ST_UNBLOCKED */
+ /* ensure the fi->state works correct */
+ OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[i]));
+ ns2_nse_notify_unblocked(nsvc[i], true);
+ }
+
+ OSMO_ASSERT(nse->alive);
+ /* both nsvcs are unblocked and alive. Let's block them. */
+ OSMO_ASSERT(!find_pdu(bind[0], NS_PDUT_BLOCK));
+ clear_pdus(bind[0]);
+ ns2_vc_block(nsvc[0]);
+ OSMO_ASSERT(find_pdu(bind[0], NS_PDUT_BLOCK));
+ clear_pdus(bind[0]);
+ OSMO_ASSERT(nse->alive);
+
+ OSMO_ASSERT(!find_pdu(bind[1], NS_PDUT_BLOCK));
+ clear_pdus(bind[1]);
+ ns2_vc_block(nsvc[1]);
+ OSMO_ASSERT(find_pdu(bind[1], NS_PDUT_BLOCK));
+ clear_pdus(bind[1]);
+ OSMO_ASSERT(!nse->alive);
+
+ /* now unblocking the 1st NSVC */
+ ns2_vc_unblock(nsvc[0]);
+ OSMO_ASSERT(find_pdu(bind[0], NS_PDUT_UNBLOCK));
+ clear_pdus(bind[0]);
+ msg = msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM, "test_unblock");
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = NS_PDUT_UNBLOCK_ACK;
+ ns2_recv_vc(nsvc[0], msg);
+ OSMO_ASSERT(nse->alive);
+
+ OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[0]));
+ gprs_ns2_free(nsi);
+ printf("--- Finish NSE block unblock nsvc2\n");
+}
+
static struct msgb *generate_unitdata(const char *msgname)
{
struct gprs_ns_hdr *nsh;
@@ -368,7 +439,7 @@ void test_unitdata(void *ctx)
printf("---- Send Block NSVC[0]\n");
ns2_vc_block(nsvc[0]);
- ns2_tx_block_ack(loop[0]);
+ ns2_tx_block_ack(loop[0], NULL);
/* try to receive a unitdata - this should be dropped & freed by NS */
printf("---- Try to receive over blocked NSVC[0]\n");
@@ -513,10 +584,64 @@ void test_mtu(void *ctx)
/* 1b NS PDU type, 1b NS SDU control, 2b BVCI */
OSMO_ASSERT(last_nse_recovery.u.status.mtu == 123 - 4);
+ bind[0]->mtu = 100;
+ ns2_nse_update_mtu(nse);
+ OSMO_ASSERT(last_nse_mtu_change.u.status.mtu == 100 - 4);
+
gprs_ns2_free(nsi);
printf("--- Finish unitdata test\n");
}
+void test_unconfigured(void *ctx)
+{
+ struct gprs_ns2_inst *nsi;
+ struct gprs_ns2_vc_bind *bind[2];
+ struct gprs_ns2_vc_bind *loopbind;
+ struct gprs_ns2_nse *nse;
+ struct gprs_ns2_vc *nsvc[2];
+ struct gprs_ns2_vc *loop[2];
+
+ char idbuf[32];
+ int i;
+
+ printf("--- Testing force unconfigured\n");
+ osmo_wqueue_clear(unitdata);
+ printf("---- Create NSE + Binds\n");
+ nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
+ bind[0] = dummy_bind(nsi, "bblock1");
+ bind[1] = dummy_bind(nsi, "bblock2");
+ loopbind = loopback_bind(nsi, "loopback");
+ nse = gprs_ns2_create_nse(nsi, 1004, GPRS_NS2_LL_UDP, GPRS_NS2_DIALECT_STATIC_RESETBLOCK);
+ OSMO_ASSERT(nse);
+
+ for (i=0; i<2; i++) {
+ printf("---- Create NSVC[%d]\n", i);
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-dummy-%i", nse->nsei, i);
+ nsvc[i] = ns2_vc_alloc(bind[i], nse, false, GPRS_NS2_VC_MODE_BLOCKRESET, idbuf);
+ loop[i] = loopback_nsvc(loopbind, nsvc[i]);
+ OSMO_ASSERT(nsvc[i]);
+ ns2_vc_fsm_start(nsvc[i]);
+ OSMO_ASSERT(!ns2_vc_is_unblocked(nsvc[i]));
+ ns2_tx_reset(loop[i], NS_CAUSE_OM_INTERVENTION);
+ ns2_tx_unblock(loop[i]);
+ OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[i]));
+ }
+
+ /* both nsvcs are unblocked and alive */
+ printf("---- Check if NSE is alive\n");
+ OSMO_ASSERT(nse->alive);
+
+ ns2_vc_force_unconfigured(nsvc[0]);
+ OSMO_ASSERT(nse->alive);
+
+ ns2_vc_force_unconfigured(nsvc[1]);
+ printf("---- Check if NSE is dead\n");
+ OSMO_ASSERT(!nse->alive);
+
+ gprs_ns2_free(nsi);
+ printf("--- Finish force unconfigured test\n");
+}
+
int main(int argc, char **argv)
{
void *ctx = talloc_named_const(NULL, 0, "gprs_ns2_test");
@@ -533,8 +658,10 @@ int main(int argc, char **argv)
printf("===== NS2 protocol test START\n");
test_nse_transfer_cap(ctx);
test_block_unblock_nsvc(ctx);
+ test_block_unblock_nsvc2(ctx);
test_unitdata(ctx);
test_unitdata_weights(ctx);
+ test_unconfigured(ctx);
test_mtu(ctx);
printf("===== NS2 protocol test END\n\n");
diff --git a/tests/gb/gprs_ns2_test.err b/tests/gb/gprs_ns2_test.err
new file mode 100644
index 00000000..5ed62dba
--- /dev/null
+++ b/tests/gb/gprs_ns2_test.err
@@ -0,0 +1,139 @@
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01001)-NSVC(none) Tx NS-BLOCK cause=O&M intervention
+NSE(01001)-NSVC(none) Tx NS-ALIVE
+NSE(01001)-NSVC(none) Tx NS-UNBLOCK
+NSE(01001)-NSVC(none) Rx NS-UNBLOCK-ACK
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=84, first=0, mtu=119
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01001)-NSVC(none) Tx NS-BLOCK cause=O&M intervention
+NSE(01001)-NSVC(none) Tx NS-ALIVE
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) Tx NS-BLOCK cause=O&M intervention
+NSE(01001)-NSVC(none) Tx NS-ALIVE
+NSE(01001)-NSVC(none) Tx NS-UNBLOCK
+NSE(01001)-NSVC(none) Rx NS-UNBLOCK-ACK
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=42, first=0, mtu=119
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-RESET cause=O&M intervention
+NSE(01004)-NSVC(none) Rx NS-RESET
+NSE(01004)-NSVC(none) Tx NS-RESET-ACK
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK
+NSE(01004)-NSVC(none) Rx NS-UNBLOCK
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK-ACK
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=42, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-RESET cause=O&M intervention
+NSE(01004)-NSVC(none) Rx NS-RESET
+NSE(01004)-NSVC(none) Tx NS-RESET-ACK
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK
+NSE(01004)-NSVC(none) Rx NS-UNBLOCK
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK-ACK
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=84, first=0, mtu=119
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-BLOCK cause=O&M intervention
+NSE(01004)-NSVC(none) Tx NS-BLOCK-ACK
+NSE(01004)-NSVC(none) Rx NS-BLOCK-ACK
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-STATUS cause=NS-VC blocked
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-ALIVE-ACK
+NSE(01004)-NSVC(none) Rx NS-ALIVE-ACK
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=42, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-ALIVE-ACK
+NSE(01004)-NSVC(none) Rx NS-ALIVE-ACK
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=84, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-ALIVE-ACK
+NSE(01004)-NSVC(none) Rx NS-ALIVE-ACK
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=126, first=0, mtu=119
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+count_pdus(bind[0]) = 2
+count_pdus(bind[1]) = 4
+count_pdus(bind[2]) = 6
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-RESET cause=O&M intervention
+NSE(01004)-NSVC(none) Rx NS-RESET
+NSE(01004)-NSVC(none) Tx NS-RESET-ACK
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK
+NSE(01004)-NSVC(none) Rx NS-UNBLOCK
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK-ACK
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=42, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-RESET cause=O&M intervention
+NSE(01004)-NSVC(none) Rx NS-RESET
+NSE(01004)-NSVC(none) Tx NS-RESET-ACK
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK
+NSE(01004)-NSVC(none) Rx NS-UNBLOCK
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK-ACK
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=84, first=0, mtu=119
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-RESET cause=O&M intervention
+NSE(01004)-NSVC(none) Rx NS-RESET
+NSE(01004)-NSVC(none) Tx NS-RESET-ACK
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK
+NSE(01004)-NSVC(none) Rx NS-UNBLOCK
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK-ACK
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=42, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-RESET cause=O&M intervention
+NSE(01004)-NSVC(none) Rx NS-RESET
+NSE(01004)-NSVC(none) Tx NS-RESET-ACK
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK
+NSE(01004)-NSVC(none) Rx NS-UNBLOCK
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK-ACK
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=84, first=0, mtu=119
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE MTU changed, transfer=84, first=0, mtu=96
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=96
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=96
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=96
diff --git a/tests/gb/gprs_ns2_test.ok b/tests/gb/gprs_ns2_test.ok
index 40a1528b..16e66bb8 100644
--- a/tests/gb/gprs_ns2_test.ok
+++ b/tests/gb/gprs_ns2_test.ok
@@ -11,6 +11,11 @@
---- Create NSVC[i]
---- Create NSVC[i]
--- Finish NSE block unblock nsvc
+--- Testing NSE block unblock nsvc2
+---- Create NSE + Binds
+---- Create NSVC[i]
+---- Create NSVC[i]
+--- Finish NSE block unblock nsvc2
--- Testing unitdata test
---- Create NSE + Binds
---- Create NSVC[0]
@@ -28,6 +33,13 @@
---- Send UNITDATA to all NSVCs
---- Send BSSGP data to the NSE to test unitdata over NSVC[1]
--- Finish unitdata weight test
+--- Testing force unconfigured
+---- Create NSE + Binds
+---- Create NSVC[0]
+---- Create NSVC[1]
+---- Check if NSE is alive
+---- Check if NSE is dead
+--- Finish force unconfigured test
--- Testing mtu test
---- Create NSE + Binds
---- Create NSVC[0]
diff --git a/tests/gb/gprs_ns2_vty.vty b/tests/gb/gprs_ns2_vty.vty
index 891bde59..8db5fb50 100644
--- a/tests/gb/gprs_ns2_vty.vty
+++ b/tests/gb/gprs_ns2_vty.vty
@@ -17,7 +17,7 @@ OsmoNSdummy(config)# list
OsmoNSdummy(config)# ns
OsmoNSdummy(config-ns)# list
...
- timer (tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries) <0-65535>
+ timer (tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries|tsns-procedures-retries) <0-65535>
nse <0-65535> [ip-sns-role-sgsn]
no nse <0-65535>
bind (fr|udp) ID
@@ -38,13 +38,13 @@ OsmoNSdummy(config-ns)# nse 1234
OsmoNSdummy(config-ns-nse)# nsvc udp abc 127.0.0.15 9496
OsmoNSdummy(config-ns-nse)# end
OsmoNSdummy# show ns
-NSEI 01234: UDP, DEAD
+NSEI 01234: UDP, DEAD since 0d 0h 0m 0s
1 NS-VC:
- NSVCI none: RECOVERING PERSIST data_weight=1 sig_weight=1 udp)[127.0.0.14]:42999<>[127.0.0.15]:9496
+ RECOVERING PERSIST sig_weight=1 data_weight=1 udp)[127.0.0.14]:42999<>[127.0.0.15]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
UDP bind: 127.0.0.14:42999 DSCP: 0 Priority: 0
IP-SNS signalling weight: 1 data weight: 1
1 NS-VC:
- NSVCI none: RECOVERING PERSIST data_weight=1 sig_weight=1 udp)[127.0.0.14]:42999<>[127.0.0.15]:9496
+ RECOVERING PERSIST sig_weight=1 data_weight=1 udp)[127.0.0.14]:42999<>[127.0.0.15]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
OsmoNSdummy# configure terminal
OsmoNSdummy(config)# ns
OsmoNSdummy(config-ns)# nse 1234
@@ -52,17 +52,17 @@ OsmoNSdummy(config-ns-nse)# nsvc udp abc 127.0.0.16 9496 signalling-weight 0 dat
OsmoNSdummy(config-ns-nse)# nsvc udp abc 127.0.0.17 9496 signalling-weight 0 data-weight 0
OsmoNSdummy(config-ns-nse)# end
OsmoNSdummy# show ns
-NSEI 01234: UDP, DEAD
+NSEI 01234: UDP, DEAD since 0d 0h 0m 0s
3 NS-VC:
- NSVCI none: RECOVERING PERSIST data_weight=0 sig_weight=0 udp)[127.0.0.14]:42999<>[127.0.0.17]:9496
- NSVCI none: RECOVERING PERSIST data_weight=9 sig_weight=0 udp)[127.0.0.14]:42999<>[127.0.0.16]:9496
- NSVCI none: RECOVERING PERSIST data_weight=1 sig_weight=1 udp)[127.0.0.14]:42999<>[127.0.0.15]:9496
+ RECOVERING PERSIST sig_weight=1 data_weight=1 udp)[127.0.0.14]:42999<>[127.0.0.15]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
+ RECOVERING PERSIST sig_weight=0 data_weight=9 udp)[127.0.0.14]:42999<>[127.0.0.16]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
+ RECOVERING PERSIST sig_weight=0 data_weight=0 udp)[127.0.0.14]:42999<>[127.0.0.17]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
UDP bind: 127.0.0.14:42999 DSCP: 0 Priority: 0
IP-SNS signalling weight: 1 data weight: 1
3 NS-VC:
- NSVCI none: RECOVERING PERSIST data_weight=0 sig_weight=0 udp)[127.0.0.14]:42999<>[127.0.0.17]:9496
- NSVCI none: RECOVERING PERSIST data_weight=9 sig_weight=0 udp)[127.0.0.14]:42999<>[127.0.0.16]:9496
- NSVCI none: RECOVERING PERSIST data_weight=1 sig_weight=1 udp)[127.0.0.14]:42999<>[127.0.0.15]:9496
+ RECOVERING PERSIST sig_weight=1 data_weight=1 udp)[127.0.0.14]:42999<>[127.0.0.15]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
+ RECOVERING PERSIST sig_weight=0 data_weight=9 udp)[127.0.0.14]:42999<>[127.0.0.16]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
+ RECOVERING PERSIST sig_weight=0 data_weight=0 udp)[127.0.0.14]:42999<>[127.0.0.17]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
OsmoNSdummy# configure terminal
OsmoNSdummy(config)# ns
OsmoNSdummy(config-ns)# nse 1234
@@ -83,3 +83,7 @@ OsmoNSdummy(config-ns-nse)# ip-sns-remote 127.0.0.1 22222
OsmoNSdummy(config-ns-nse)# ip-sns-remote 127.0.0.1 22222
Specified SNS endpoint already part of the NSE.
OsmoNSdummy(config-ns-nse)# exit
+OsmoNSdummy(config-ns)# no bind abc
+OsmoNSdummy(config-ns)# no bind abc77
+bind abc77 does not exist!
+OsmoNSdummy(config-ns)# exit
diff --git a/tests/gb/gprs_ns_test.c b/tests/gb/gprs_ns_test.c
index 454a29e4..fc1ec605 100644
--- a/tests/gb/gprs_ns_test.c
+++ b/tests/gb/gprs_ns_test.c
@@ -262,7 +262,7 @@ static void dump_rate_ctr_group(FILE *stream, const char *prefix,
unsigned int i;
for (i = 0; i < ctrg->desc->num_ctr; i++) {
- struct rate_ctr *ctr = &ctrg->ctr[i];
+ struct rate_ctr *ctr = rate_ctr_group_get_ctr(ctrg, i);
if (ctr->current && !strchr(ctrg->desc->ctr_desc[i].name, ':'))
fprintf(stream, " %s%s: %llu%s",
prefix, ctrg->desc->ctr_desc[i].description,
@@ -420,7 +420,7 @@ static int expire_nsvc_timer(struct gprs_nsvc *nsvc)
return rc;
}
-static void test_nsvc()
+static void test_nsvc(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in peer[1] = {{0},};
@@ -459,7 +459,7 @@ static void test_nsvc()
alarm(0);
}
-static void test_ignored_messages()
+static void test_ignored_messages(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in peer[1] = {{0},};
@@ -486,7 +486,7 @@ static void test_ignored_messages()
nsi = NULL;
}
-static void test_bss_port_changes()
+static void test_bss_port_changes(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in peer[4] = {{0},};
@@ -540,7 +540,7 @@ static void test_bss_port_changes()
nsi = NULL;
}
-static void test_bss_reset_ack()
+static void test_bss_reset_ack(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in peer[4] = {{0},};
@@ -687,7 +687,7 @@ static void test_bss_reset_ack()
}
-static void test_sgsn_reset()
+static void test_sgsn_reset(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in sgsn_peer= {0};
@@ -765,7 +765,7 @@ static void test_sgsn_reset()
nsi = NULL;
}
-static void test_sgsn_reset_invalid_state()
+static void test_sgsn_reset_invalid_state(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in sgsn_peer= {0};
@@ -833,7 +833,7 @@ static void test_sgsn_reset_invalid_state()
nsi = NULL;
}
-static void test_sgsn_output()
+static void test_sgsn_output(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in sgsn_peer= {0};
diff --git a/tests/gb/gprs_ns_test.err b/tests/gb/gprs_ns_test.err
new file mode 100644
index 00000000..266fb4e5
--- /dev/null
+++ b/tests/gb/gprs_ns_test.err
@@ -0,0 +1,147 @@
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:1111
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:1111
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:1111
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:1111
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+Ignoring NS STATUS from 1.2.3.4:1111 for non-existing NS-VC
+Ignoring NS ALIVE ACK from 1.2.3.4:1111 for non-existing NS-VC
+Ignoring NS RESET ACK from 1.2.3.4:1111 for non-existing NS-VC
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:1111
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4386, NSVCI=4386, cause=O&M intervention)
+NSEI=4386 Tx NS RESET ACK (NSVCI=4386)
+NSEI=4386 Rx NS UNBLOCK
+NSVCI=4386 Rx NS RESET (NSEI=4386, NSVCI=4386, cause=O&M intervention)
+NSEI=4386 Tx NS RESET ACK (NSVCI=4386)
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:3333
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4386, NSVCI=13124, cause=O&M intervention)
+NSEI=4386 Tx NS RESET ACK (NSVCI=13124)
+NS-VC changed NSEI (NSVCI=4386) from 4386 to 13124
+NSVCI=4386 Rx NS RESET (NSEI=13124, NSVCI=4386, cause=O&M intervention)
+NSEI=13124 Tx NS RESET ACK (NSVCI=4386)
+NSVCI=13124 Rx NS RESET (NSEI=4386, NSVCI=4386, cause=O&M intervention)
+NS-VC changed link (NSVCI=4386) from 1.2.3.4:3333 to 1.2.3.4:4444
+NSEI=4386 Tx NS RESET ACK (NSVCI=4386)
+NSVCI=4386 Rx NS RESET (NSEI=4386, NSVCI=4386, cause=O&M intervention)
+NSEI=4386 Tx NS RESET ACK (NSVCI=4386)
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:1111
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSEI=4096 Rx NS UNBLOCK
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:2222
+NSVCI=65535(invalid) Rx NS RESET (NSEI=8192, NSVCI=8193, cause=O&M intervention)
+NSEI=8192 Tx NS RESET ACK (NSVCI=8193)
+NSEI=8192 Rx NS UNBLOCK
+NSEI=4096 RESET procedure based on API request
+NSEI=4096 Tx NS RESET (NSVCI=4097, cause=O&M intervention)
+NSVCI=4097 Rx NS RESET ACK (NSEI=4096, NSVCI=4097)
+NSEI=8192 RESET procedure based on API request
+NSEI=8192 Tx NS RESET (NSVCI=8193, cause=O&M intervention)
+NSVCI=8193 Rx NS RESET ACK (NSEI=4096, NSVCI=4097)
+NS-VC changed link (NSVCI=4097) from 1.2.3.4:2222 to 1.2.3.4:1111
+NSEI=8192 RESET procedure based on API request
+NSEI=8192 Tx NS RESET (NSVCI=8193, cause=O&M intervention)
+failed to send NS message via UDP: Operation not permitted
+NSEI=8192, error resetting NS-VC
+NSEI=4096 RESET procedure based on API request
+NSEI=4096 Tx NS RESET (NSVCI=4097, cause=O&M intervention)
+NSVCI=4097 Rx NS RESET ACK (NSEI=4096, NSVCI=4097)
+NSVCI=8193 Rx NS RESET (NSEI=8192, NSVCI=8193, cause=O&M intervention)
+NSEI=8192 Tx NS RESET ACK (NSVCI=8193)
+NSEI=8192 Rx NS UNBLOCK
+NSVCI=4097 Rx NS RESET (NSEI=61440, NSVCI=4097, cause=O&M intervention)
+NSEI=61440 Tx NS RESET ACK (NSVCI=4097)
+NSVCI=4097 Rx NS RESET (NSEI=4096, NSVCI=61441, cause=O&M intervention)
+Creating NS-VC 61441 replacing 4097 at 1.2.3.4:2222
+NSVCI=61441 Creating NS-VC with Signal weight 1, Data weight 1
+NSEI=4096 Tx NS RESET ACK (NSVCI=61441)
+NSVCI=61441 Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NS-VC changed link (NSVCI=4097) from 1.2.3.4:2222 to 0.0.0.0:0
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSVCI=4097 Rx NS RESET ACK (NSEI=4096, NSVCI=4097)
+NS RESET ACK Discarding unexpected message for NS-VCI 4097 from SGSN NSEI=4096
+NSEI=4096 RESET procedure based on API request
+NSEI=4096 Tx NS RESET (NSVCI=4097, cause=O&M intervention)
+NSVCI=4097 Rx NS RESET ACK (NSEI=61440, NSVCI=4097)
+NSEI=61440 RESET procedure based on API request
+NSEI=61440 Tx NS RESET (NSVCI=4097, cause=O&M intervention)
+NSVCI=4097 Rx NS RESET ACK (NSEI=4096, NSVCI=61441)
+NS-VC changed link (NSVCI=61441) from 1.2.3.4:2222 to 0.0.0.0:0
+NSVCI=61441 Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NS-VC changed link (NSVCI=4097) from 1.2.3.4:2222 to 0.0.0.0:0
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSEI=4096 Rx NS UNBLOCK
+NSEI=4096 RESET procedure based on API request
+NSEI=4096 Tx NS RESET (NSVCI=4097, cause=O&M intervention)
+NSEI=4096 Rx NS UNBLOCK ACK
+NSVCI=4097 Rx NS RESET ACK (NSEI=4096, NSVCI=4097)
+NS RESET ACK Discarding unexpected message for NS-VCI 4097 from SGSN NSEI=4096
+NSEI=4096 Reset timed out but RESET flag is not set
+NSEI=4096 Tx NS RESET (NSVCI=4097, cause=O&M intervention)
+NSVCI=4097 Rx NS RESET ACK (NSEI=4096, NSVCI=4097)
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+NSVCI=257 Creating NS-VC with Signal weight 1, Data weight 1
+NSEI=256 RESET procedure based on API request
+NSEI=256 Tx NS RESET (NSVCI=257, cause=O&M intervention)
+NSVCI=257 Rx NS RESET ACK (NSEI=256, NSVCI=257)
+NSEI=256 Tx NS UNBLOCK (NSVCI=257)
+NSEI=256 Rx NS UNBLOCK ACK
+NSVCI=257 Rx NS RESET (NSEI=256, NSVCI=257, cause=O&M intervention)
+NSEI=256 Tx NS RESET ACK (NSVCI=257)
+NSVCI=257 Rx NS RESET (NSEI=61440, NSVCI=257, cause=O&M intervention)
+NSEI=256 Tx NS RESET ACK (NSVCI=257)
+NSVCI=257 Rx NS RESET (NSEI=256, NSVCI=61441, cause=O&M intervention)
+NSEI=256 Tx NS RESET ACK (NSVCI=257)
+NSVCI=257 Rx NS RESET (NSEI=256, NSVCI=257, cause=O&M intervention)
+NSEI=256 Tx NS RESET ACK (NSVCI=257)
+NSVCI=257 Rx NS RESET ACK (NSEI=256, NSVCI=257)
+NS RESET ACK Discarding unexpected message for NS-VCI 257 from SGSN NSEI=256
+NSEI=256 RESET procedure based on API request
+NSEI=256 Tx NS RESET (NSVCI=257, cause=O&M intervention)
+NSVCI=257 Rx NS RESET ACK (NSEI=57344, NSVCI=257)
+NS RESET ACK Unknown NSEI 57344 (NS-VCI=257) from 5.6.7.8:32000
+NSEI=256 RESET procedure based on API request
+NSEI=256 Tx NS RESET (NSVCI=257, cause=O&M intervention)
+NSVCI=257 Rx NS RESET ACK (NSEI=256, NSVCI=57345)
+NS RESET ACK Unknown NS-VCI 57345 (SGSN NSEI=256) from 5.6.7.8:32000
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+NSVCI=257 Creating NS-VC with Signal weight 1, Data weight 1
+NSEI=256 RESET procedure based on API request
+NSEI=256 Tx NS RESET (NSVCI=257, cause=O&M intervention)
+NSVCI=257 Rx NS RESET ACK (NSEI=256, NSVCI=257)
+NSEI=256 Tx NS UNBLOCK (NSVCI=257)
+NSEI=256 Rx NS UNBLOCK ACK
+NSEI=256 Tns-alive expired more then 10 times, blocking NS-VC
+NSEI=256 RESET procedure based on API request
+NSEI=256 Tx NS RESET (NSVCI=257, cause=PDU not compatible with protocol state)
+NSVCI=257 Rx NS RESET ACK (NSEI=256, NSVCI=257)
+NSEI=256 Tx NS UNBLOCK (NSVCI=257)
+NSEI=256 Rx NS UNBLOCK ACK
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+Unable to resolve NSEI 256 to NS-VC!
+NSVCI=257 Creating NS-VC with Signal weight 1, Data weight 1
+NSEI=256 RESET procedure based on API request
+NSEI=256 Tx NS RESET (NSVCI=257, cause=O&M intervention)
+All NS-VCs for NSEI 256 are either dead or blocked!
+NSVCI=257 Rx NS RESET ACK (NSEI=256, NSVCI=257)
+NSEI=256 Tx NS UNBLOCK (NSVCI=257)
+All NS-VCs for NSEI 256 are either dead or blocked!
+NSEI=256 Rx NS UNBLOCK ACK
diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c
index 5a596397..8a89357c 100644
--- a/tests/gsm0408/gsm0408_test.c
+++ b/tests/gsm0408/gsm0408_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
@@ -35,40 +31,96 @@
#include <osmocom/core/msgb.h>
-static const uint8_t csd_9600_v110_lv[] = { 0x07, 0xa1, 0xb8, 0x89, 0x21, 0x15, 0x63, 0x80 };
+static const uint8_t csd_9600_v110_lv[] = { 0x07, 0xa1, 0x88, 0x89, 0x21, 0x15, 0x63, 0x80 };
static const struct gsm_mncc_bearer_cap bcap_csd_9600_v110 = {
- .transfer = GSM48_BCAP_ITCAP_UNR_DIG_INF,
- .mode = GSM48_BCAP_TMOD_CIRCUIT,
- .coding = GSM48_BCAP_CODING_GSM_STD,
- .radio = GSM48_BCAP_RRQ_FR_ONLY,
- .speech_ver[0]= -1,
+ .transfer = GSM48_BCAP_ITCAP_UNR_DIG_INF,
+ .mode = GSM48_BCAP_TMOD_CIRCUIT,
+ .coding = GSM48_BCAP_CODING_GSM_STD,
+ .radio = GSM48_BCAP_RRQ_FR_ONLY,
+ .speech_ver[0] = -1,
.data = {
- .rate_adaption = GSM48_BCAP_RA_V110_X30,
- .sig_access = GSM48_BCAP_SA_I440_I450,
- .async = 1,
- .nr_stop_bits = 1,
- .nr_data_bits = 8,
- .user_rate = GSM48_BCAP_UR_9600,
- .parity = GSM48_BCAP_PAR_NONE,
- .interm_rate = GSM48_BCAP_IR_16k,
- .transp = GSM48_BCAP_TR_TRANSP,
- .modem_type = GSM48_BCAP_MT_NONE,
+ .rate_adaption = GSM48_BCAP_RA_V110_X30,
+ .sig_access = GSM48_BCAP_SA_I440_I450,
+ .async = 1,
+ .nr_stop_bits = 1,
+ .nr_data_bits = 8,
+ .user_rate = GSM48_BCAP_UR_9600,
+ .parity = GSM48_BCAP_PAR_NONE,
+ .interm_rate = GSM48_BCAP_IR_16k,
+ .transp = GSM48_BCAP_TR_TRANSP,
+ .modem_type = GSM48_BCAP_MT_NONE,
+ },
+};
+
+static const uint8_t csd_4800_rlp_lv[] = { 0x07, 0xa1, 0x88, 0x89, 0x21, 0x14, 0x63, 0xa0 };
+
+static const struct gsm_mncc_bearer_cap bcap_csd_4800_rlp = {
+ .transfer = GSM48_BCAP_ITCAP_UNR_DIG_INF,
+ .mode = GSM48_BCAP_TMOD_CIRCUIT,
+ .coding = GSM48_BCAP_CODING_GSM_STD,
+ .radio = GSM48_BCAP_RRQ_FR_ONLY,
+ .speech_ver[0] = -1,
+ .data = {
+ .rate_adaption = GSM48_BCAP_RA_V110_X30,
+ .sig_access = GSM48_BCAP_SA_I440_I450,
+ .async = 1,
+ .nr_stop_bits = 1,
+ .nr_data_bits = 8,
+ .user_rate = GSM48_BCAP_UR_4800,
+ .parity = GSM48_BCAP_PAR_NONE,
+ .interm_rate = GSM48_BCAP_IR_16k,
+ .transp = GSM48_BCAP_TR_RLP,
+ .modem_type = GSM48_BCAP_MT_NONE,
+ },
+};
+
+static const uint8_t csd_2400_v22bis_lv[] = { 0x07, 0xa2, 0xb8, 0x81, 0x21, 0x13, 0x43, 0x83 };
+
+static const struct gsm_mncc_bearer_cap bcap_csd_2400_v22bis = {
+ .transfer = GSM48_BCAP_ITCAP_3k1_AUDIO,
+ .mode = GSM48_BCAP_TMOD_CIRCUIT,
+ .coding = GSM48_BCAP_CODING_GSM_STD,
+ .radio = GSM48_BCAP_RRQ_FR_ONLY,
+ .speech_ver[0] = -1,
+ .data = {
+ .rate_adaption = GSM48_BCAP_RA_NONE,
+ .sig_access = GSM48_BCAP_SA_I440_I450,
+ .async = 1,
+ .nr_stop_bits = 1,
+ .nr_data_bits = 8,
+ .user_rate = GSM48_BCAP_UR_2400,
+ .parity = GSM48_BCAP_PAR_NONE,
+ .interm_rate = GSM48_BCAP_IR_8k,
+ .transp = GSM48_BCAP_TR_TRANSP,
+ .modem_type = GSM48_BCAP_MT_V22bis,
},
};
static const uint8_t speech_all_lv[] = { 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, 0x81 };
static const struct gsm_mncc_bearer_cap bcap_speech_all = {
- .transfer = GSM48_BCAP_ITCAP_SPEECH,
- .mode = GSM48_BCAP_TMOD_CIRCUIT,
- .coding = GSM48_BCAP_CODING_GSM_STD,
- .radio = GSM48_BCAP_RRQ_DUAL_FR,
+ .transfer = GSM48_BCAP_ITCAP_SPEECH,
+ .mode = GSM48_BCAP_TMOD_CIRCUIT,
+ .coding = GSM48_BCAP_CODING_GSM_STD,
+ .radio = GSM48_BCAP_RRQ_DUAL_FR,
.speech_ver = {
4, 2, 0, 5, 1, -1,
},
};
+static const uint8_t speech_no3a_lv[] = { 0x01, 0xa0 };
+
+static const struct gsm_mncc_bearer_cap bcap_speech_no3a = {
+ .transfer = GSM48_BCAP_ITCAP_SPEECH,
+ .mode = GSM48_BCAP_TMOD_CIRCUIT,
+ .coding = GSM48_BCAP_CODING_GSM_STD,
+ .radio = GSM48_BCAP_RRQ_FR_ONLY,
+ .speech_ver = {
+ 0, -1,
+ },
+};
+
struct bcap_test {
const uint8_t *lv;
@@ -78,16 +130,22 @@ struct bcap_test {
static const struct bcap_test bcap_tests[] = {
{ csd_9600_v110_lv, &bcap_csd_9600_v110, "CSD 9600/V.110/transparent" },
+ { csd_4800_rlp_lv, &bcap_csd_4800_rlp, "CSD 4800/RLP/non-transparent" },
+ { /* XXX: this testcase is expected to fail because octet 4 is not represented in
+ * 'struct gsm_mncc_bearer_cap' and the encoder unconditionally hard-codes it to 0x88. */
+ csd_2400_v22bis_lv, &bcap_csd_2400_v22bis, "CSD 2400/V.22bis/transparent" },
{ speech_all_lv, &bcap_speech_all, "Speech, all codecs" },
+ { speech_no3a_lv, &bcap_speech_no3a, "Speech, without octet 3a" },
};
-static int test_bearer_cap()
+static int test_bearer_cap(void)
{
struct gsm_mncc_bearer_cap bc;
int i, rc;
for (i = 0; i < ARRAY_SIZE(bcap_tests); i++) {
struct msgb *msg = msgb_alloc(100, "test");
+ bool pass = false;
int lv_len;
memset(&bc, 0, sizeof(bc));
@@ -97,7 +155,7 @@ static int test_bearer_cap()
if (rc < 0) {
fprintf(stderr, "Error decoding %s\n",
bcap_tests[i].name);
- return rc;
+ goto verdict;
}
if (memcmp(&bc, bcap_tests[i].bc, sizeof(bc))) {
fprintf(stderr, "Incorrect decoded result of %s:\n",
@@ -106,7 +164,7 @@ static int test_bearer_cap()
osmo_hexdump((uint8_t *) bcap_tests[i].bc, sizeof(bc)));
fprintf(stderr, " is: %s\n",
osmo_hexdump((uint8_t *) &bc, sizeof(bc)));
- return -1;
+ goto verdict;
}
/* also test re-encode? */
@@ -114,7 +172,7 @@ static int test_bearer_cap()
if (rc < 0) {
fprintf(stderr, "Error encoding %s\n",
bcap_tests[i].name);
- return rc;
+ goto verdict;
}
lv_len = bcap_tests[i].lv[0]+1;
if (memcmp(msg->data, bcap_tests[i].lv, lv_len)) {
@@ -124,10 +182,14 @@ static int test_bearer_cap()
osmo_hexdump(bcap_tests[i].lv, lv_len));
fprintf(stderr, " is: %s\n",
osmo_hexdump(msg->data, msg->len));
- return -1;
+ goto verdict;
}
- printf("Test `%s' passed\n", bcap_tests[i].name);
+ /* all checks passed */
+ pass = true;
+
+verdict:
+ printf("Test `%s' %sed\n", bcap_tests[i].name, pass ? "pass" : "fail");
msgb_free(msg);
}
@@ -327,6 +389,171 @@ static void test_lai_encode_decode(void)
}
}
+static struct osmo_routing_area_id test_osmo_routing_area_id_items[] = {
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 77,
+ .mnc = 121,
+ },
+ .lac = 666,
+ },
+ .rac = 5,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 84,
+ .mnc = 98,
+ },
+ .lac = 11,
+ },
+ .rac = 89,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 0,
+ .mnc = 0,
+ .mnc_3_digits = false,
+ /* expecting 000-00, BCD = 00 f0 00 */
+ },
+ .lac = 0,
+ },
+ .rac = 0,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 0,
+ .mnc = 0,
+ .mnc_3_digits = true,
+ /* expecting 000-000, BCD = 00 00 00 */
+ },
+ .lac = 0,
+ },
+ .rac = 0,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 999,
+ .mnc = 999,
+ },
+ .lac = 65535,
+ },
+ .rac = 255,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 1,
+ .mnc = 2,
+ .mnc_3_digits = false,
+ /* expecting 001-02, BCD = 00 f1 20 */
+ },
+ .lac = 23,
+ },
+ .rac = 42,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 1,
+ .mnc = 2,
+ .mnc_3_digits = true,
+ /* expecting 001-002, BCD = 00 21 00 */
+ },
+ .lac = 23,
+ },
+ .rac = 42,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 12,
+ .mnc = 34,
+ .mnc_3_digits = false,
+ /* expecting 012-34, BCD = 10 f2 43 */
+ },
+ .lac = 56,
+ },
+ .rac = 78,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 12,
+ .mnc = 34,
+ .mnc_3_digits = true,
+ /* expecting 012-034, BCD = 10 42 30 */
+ },
+ .lac = 23,
+ },
+ .rac = 42,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 123,
+ .mnc = 456,
+ .mnc_3_digits = false,
+ /* expecting 123-456, BCD = 21 63 54 (false flag has no effect) */
+ },
+ .lac = 23,
+ },
+ .rac = 42,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 123,
+ .mnc = 456,
+ .mnc_3_digits = true,
+ /* expecting 123-456, BCD = 21 63 54 (same) */
+ },
+ .lac = 23,
+ },
+ .rac = 42,
+ },
+};
+
+static inline void dump_osmo_routing_area_id(const struct osmo_routing_area_id *raid)
+{
+ printf("%s%s", osmo_rai_name2(raid), raid->lac.plmn.mnc_3_digits ? " (3-digit MNC)" : "");
+}
+
+static inline void check_osmo_routing_area_id(const struct osmo_routing_area_id *raid)
+{
+ uint8_t buf[sizeof(struct gsm48_ra_id)] = {};
+ struct osmo_routing_area_id raid0 = {};
+ int rc;
+
+ printf("RA ID: ");
+ dump_osmo_routing_area_id(raid);
+
+ rc = osmo_routing_area_id_encode_buf(buf, sizeof(buf), raid);
+ printf("osmo_routing_area_id_encode_buf(): %src=%d\n", osmo_hexdump(buf, sizeof(buf)), rc);
+
+ rc = osmo_routing_area_id_decode(&raid0, buf, sizeof(buf));
+ printf("osmo_routing_area_id_decode(): ");
+ dump_osmo_routing_area_id(&raid0);
+ printf(" rc=%d\n", rc);
+
+ if (osmo_rai_cmp(raid, &raid0))
+ printf("FAIL\n");
+ else
+ printf("ok\n");
+}
+
+static void test_osmo_routing_area_id(void)
+{
+ int i;
+ printf("==%s()==\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(test_osmo_routing_area_id_items); i++)
+ check_osmo_routing_area_id(&test_osmo_routing_area_id_items[i]);
+}
+
static void dump_cm3(struct gsm48_classmark3 *cm3)
{
printf("mult_band_supp=%02x\n", cm3->mult_band_supp);
@@ -1140,7 +1367,7 @@ struct mobile_identity_tc mobile_identity_tests[] = {
},
};
-void test_struct_mobile_identity()
+void test_struct_mobile_identity(void)
{
struct mobile_identity_tc *t;
printf("%s()\n", __func__);
@@ -1154,7 +1381,7 @@ void test_struct_mobile_identity()
rc = osmo_mobile_identity_decode_from_l3(&mi, msg, false);
msgb_free(msg);
- printf("%s: rc = %d", t->label, rc);
+ printf("%s: %s", t->label, rc ? "rc != 0" : "rc == 0");
if (!rc) {
printf(", mi = %s", osmo_mobile_identity_to_str_c(OTC_SELECT, &mi));
}
@@ -1163,7 +1390,7 @@ void test_struct_mobile_identity()
&& ((rc != 0) || !osmo_mobile_identity_cmp(&mi, &t->expect_mi))) {
printf(" ok");
} else {
- printf(" ERROR: Expected rc = %d", t->expect_rc);
+ printf(" ERROR: Got rc = %d, expected rc = %d", rc, t->expect_rc);
if (!t->expect_rc)
printf(", mi = %s", osmo_mobile_identity_to_str_c(OTC_SELECT, &t->expect_mi));
}
@@ -1302,7 +1529,7 @@ static const struct bcd_number_test {
},
};
-static void test_bcd_number_encode_decode()
+static void test_bcd_number_encode_decode(void)
{
const struct bcd_number_test *test;
uint8_t buf_enc[0xff] = { 0xff };
@@ -1536,7 +1763,7 @@ static void test_random_range_encoding(int range, int max_arfcn_num)
}
}
-static void test_range_encoding()
+static void test_range_encoding(void)
{
int *arfcns;
int arfcns_num = 0;
@@ -1597,7 +1824,7 @@ static int range512[] = {
__FILE__, __LINE__, (int) res, # cmp, (int) wanted); \
}
-static void test_arfcn_filter()
+static void test_arfcn_filter(void)
{
int arfcns[50], i, res, f0_included;
for (i = 0; i < ARRAY_SIZE(arfcns); ++i)
@@ -1630,7 +1857,7 @@ static void test_arfcn_filter()
VERIFY(arfcns[i], ==, ((i + 1) * 2) - 1);
}
-static void test_print_encoding()
+static void test_print_encoding(void)
{
int rc;
int w[17];
@@ -1655,7 +1882,7 @@ static void test_print_encoding()
printf("Range512: %s\n", osmo_hexdump(chan_list, ARRAY_SIZE(chan_list)));
}
-static void test_si_range_helpers()
+static void test_si_range_helpers(void)
{
int ws[(sizeof(freqs1)/sizeof(freqs1[0]))];
int i, f0 = 0xFFFFFF;
@@ -1694,7 +1921,7 @@ static void test_si_range_helpers()
VERIFY(f0, ==, 1);
}
-static void test_power_ctrl()
+static void test_power_ctrl(void)
{
int8_t rc8;
int rc;
@@ -1736,6 +1963,62 @@ static void test_power_ctrl()
VERIFY(rc, <, 0);
}
+static void test_rach_tx_integer_raw2val(void)
+{
+ unsigned int raw;
+ for (raw = 0; raw <= 0x0f; raw++) {
+ unsigned int val = rach_tx_integer_raw2val(raw);
+ printf("rach_tx_integer_raw2val(0x0%x): %u slots used to spread transmission\n",
+ raw, val);
+ }
+}
+
+static void test_gsm_gsmtime2fn(void)
+{
+ struct gsm_time gsm_time;
+ uint32_t fn;
+ uint32_t fn_recovered;
+
+ for (fn = 0; fn < 42432; fn++) {
+ gsm_time.t1 = (fn / 1326) % 32;
+ gsm_time.t2 = fn % 26;
+ gsm_time.t3 = fn % 51;
+
+ fn_recovered = gsm_gsmtime2fn(&gsm_time);
+
+ if (fn_recovered != fn) {
+ printf(" Wrong frame number computed! t1=%d, t2=%d, t3=%d ==> fn=%d, expected fn=%d\n",
+ gsm_time.t1, gsm_time.t2, gsm_time.t3, fn_recovered, fn);
+ OSMO_ASSERT(false);
+ }
+ }
+}
+
+static void test_gsm_rfn2fn(void)
+{
+ unsigned int i;
+ struct {
+ uint32_t curr_fn;
+ uint16_t rfn;
+ uint32_t exp_fn;
+ } input[] = {
+ { .curr_fn = 0, .rfn = 0, .exp_fn = 0 },
+ { .curr_fn = 0, .rfn = 4, .exp_fn = 4 },
+ { .curr_fn = 2229729, .rfn = 23322, .exp_fn = 2229786 },
+ { .curr_fn = 2229777, .rfn = 23322, .exp_fn = 2229786 },
+ { .curr_fn = 1320458, .rfn = 5070, .exp_fn = 1320462 },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(input); i++) {
+ uint32_t fn = gsm_rfn2fn(input[i].rfn, input[i].curr_fn);
+ if (fn != input[i].exp_fn) {
+ printf("Wrong frame number computed! curr_fn=%u, rfn=%u ==> fn=%u, expected fn=%u\n",
+ input[i].curr_fn, input[i].rfn, fn, input[i].exp_fn);
+ OSMO_ASSERT(false);
+ }
+ }
+}
+
int main(int argc, char **argv)
{
test_bearer_cap();
@@ -1747,6 +2030,7 @@ int main(int argc, char **argv)
test_bcd_number_encode_decode();
test_ra_cap();
test_lai_encode_decode();
+ test_osmo_routing_area_id();
test_decode_classmark3();
test_si_range_helpers();
@@ -1754,6 +2038,9 @@ int main(int argc, char **argv)
test_print_encoding();
test_range_encoding();
test_power_ctrl();
+ test_rach_tx_integer_raw2val();
+ test_gsm_gsmtime2fn();
+ test_gsm_rfn2fn();
return EXIT_SUCCESS;
}
diff --git a/tests/gsm0408/gsm0408_test.err b/tests/gsm0408/gsm0408_test.err
new file mode 100644
index 00000000..0102de01
--- /dev/null
+++ b/tests/gsm0408/gsm0408_test.err
@@ -0,0 +1,6 @@
+Incorrect encoded result of CSD 2400/V.22bis/transparent:
+ should: 07 a2 b8 81 21 13 43 83
+ is: 07 a2 88 81 21 13 43 83
+Incorrect encoded result of Speech, without octet 3a:
+ should: 01 a0
+ is: 02 20 80
diff --git a/tests/gsm0408/gsm0408_test.ok b/tests/gsm0408/gsm0408_test.ok
index 52c4ea72..721e2c53 100644
--- a/tests/gsm0408/gsm0408_test.ok
+++ b/tests/gsm0408/gsm0408_test.ok
@@ -1,5 +1,8 @@
Test `CSD 9600/V.110/transparent' passed
+Test `CSD 4800/RLP/non-transparent' passed
+Test `CSD 2400/V.22bis/transparent' failed
Test `Speech, all codecs' passed
+Test `Speech, without octet 3a' failed
Simple TMSI encoding test....passed
Simple IMSI encoding test....passed: [10] 17 08 99 10 07 00 00 00 64 02
@@ -140,55 +143,55 @@ Decoding zero length Mobile Identities
returned empty string
test_struct_mobile_identity()
-LU with IMSI 901700000004620: rc = 0, mi = IMSI-901700000004620 ok
-LU with TMSI 0x0980ad8a: rc = 0, mi = TMSI-0x0980AD8A ok
-LU with invalid MI type: rc = -22 ok
-LU with truncated IMSI MI: rc = -74 ok
-LU with too short IMSI MI (12345): rc = -74 ok
-LU with just long enough IMSI MI 123456: rc = 0, mi = IMSI-123456 ok
-LU with max length IMSI MI 123456789012345: rc = 0, mi = IMSI-123456789012345 ok
-LU with just too long IMSI MI 1234567890123456: rc = -74 ok
-LU with truncated TMSI MI: rc = -74 ok
-LU with odd length TMSI: rc = -74 ok
-LU with too long TMSI MI: rc = -74 ok
-LU with too short TMSI: rc = -74 ok
-CM Service Request with IMSI 123456: rc = 0, mi = IMSI-123456 ok
-CM Service Request with TMSI 0x5a42e404: rc = 0, mi = TMSI-0x5A42E404 ok
-CM Service Request with shorter CM2, with IMSI 123456: rc = 0, mi = IMSI-123456 ok
-CM Service Request with longer CM2, with IMSI 123456: rc = 0, mi = IMSI-123456 ok
-CM Service Request with shorter CM2, with TMSI 0x00000000: rc = 0, mi = TMSI-0x00000000 ok
-CM Service Request with invalid MI type: rc = -22 ok
-CM Service Request with truncated IMSI MI: rc = -74 ok
-CM Service Request with truncated TMSI MI: rc = -74 ok
-CM Service Request with odd length TMSI: rc = -74 ok
-CM Service Request with too long TMSI MI: rc = -74 ok
-CM Service Request with too short TMSI: rc = -74 ok
-CM Service Reestablish Request with TMSI 0x5a42e404: rc = 0, mi = TMSI-0x5A42E404 ok
-Paging Response with IMSI 1234567: rc = 0, mi = IMSI-1234567 ok
-Paging Response with TMSI 0xb48883de: rc = 0, mi = TMSI-0xB48883DE ok
-Paging Response with TMSI, with unused nibble not 0xf: rc = -74 ok
-Paging Response with too short IMEI (1234567): rc = -74 ok
-Paging Response with IMEI 123456789012345: rc = 0, mi = IMEI-123456789012345 ok
-Paging Response with IMEI 12345678901234 (no Luhn checksum): rc = 0, mi = IMEI-12345678901234 ok
-Paging Response with IMEISV 1234567890123456: rc = 0, mi = IMEI-SV-1234567890123456 ok
-Paging Response with too short IMEISV 123456789012345: rc = -74 ok
-Paging Response with too long IMEISV 12345678901234567: rc = -74 ok
-Paging Response with IMSI 123456789012345 and flipped ODD bit: rc = -74 ok
-IMSI-Detach with IMSI 901700000004620: rc = 0, mi = IMSI-901700000004620 ok
-IMSI-Detach with TMSI 0x0980ad8a: rc = 0, mi = TMSI-0x0980AD8A ok
-IMSI-Detach with invalid MI type: rc = -22 ok
-IMSI-Detach with truncated IMSI MI: rc = -74 ok
-IMSI-Detach with too short IMSI MI (12345): rc = -74 ok
-IMSI-Detach with just long enough IMSI MI 123456: rc = 0, mi = IMSI-123456 ok
-IMSI-Detach with max length IMSI MI 123456789012345: rc = 0, mi = IMSI-123456789012345 ok
-IMSI-Detach with just too long IMSI MI 1234567890123456: rc = -74 ok
-IMSI-Detach with truncated TMSI MI: rc = -74 ok
-IMSI-Detach with odd length TMSI: rc = -74 ok
-IMSI-Detach with too long TMSI MI: rc = -74 ok
-IMSI-Detach with too short TMSI: rc = -74 ok
-Identity Response with IMSI 901700000004620: rc = 0, mi = IMSI-901700000004620 ok
-Identity Response with IMEI 123456789012345: rc = 0, mi = IMEI-123456789012345 ok
-Identity Response with IMEISV 9876543210987654: rc = 0, mi = IMEI-SV-9876543210987654 ok
+LU with IMSI 901700000004620: rc == 0, mi = IMSI-901700000004620 ok
+LU with TMSI 0x0980ad8a: rc == 0, mi = TMSI-0x0980AD8A ok
+LU with invalid MI type: rc != 0 ok
+LU with truncated IMSI MI: rc != 0 ok
+LU with too short IMSI MI (12345): rc != 0 ok
+LU with just long enough IMSI MI 123456: rc == 0, mi = IMSI-123456 ok
+LU with max length IMSI MI 123456789012345: rc == 0, mi = IMSI-123456789012345 ok
+LU with just too long IMSI MI 1234567890123456: rc != 0 ok
+LU with truncated TMSI MI: rc != 0 ok
+LU with odd length TMSI: rc != 0 ok
+LU with too long TMSI MI: rc != 0 ok
+LU with too short TMSI: rc != 0 ok
+CM Service Request with IMSI 123456: rc == 0, mi = IMSI-123456 ok
+CM Service Request with TMSI 0x5a42e404: rc == 0, mi = TMSI-0x5A42E404 ok
+CM Service Request with shorter CM2, with IMSI 123456: rc == 0, mi = IMSI-123456 ok
+CM Service Request with longer CM2, with IMSI 123456: rc == 0, mi = IMSI-123456 ok
+CM Service Request with shorter CM2, with TMSI 0x00000000: rc == 0, mi = TMSI-0x00000000 ok
+CM Service Request with invalid MI type: rc != 0 ok
+CM Service Request with truncated IMSI MI: rc != 0 ok
+CM Service Request with truncated TMSI MI: rc != 0 ok
+CM Service Request with odd length TMSI: rc != 0 ok
+CM Service Request with too long TMSI MI: rc != 0 ok
+CM Service Request with too short TMSI: rc != 0 ok
+CM Service Reestablish Request with TMSI 0x5a42e404: rc == 0, mi = TMSI-0x5A42E404 ok
+Paging Response with IMSI 1234567: rc == 0, mi = IMSI-1234567 ok
+Paging Response with TMSI 0xb48883de: rc == 0, mi = TMSI-0xB48883DE ok
+Paging Response with TMSI, with unused nibble not 0xf: rc != 0 ok
+Paging Response with too short IMEI (1234567): rc != 0 ok
+Paging Response with IMEI 123456789012345: rc == 0, mi = IMEI-123456789012345 ok
+Paging Response with IMEI 12345678901234 (no Luhn checksum): rc == 0, mi = IMEI-12345678901234 ok
+Paging Response with IMEISV 1234567890123456: rc == 0, mi = IMEI-SV-1234567890123456 ok
+Paging Response with too short IMEISV 123456789012345: rc != 0 ok
+Paging Response with too long IMEISV 12345678901234567: rc != 0 ok
+Paging Response with IMSI 123456789012345 and flipped ODD bit: rc != 0 ok
+IMSI-Detach with IMSI 901700000004620: rc == 0, mi = IMSI-901700000004620 ok
+IMSI-Detach with TMSI 0x0980ad8a: rc == 0, mi = TMSI-0x0980AD8A ok
+IMSI-Detach with invalid MI type: rc != 0 ok
+IMSI-Detach with truncated IMSI MI: rc != 0 ok
+IMSI-Detach with too short IMSI MI (12345): rc != 0 ok
+IMSI-Detach with just long enough IMSI MI 123456: rc == 0, mi = IMSI-123456 ok
+IMSI-Detach with max length IMSI MI 123456789012345: rc == 0, mi = IMSI-123456789012345 ok
+IMSI-Detach with just too long IMSI MI 1234567890123456: rc != 0 ok
+IMSI-Detach with truncated TMSI MI: rc != 0 ok
+IMSI-Detach with odd length TMSI: rc != 0 ok
+IMSI-Detach with too long TMSI MI: rc != 0 ok
+IMSI-Detach with too short TMSI: rc != 0 ok
+Identity Response with IMSI 901700000004620: rc == 0, mi = IMSI-901700000004620 ok
+Identity Response with IMEI 123456789012345: rc == 0, mi = IMEI-123456789012345 ok
+Identity Response with IMEISV 9876543210987654: rc == 0, mi = IMEI-SV-9876543210987654 ok
BSD number encoding / decoding test
- Running test: regular 9-digit MSISDN
@@ -385,6 +388,40 @@ RA test...passed
Encoded 21 63 54 00 17
gsm48_decode_lai2() gives 123-456-23 (3-digit MNC)
passed
+==test_osmo_routing_area_id()==
+RA ID: 077-121-666-5osmo_routing_area_id_encode_buf(): 70 17 21 02 9a 05 rc=6
+osmo_routing_area_id_decode(): 077-121-666-5 (3-digit MNC) rc=6
+ok
+RA ID: 084-98-11-89osmo_routing_area_id_encode_buf(): 80 f4 89 00 0b 59 rc=6
+osmo_routing_area_id_decode(): 084-98-11-89 rc=6
+ok
+RA ID: 000-00-0-0osmo_routing_area_id_encode_buf(): 00 f0 00 00 00 00 rc=6
+osmo_routing_area_id_decode(): 000-00-0-0 rc=6
+ok
+RA ID: 000-000-0-0 (3-digit MNC)osmo_routing_area_id_encode_buf(): 00 00 00 00 00 00 rc=6
+osmo_routing_area_id_decode(): 000-000-0-0 (3-digit MNC) rc=6
+ok
+RA ID: 999-999-65535-255osmo_routing_area_id_encode_buf(): 99 99 99 ff ff ff rc=6
+osmo_routing_area_id_decode(): 999-999-65535-255 (3-digit MNC) rc=6
+ok
+RA ID: 001-02-23-42osmo_routing_area_id_encode_buf(): 00 f1 20 00 17 2a rc=6
+osmo_routing_area_id_decode(): 001-02-23-42 rc=6
+ok
+RA ID: 001-002-23-42 (3-digit MNC)osmo_routing_area_id_encode_buf(): 00 21 00 00 17 2a rc=6
+osmo_routing_area_id_decode(): 001-002-23-42 (3-digit MNC) rc=6
+ok
+RA ID: 012-34-56-78osmo_routing_area_id_encode_buf(): 10 f2 43 00 38 4e rc=6
+osmo_routing_area_id_decode(): 012-34-56-78 rc=6
+ok
+RA ID: 012-034-23-42 (3-digit MNC)osmo_routing_area_id_encode_buf(): 10 42 30 00 17 2a rc=6
+osmo_routing_area_id_decode(): 012-034-23-42 (3-digit MNC) rc=6
+ok
+RA ID: 123-456-23-42osmo_routing_area_id_encode_buf(): 21 63 54 00 17 2a rc=6
+osmo_routing_area_id_decode(): 123-456-23-42 (3-digit MNC) rc=6
+ok
+RA ID: 123-456-23-42 (3-digit MNC)osmo_routing_area_id_encode_buf(): 21 63 54 00 17 2a rc=6
+osmo_routing_area_id_decode(): 123-456-23-42 (3-digit MNC) rc=6
+ok
=====cm3_1=====
mult_band_supp=06
a5_bits=00
@@ -791,3 +828,19 @@ Random range test: range 127, max num ARFCNs 29
Random range test: range 255, max num ARFCNs 22
Random range test: range 511, max num ARFCNs 18
Random range test: range 1023, max num ARFCNs 16
+rach_tx_integer_raw2val(0x00): 3 slots used to spread transmission
+rach_tx_integer_raw2val(0x01): 4 slots used to spread transmission
+rach_tx_integer_raw2val(0x02): 5 slots used to spread transmission
+rach_tx_integer_raw2val(0x03): 6 slots used to spread transmission
+rach_tx_integer_raw2val(0x04): 7 slots used to spread transmission
+rach_tx_integer_raw2val(0x05): 8 slots used to spread transmission
+rach_tx_integer_raw2val(0x06): 9 slots used to spread transmission
+rach_tx_integer_raw2val(0x07): 10 slots used to spread transmission
+rach_tx_integer_raw2val(0x08): 11 slots used to spread transmission
+rach_tx_integer_raw2val(0x09): 12 slots used to spread transmission
+rach_tx_integer_raw2val(0x0a): 14 slots used to spread transmission
+rach_tx_integer_raw2val(0x0b): 16 slots used to spread transmission
+rach_tx_integer_raw2val(0x0c): 20 slots used to spread transmission
+rach_tx_integer_raw2val(0x0d): 25 slots used to spread transmission
+rach_tx_integer_raw2val(0x0e): 32 slots used to spread transmission
+rach_tx_integer_raw2val(0x0f): 50 slots used to spread transmission
diff --git a/tests/gsm0502/gsm0502_test.c b/tests/gsm0502/gsm0502_test.c
index a950c6c1..7768abb0 100644
--- a/tests/gsm0502/gsm0502_test.c
+++ b/tests/gsm0502/gsm0502_test.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <string.h>
@@ -90,7 +86,7 @@ uint32_t facch_h1_fn_samples[] = { 500728, 500771, 500797, 500841, 500875, 50091
502782, 502825, 502869, 502903, 502955, 502999
};
-static void test_gsm0502_fn_remap()
+static void test_gsm0502_fn_remap(void)
{
unsigned int i;
uint32_t fn_begin;
@@ -152,8 +148,28 @@ static void test_gsm0502_fn_remap()
printf("\n");
}
+static void test_gsm0502_fncmp(void)
+{
+ OSMO_ASSERT(gsm0502_fncmp(1337, 1337) == 0);
+ OSMO_ASSERT(gsm0502_fncmp(42, 1337) == -1);
+ OSMO_ASSERT(gsm0502_fncmp(1337, 42) == 1);
+ OSMO_ASSERT(gsm0502_fncmp(42, 0) == 1);
+
+ /* 2715642 is very close to the Fn period (GSM_TDMA_HYPERFRAME) */
+ OSMO_ASSERT(gsm0502_fncmp(2715642, 42) == -1);
+ OSMO_ASSERT(gsm0502_fncmp(42, 2715642) == 1);
+ OSMO_ASSERT(gsm0502_fncmp(0, 2715642) == 1);
+
+ /* 1357824 is half of the Fn period (GSM_TDMA_HYPERFRAME) */
+ OSMO_ASSERT(gsm0502_fncmp(1357820, 1357824) == -1);
+ OSMO_ASSERT(gsm0502_fncmp(1357820, 1357825) == -1);
+ OSMO_ASSERT(gsm0502_fncmp(1357824, 1357820) == 1);
+ OSMO_ASSERT(gsm0502_fncmp(1357825, 1357820) == 1);
+}
+
int main(int argc, char **argv)
{
test_gsm0502_fn_remap();
+ test_gsm0502_fncmp();
return EXIT_SUCCESS;
}
diff --git a/tests/gsm0808/gsm0808_test.c b/tests/gsm0808/gsm0808_test.c
index 04b3b727..ed99245c 100644
--- a/tests/gsm0808/gsm0808_test.c
+++ b/tests/gsm0808/gsm0808_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/gsm/gsm0808.h>
@@ -115,7 +111,7 @@ static void test_create_layer3(void)
msgb_free(in_msg);
}
-static void test_create_layer3_aoip()
+static void test_create_layer3_aoip(void)
{
static const uint8_t res[] = {
0x00, 0x17, 0x57, 0x05, 0x08, 0x00, 0x77, 0x62,
@@ -152,7 +148,7 @@ static void test_create_layer3_aoip()
msgb_free(in_msg);
}
-static void test_create_reset()
+static void test_create_reset(void)
{
static const uint8_t res[] = { 0x00, 0x04, 0x30, 0x04, 0x01, 0x20 };
struct msgb *msg;
@@ -163,7 +159,7 @@ static void test_create_reset()
msgb_free(msg);
}
-static void test_create_reset_ack()
+static void test_create_reset_ack(void)
{
static const uint8_t res[] = { 0x00, 0x01, 0x31 };
struct msgb *msg;
@@ -175,7 +171,7 @@ static void test_create_reset_ack()
}
-static void test_create_clear_command()
+static void test_create_clear_command(void)
{
static const uint8_t res[] = { 0x20, 0x04, 0x01, 0x23 };
struct msgb *msg;
@@ -186,7 +182,7 @@ static void test_create_clear_command()
msgb_free(msg);
}
-static void test_create_clear_command2()
+static void test_create_clear_command2(void)
{
static const uint8_t res[] = { 0x00, 0x04, 0x20, 0x04, 0x01, 0x23 };
struct msgb *msg;
@@ -197,7 +193,7 @@ static void test_create_clear_command2()
msgb_free(msg);
}
-static void test_create_clear_command2_csfb()
+static void test_create_clear_command2_csfb(void)
{
static const uint8_t res[] = { 0x00, 0x05, 0x20, 0x04, 0x01, 0x23, 0x8F };
struct msgb *msg;
@@ -208,7 +204,7 @@ static void test_create_clear_command2_csfb()
msgb_free(msg);
}
-static void test_create_clear_complete()
+static void test_create_clear_complete(void)
{
static const uint8_t res[] = { 0x00, 0x01, 0x21 };
struct msgb *msg;
@@ -219,7 +215,7 @@ static void test_create_clear_complete()
msgb_free(msg);
}
-static void test_create_cipher()
+static void test_create_cipher(void)
{
static const uint8_t res[] =
{ 0x00, 0x0c, 0x53, 0x0a, 0x09, 0x03, 0xaa,
@@ -259,7 +255,7 @@ static void test_create_cipher()
msgb_free(msg);
}
-static void test_create_cipher_complete()
+static void test_create_cipher_complete(void)
{
static const uint8_t res1[] = {
0x00, 0x08, 0x55, 0x20, 0x03, 0x23, 0x42, 0x21, 0x2c, 0x04 };
@@ -316,7 +312,7 @@ static inline void parse_cipher_reject(struct msgb *msg, uint8_t exp)
rc, exp, OSMO_BIT_PRINT(exp), msgb_hexdump(msg));
}
-static void test_create_cipher_reject()
+static void test_create_cipher_reject(void)
{
static const uint8_t res[] = { 0x00, 0x04, 0x59, 0x04, 0x01, 0x23 };
enum gsm0808_cause cause = GSM0808_CAUSE_CCCH_OVERLOAD;
@@ -331,7 +327,7 @@ static void test_create_cipher_reject()
msgb_free(msg);
}
-static void test_create_cipher_reject_ext()
+static void test_create_cipher_reject_ext(void)
{
static const uint8_t res[] = { 0x00, 0x05, 0x59, 0x04, 0x02, 0xd0, 0xFA };
uint8_t cause = 0xFA;
@@ -346,7 +342,7 @@ static void test_create_cipher_reject_ext()
msgb_free(msg);
}
-static void test_create_cm_u()
+static void test_create_cm_u(void)
{
static const uint8_t res[] = {
0x00, 0x07, 0x54, 0x12, 0x01, 0x23, 0x13, 0x01, 0x42 };
@@ -368,7 +364,7 @@ static void test_create_cm_u()
msgb_free(msg);
}
-static void test_create_sapi_reject()
+static void test_create_sapi_reject(void)
{
static const uint8_t res[] = { 0x00, 0x06, 0x25, 0x18, 0x03, 0x04, 0x01, 0x25 };
struct msgb *msg;
@@ -379,7 +375,7 @@ static void test_create_sapi_reject()
msgb_free(msg);
}
-static void test_dec_confusion()
+static void test_dec_confusion(void)
{
static const uint8_t hex[] =
{ 0x26, 0x04, 0x01, 0x52, 0x1f, 0x07, 0x00, 0xff, 0x00, 0x03, 0x25, 0x03, 0x25 };
@@ -428,7 +424,60 @@ static void test_dec_confusion()
osmo_hexdump(diag->msg, diag_len-2));
}
-static void test_create_ass()
+/* Test Perform Location Report SYS#5891 */
+static void test_dec_perform_location_report_sys5891(void)
+{
+/* Message Type Perform Location Request
+ Location Type
+ Element ID: 0x44
+ Length: 1
+ Location Information: current geographic location (0x00)
+ Cell Identifier/CI (25911)
+ Element ID: 0x05
+ Length: 8
+ 0000 .... = Spare bit(s): 0x00
+ .... 0000 = Cell identification discriminator: The whole Cell Global Identification, CGI, is used to identify the cells. (0)
+ Mobile Country Code (MCC): (removed))
+ Mobile Network Code (MNC): (removed))
+ Cell LAC: 0x001e (30)
+ Cell CI: 0x6537 (25911)
+ LCS Client Type
+ Element ID: 0x48
+ Length: 1
+ 0011 .... = Client Category: Emergency Services (0x03)
+ .... 0000 = Client Subtype: unspecified (0x00)
+ LCS Priority
+ Element ID: 0x43
+ Length: 1
+ Periodicity: highest (0)
+ LCS QoS
+ Element ID: 0x3e
+ Length: 4
+ 0000 00.. = Spare: 0x00
+ .... ..0. = Velocity Requested: do not report velocity (0x00)
+ .... ...0 = Vertical Coordinate Indicator: vertical coordinate not requested (0x00)
+ 1... .... = Horizontal Accuracy Indicator: horizontal accuracy is specified (0x01)
+ .001 0010 = Horizontal Accuracy: 0x12
+ 0... .... = Vertical Accuracy Indicator: vertical accuracy is not specified (0x00)
+ .000 0000 = Spare: 0x00
+ 00.. .... = Response Time Category: Response Time is not specified (0x00)
+*/
+ const uint8_t hex[] = {
+ 0x2b, 0x44, 0x01, 0x00, 0x05, 0x08, 0x00, 0xab, 0xbc, 0xcd, 0x00, 0x1e,
+ 0x65, 0x37, 0x48, 0x01, 0x30, 0x43, 0x01, 0x00, 0x3e, 0x04, 0x00, 0x92,
+ 0x00, 0x00
+ };
+
+ struct tlv_parsed tp;
+ int rc;
+
+ printf("Testing decoding Perform Location Report SYS#5891\n");
+
+ rc = tlv_parse(&tp, gsm0808_att_tlvdef(), hex+1, sizeof(hex)-1, 0, 0);
+ OSMO_ASSERT(rc == 5);
+}
+
+static void test_create_ass(void)
{
static const uint8_t res1[] =
{ 0x00, 0x0a, 0x01, 0x0b, 0x04, 0x01, 0x0b, 0xa1, 0x25, 0x01, 0x00,
@@ -478,7 +527,7 @@ static void test_create_ass()
msgb_free(msg);
}
-static void test_create_ass2()
+static void test_create_ass2(void)
{
static const uint8_t res[] = {
BSSAP_MSG_BSS_MANAGEMENT,
@@ -555,7 +604,7 @@ static void test_create_ass2()
msgb_free(msg);
}
-static void test_create_ass_compl()
+static void test_create_ass_compl(void)
{
static const uint8_t res1[] = {
0x00, 0x09, 0x02, 0x15, 0x23, 0x21, 0x42, 0x2c,
@@ -574,7 +623,7 @@ static void test_create_ass_compl()
msgb_free(msg);
}
-static void test_create_ass_compl_aoip()
+static void test_create_ass_compl_aoip(void)
{
struct sockaddr_storage ss;
struct sockaddr_in sin;
@@ -611,7 +660,7 @@ static void test_create_ass_compl_aoip()
msgb_free(msg);
}
-static void test_create_ass_fail()
+static void test_create_ass_fail(void)
{
static const uint8_t res1[] = { 0x00, 0x04, 0x03, 0x04, 0x01, 0x23 };
static const uint8_t res2[] = {
@@ -629,7 +678,7 @@ static void test_create_ass_fail()
msgb_free(msg);
}
-static void test_create_ass_fail_aoip()
+static void test_create_ass_fail_aoip(void)
{
static const uint8_t res1[] =
{ 0x00, 0x0d, 0x03, 0x04, 0x01, 0x23, GSM0808_IE_SPEECH_CODEC_LIST,
@@ -655,7 +704,7 @@ static void test_create_ass_fail_aoip()
msgb_free(msg);
}
-static void test_create_clear_rqst()
+static void test_create_clear_rqst(void)
{
static const uint8_t res[] = { 0x00, 0x04, 0x22, 0x04, 0x01, 0x23 };
struct msgb *msg;
@@ -666,7 +715,7 @@ static void test_create_clear_rqst()
msgb_free(msg);
}
-static void test_create_paging()
+static void test_create_paging(void)
{
static const uint8_t res[] =
{ 0x00, 0x10, 0x52, 0x08, 0x08, 0x09, 0x10, 0x10, 0x00, 0x00, 0x00,
@@ -706,7 +755,7 @@ static void test_create_paging()
msgb_free(msg);
}
-static void test_create_dtap()
+static void test_create_dtap(void)
{
static const uint8_t res[] = { 0x01, 0x03, 0x02, 0x23, 0x42 };
struct msgb *msg, *l3;
@@ -723,7 +772,7 @@ static void test_create_dtap()
msgb_free(l3);
}
-static void test_prepend_dtap()
+static void test_prepend_dtap(void)
{
static const uint8_t res[] = { 0x01, 0x03, 0x02, 0x23, 0x42 };
struct msgb *in_msg;
@@ -740,7 +789,7 @@ static void test_prepend_dtap()
msgb_free(in_msg);
}
-static void test_enc_dec_lcls()
+static void test_enc_dec_lcls(void)
{
static const uint8_t res[] = {
GSM0808_IE_GLOBAL_CALL_REF,
@@ -818,7 +867,7 @@ static void test_enc_dec_lcls()
msgb_free(msg);
}
-static void test_enc_dec_aoip_trasp_addr_v4()
+static void test_enc_dec_aoip_trasp_addr_v4(void)
{
struct sockaddr_storage enc_addr;
struct sockaddr_storage dec_addr;
@@ -846,7 +895,7 @@ static void test_enc_dec_aoip_trasp_addr_v4()
msgb_free(msg);
}
-static void test_enc_dec_aoip_trasp_addr_v6()
+static void test_enc_dec_aoip_trasp_addr_v6(void)
{
struct sockaddr_storage enc_addr;
struct sockaddr_storage dec_addr;
@@ -875,7 +924,29 @@ static void test_enc_dec_aoip_trasp_addr_v6()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_speech_codec()
+static void test_enc_aoip_trasp_addr_msg_too_small(void)
+{
+ struct msgb *msg;
+ struct sockaddr_storage enc_addr;
+ struct sockaddr_in enc_addr_in;
+ uint8_t rc_enc;
+
+ memset(&enc_addr_in, 0, sizeof(enc_addr_in));
+ enc_addr_in.sin_family = AF_INET;
+ enc_addr_in.sin_port = htons(1234);
+ inet_aton("255.0.255.255", &enc_addr_in.sin_addr);
+
+ memset(&enc_addr, 0, sizeof(enc_addr));
+ memcpy(&enc_addr, &enc_addr_in, sizeof(enc_addr_in));
+
+ msg = msgb_alloc(7, "output buffer");
+ rc_enc = gsm0808_enc_aoip_trasp_addr(msg, &enc_addr);
+ OSMO_ASSERT(rc_enc == 0);
+
+ msgb_free(msg);
+}
+
+static void test_gsm0808_enc_dec_speech_codec(void)
{
struct gsm0808_speech_codec enc_sc = {
.pi = true,
@@ -888,7 +959,7 @@ static void test_gsm0808_enc_dec_speech_codec()
int rc_dec;
msg = msgb_alloc(1024, "output buffer");
- rc_enc = gsm0808_enc_speech_codec(msg, &enc_sc);
+ rc_enc = gsm0808_enc_speech_codec2(msg, &enc_sc);
OSMO_ASSERT(rc_enc == 3);
rc_dec = gsm0808_dec_speech_codec(&dec_sc, msg->data + 2, msg->len - 2);
@@ -900,7 +971,7 @@ static void test_gsm0808_enc_dec_speech_codec()
}
-static void test_gsm0808_enc_dec_speech_codec_with_cfg()
+static void test_gsm0808_enc_dec_speech_codec_with_cfg(void)
{
struct gsm0808_speech_codec enc_sc = {
.pi = true,
@@ -914,7 +985,7 @@ static void test_gsm0808_enc_dec_speech_codec_with_cfg()
int rc_dec;
msg = msgb_alloc(1024, "output buffer");
- rc_enc = gsm0808_enc_speech_codec(msg, &enc_sc);
+ rc_enc = gsm0808_enc_speech_codec2(msg, &enc_sc);
OSMO_ASSERT(rc_enc == 5);
rc_dec = gsm0808_dec_speech_codec(&dec_sc, msg->data + 2, msg->len - 2);
@@ -925,7 +996,7 @@ static void test_gsm0808_enc_dec_speech_codec_with_cfg()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_speech_codec_ext_with_cfg()
+static void test_gsm0808_enc_dec_speech_codec_ext_with_cfg(void)
{
struct gsm0808_speech_codec enc_sc = {
.pi = true,
@@ -939,7 +1010,7 @@ static void test_gsm0808_enc_dec_speech_codec_ext_with_cfg()
int rc_dec;
msg = msgb_alloc(1024, "output buffer");
- rc_enc = gsm0808_enc_speech_codec(msg, &enc_sc);
+ rc_enc = gsm0808_enc_speech_codec2(msg, &enc_sc);
OSMO_ASSERT(rc_enc == 5);
rc_dec = gsm0808_dec_speech_codec(&dec_sc, msg->data + 2, msg->len - 2);
@@ -950,7 +1021,7 @@ static void test_gsm0808_enc_dec_speech_codec_ext_with_cfg()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_speech_codec_list()
+static void test_gsm0808_enc_dec_speech_codec_list(void)
{
struct gsm0808_speech_codec_list enc_scl = {
.codec = {
@@ -982,7 +1053,7 @@ static void test_gsm0808_enc_dec_speech_codec_list()
int rc_dec;
msg = msgb_alloc(1024, "output buffer");
- rc_enc = gsm0808_enc_speech_codec_list(msg, &enc_scl);
+ rc_enc = gsm0808_enc_speech_codec_list2(msg, &enc_scl);
OSMO_ASSERT(rc_enc == 9);
rc_dec = gsm0808_dec_speech_codec_list(&dec_scl, msg->data + 2, msg->len - 2);
@@ -993,7 +1064,7 @@ static void test_gsm0808_enc_dec_speech_codec_list()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_empty_speech_codec_list()
+static void test_gsm0808_enc_dec_empty_speech_codec_list(void)
{
struct gsm0808_speech_codec_list enc_scl = {
.len = 0,
@@ -1004,7 +1075,7 @@ static void test_gsm0808_enc_dec_empty_speech_codec_list()
int rc_dec;
msg = msgb_alloc(1024, "output buffer");
- rc_enc = gsm0808_enc_speech_codec_list(msg, &enc_scl);
+ rc_enc = gsm0808_enc_speech_codec_list2(msg, &enc_scl);
OSMO_ASSERT(rc_enc == 2);
rc_dec = gsm0808_dec_speech_codec_list(&dec_scl, msg->data + 2, msg->len - 2);
@@ -1015,7 +1086,89 @@ static void test_gsm0808_enc_dec_empty_speech_codec_list()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_channel_type()
+static void test_gsm0808_enc_dec_channel_type_data(void)
+{
+ struct gsm0808_channel_type enc_ct = {
+ .ch_indctr = GSM0808_CHAN_DATA,
+ .ch_rate_type = GSM0808_DATA_HALF_PREF,
+
+ .data_transparent = true,
+ .data_rate = GSM0808_DATA_RATE_TRANSP_4k8,
+ };
+ struct gsm0808_channel_type dec_ct = {};
+ struct msgb *msg;
+ uint8_t ct_enc_expected[] = { GSM0808_IE_CHANNEL_TYPE,
+ 0x03, 0x02, 0x0b, 0x11,
+ };
+ uint8_t rc_enc;
+ int rc_dec;
+
+ msg = msgb_alloc(1024, "output buffer");
+ rc_enc = gsm0808_enc_channel_type(msg, &enc_ct);
+ OSMO_ASSERT(rc_enc == 5);
+ if (memcmp(ct_enc_expected, msg->data, msg->len)) {
+ printf(" got: %s\n", osmo_hexdump(msg->data, msg->len));
+ printf("expect: %s\n", osmo_hexdump(ct_enc_expected, sizeof(ct_enc_expected)));
+ OSMO_ASSERT(false);
+ }
+
+ rc_dec = gsm0808_dec_channel_type(&dec_ct, msg->data + 2, msg->len - 2);
+ OSMO_ASSERT(rc_dec == 3);
+ OSMO_ASSERT(dec_ct.ch_indctr == enc_ct.ch_indctr);
+ OSMO_ASSERT(dec_ct.ch_rate_type == enc_ct.ch_rate_type);
+ OSMO_ASSERT(dec_ct.data_transparent == enc_ct.data_transparent);
+ OSMO_ASSERT(dec_ct.data_rate == enc_ct.data_rate);
+ OSMO_ASSERT(dec_ct.data_rate_allowed_is_set == enc_ct.data_rate_allowed_is_set);
+ OSMO_ASSERT(dec_ct.data_asym_pref_is_set == enc_ct.data_asym_pref_is_set);
+
+ msgb_free(msg);
+}
+
+static void test_gsm0808_enc_dec_channel_type_data_asym_pref(void)
+{
+ struct gsm0808_channel_type enc_ct = {
+ .ch_indctr = GSM0808_CHAN_DATA,
+ .ch_rate_type = GSM0808_DATA_HALF_PREF,
+
+ .data_transparent = false,
+ .data_rate = GSM0808_DATA_RATE_NON_TRANSP_6k0,
+ .data_rate_allowed_is_set = true,
+ .data_rate_allowed = GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_6k0
+ | GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_12k0
+ | GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_14k5,
+ .data_asym_pref_is_set = true,
+ .data_asym_pref = GSM0808_CT_ASYM_PREF_UL,
+ };
+ struct gsm0808_channel_type dec_ct = {};
+ struct msgb *msg;
+ uint8_t ct_enc_expected[] = { GSM0808_IE_CHANNEL_TYPE,
+ 0x05, 0x02, 0x0b, 0xd1, 0x8b, 0x20,
+ };
+ uint8_t rc_enc;
+ int rc_dec;
+
+ msg = msgb_alloc(1024, "output buffer");
+ rc_enc = gsm0808_enc_channel_type(msg, &enc_ct);
+ OSMO_ASSERT(rc_enc == 7);
+ if (memcmp(ct_enc_expected, msg->data, msg->len)) {
+ printf(" got: %s\n", osmo_hexdump(msg->data, msg->len));
+ printf("expect: %s\n", osmo_hexdump(ct_enc_expected, sizeof(ct_enc_expected)));
+ OSMO_ASSERT(false);
+ }
+
+ rc_dec = gsm0808_dec_channel_type(&dec_ct, msg->data + 2, msg->len - 2);
+ OSMO_ASSERT(rc_dec == 5);
+ OSMO_ASSERT(dec_ct.ch_indctr == enc_ct.ch_indctr);
+ OSMO_ASSERT(dec_ct.ch_rate_type == enc_ct.ch_rate_type);
+ OSMO_ASSERT(dec_ct.data_transparent == enc_ct.data_transparent);
+ OSMO_ASSERT(dec_ct.data_rate == enc_ct.data_rate);
+ OSMO_ASSERT(dec_ct.data_rate_allowed_is_set == enc_ct.data_rate_allowed_is_set);
+ OSMO_ASSERT(dec_ct.data_asym_pref_is_set == enc_ct.data_asym_pref_is_set);
+
+ msgb_free(msg);
+}
+
+static void test_gsm0808_enc_dec_channel_type_speech(void)
{
struct gsm0808_channel_type enc_ct = {
.ch_indctr = GSM0808_CHAN_SPEECH,
@@ -1046,7 +1199,65 @@ static void test_gsm0808_enc_dec_channel_type()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_encrypt_info()
+static void test_gsm0808_enc_dec_channel_type_sign(void)
+{
+ struct gsm0808_channel_type enc_ct = {
+ .ch_indctr = GSM0808_CHAN_SIGN,
+ .ch_rate_type = GSM0808_SIGN_FULL_PREF_NO_CHANGE,
+ };
+ struct gsm0808_channel_type dec_ct = {};
+ struct msgb *msg;
+ uint8_t ct_enc_expected[] = { GSM0808_IE_CHANNEL_TYPE,
+ 0x03, 0x03, 0x1a, 0x00
+ };
+ uint8_t rc_enc;
+ int rc_dec;
+
+ msg = msgb_alloc(1024, "output buffer");
+ rc_enc = gsm0808_enc_channel_type(msg, &enc_ct);
+ OSMO_ASSERT(rc_enc == 5);
+ OSMO_ASSERT(memcmp(ct_enc_expected, msg->data, msg->len) == 0);
+
+ rc_dec = gsm0808_dec_channel_type(&dec_ct, msg->data + 2, msg->len - 2);
+ OSMO_ASSERT(rc_dec == 2);
+ OSMO_ASSERT(enc_ct.ch_indctr == dec_ct.ch_indctr);
+ OSMO_ASSERT(enc_ct.ch_rate_type == dec_ct.ch_rate_type);
+
+ msgb_free(msg);
+}
+
+static void test_gsm0808_dec_channel_type_err(void)
+{
+ struct gsm0808_channel_type ct;
+ int rc;
+
+ /* Unknown channel indicator */
+ const uint8_t hex1[] = { 0x05, 0x0b, 0xa1, 0x25 };
+ rc = gsm0808_dec_channel_type(&ct, hex1, sizeof(hex1));
+ OSMO_ASSERT(rc == -ENOTSUP);
+
+ /* Data: ext in Octet 5 with transparent service */
+ const uint8_t hex2[] = { 0x02, 0x0b, 0x80, 0x00 };
+ rc = gsm0808_dec_channel_type(&ct, hex2, sizeof(hex2));
+ OSMO_ASSERT(rc == -EINVAL);
+
+ /* Data: ext in Octet 5, but too short */
+ const uint8_t hex3[] = { 0x02, 0x0b, 0xc0 };
+ rc = gsm0808_dec_channel_type(&ct, hex3, sizeof(hex3));
+ OSMO_ASSERT(rc == -EOVERFLOW);
+
+ /* Data: ext in Octet 5a, but too short */
+ const uint8_t hex4[] = { 0x02, 0x0b, 0xc0, 0x80 };
+ rc = gsm0808_dec_channel_type(&ct, hex4, sizeof(hex4));
+ OSMO_ASSERT(rc == -EOVERFLOW);
+
+ /* Speech: extension bit is set in last byte */
+ const uint8_t hex5[] = { 0x01, 0x0b, 0xa1, 0xa5 };
+ rc = gsm0808_dec_channel_type(&ct, hex5, sizeof(hex5));
+ OSMO_ASSERT(rc == -EOVERFLOW);
+}
+
+static void test_gsm0808_enc_dec_encrypt_info(void)
{
struct gsm0808_encrypt_info enc_ei = {
.perm_algo = { GSM0808_ALG_ID_A5_0, GSM0808_ALG_ID_A5_1 },
@@ -1076,7 +1287,20 @@ static void test_gsm0808_enc_dec_encrypt_info()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_lac()
+static void test_gsm0808_dec_cell_id_list_srvcc(void)
+{
+ /* taken from a pcap file of a real-world 3rd party MSC (SYS#5838) */
+ const uint8_t enc_cil[] = { 0x0b, 0x2, 0xf2, 0x10, 0x4e, 0x20, 0x15, 0xbe};
+ struct gsm0808_cell_id_list2 dec_cil;
+ int rc;
+
+ rc = gsm0808_dec_cell_id_list2(&dec_cil, enc_cil, sizeof(enc_cil));
+ OSMO_ASSERT(rc == sizeof(enc_cil));
+ OSMO_ASSERT(dec_cil.id_discr == CELL_IDENT_SAI);
+ OSMO_ASSERT(dec_cil.id_list_len == 1);
+}
+
+static void test_gsm0808_enc_dec_cell_id_list_lac(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1103,7 +1327,7 @@ static void test_gsm0808_enc_dec_cell_id_list_lac()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_single_lac()
+static void test_gsm0808_enc_dec_cell_id_list_single_lac(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1132,7 +1356,7 @@ static void test_gsm0808_enc_dec_cell_id_list_single_lac()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_multi_lac()
+static void test_gsm0808_enc_dec_cell_id_list_multi_lac(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1168,7 +1392,7 @@ static void test_gsm0808_enc_dec_cell_id_list_multi_lac()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_bss()
+static void test_gsm0808_enc_dec_cell_id_list_bss(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1191,7 +1415,7 @@ static void test_gsm0808_enc_dec_cell_id_list_bss()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_multi_lai_and_lac()
+static void test_gsm0808_enc_dec_cell_id_list_multi_lai_and_lac(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1247,7 +1471,7 @@ static void test_gsm0808_enc_dec_cell_id_list_multi_lai_and_lac()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_multi_ci()
+static void test_gsm0808_enc_dec_cell_id_list_multi_ci(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1281,7 +1505,7 @@ static void test_gsm0808_enc_dec_cell_id_list_multi_ci()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_multi_lac_and_ci()
+static void test_gsm0808_enc_dec_cell_id_list_multi_lac_and_ci(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1322,7 +1546,7 @@ static void test_gsm0808_enc_dec_cell_id_list_multi_lac_and_ci()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_multi_global()
+static void test_gsm0808_enc_dec_cell_id_list_multi_global(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1395,7 +1619,7 @@ static void print_cil(const struct gsm0808_cell_id_list2 *cil)
printf(" cell_id_list == %s\n", gsm0808_cell_id_list_name(cil));
}
-void test_cell_id_list_add() {
+void test_cell_id_list_add(void) {
size_t zu;
const struct gsm0808_cell_id_list2 cgi1 = {
@@ -1577,7 +1801,7 @@ void test_cell_id_list_add() {
printf("------- %s done\n", __func__);
}
-static void test_gsm0808_enc_dec_cell_id_lac()
+static void test_gsm0808_enc_dec_cell_id_lac(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_LAC,
@@ -1603,7 +1827,7 @@ static void test_gsm0808_enc_dec_cell_id_lac()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_bss()
+static void test_gsm0808_enc_dec_cell_id_bss(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_BSS,
@@ -1625,7 +1849,7 @@ static void test_gsm0808_enc_dec_cell_id_bss()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_no_cell()
+static void test_gsm0808_enc_dec_cell_id_no_cell(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_NO_CELL,
@@ -1647,7 +1871,7 @@ static void test_gsm0808_enc_dec_cell_id_no_cell()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_lai_and_lac()
+static void test_gsm0808_enc_dec_cell_id_lai_and_lac(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_LAI_AND_LAC,
@@ -1678,7 +1902,7 @@ static void test_gsm0808_enc_dec_cell_id_lai_and_lac()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_ci()
+static void test_gsm0808_enc_dec_cell_id_ci(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_CI,
@@ -1701,7 +1925,7 @@ static void test_gsm0808_enc_dec_cell_id_ci()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_lac_and_ci()
+static void test_gsm0808_enc_dec_cell_id_lac_and_ci(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_LAC_AND_CI,
@@ -1728,7 +1952,7 @@ static void test_gsm0808_enc_dec_cell_id_lac_and_ci()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_global()
+static void test_gsm0808_enc_dec_cell_id_global(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_WHOLE_GLOBAL,
@@ -1760,7 +1984,7 @@ static void test_gsm0808_enc_dec_cell_id_global()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_global_ps()
+static void test_gsm0808_enc_dec_cell_id_global_ps(void)
{
struct gsm0808_cell_id enc_cgi = {
.id_discr = CELL_IDENT_WHOLE_GLOBAL,
@@ -1802,29 +2026,81 @@ static void test_gsm0808_enc_dec_cell_id_global_ps()
msgb_free(msg_cgi_ps);
}
+static void print_s15_s0(uint16_t s15_s0, bool full_rate)
+{
+ int i;
+ printf(" S15-S0 = 0x%04x = 0b" OSMO_BIN_SPEC OSMO_BIN_SPEC "\n", s15_s0,
+ OSMO_BIN_PRINT(s15_s0 >> 8), OSMO_BIN_PRINT(s15_s0));
+ for (i = 0; i < 16; i++) {
+ uint8_t modes;
+ int m;
+ int space;
+
+ if (!(s15_s0 & (1 << i)))
+ continue;
+
+ space = 6;
+ if (i < 10)
+ space++;
+
+ printf(" S%d", i);
+
+ modes = gsm0808_amr_modes_from_cfg[full_rate ? 1 : 0][i];
+ if (!modes) {
+ printf(" (empty)\n");
+ continue;
+ }
+
+ for (m = 0; m < 8; m++) {
+ if (!(modes & (1 << m))) {
+ /* avoid whitespace at line ends -- accumulate whitespace width until there is
+ * non-whitespace to actually be printed.*/
+ space += 8;
+ continue;
+ }
+ printf("%*s", space, gsm0808_amr_mode_name(m));
+ space = 8;
+ }
+ printf("\n");
+ }
+}
+
+static void print_mr_cfg(const struct gsm48_multi_rate_conf *cfg)
+{
+ printf(" cfg.smod=%u spare=%u icmi=%u nscb=%u ver=%u\n",
+ cfg->smod, cfg->spare, cfg->icmi, cfg->nscb, cfg->ver);
+ printf(" ");
+#define PRINT_MODE_BIT(NAME) do { \
+ if (cfg->NAME) \
+ printf(" " #NAME "=1"); \
+ else \
+ printf(" -------"); \
+ } while (0)
+ PRINT_MODE_BIT(m4_75);
+ PRINT_MODE_BIT(m5_15);
+ PRINT_MODE_BIT(m5_90);
+ PRINT_MODE_BIT(m6_70);
+ PRINT_MODE_BIT(m7_40);
+ PRINT_MODE_BIT(m7_95);
+ PRINT_MODE_BIT(m10_2);
+ PRINT_MODE_BIT(m12_2);
+ printf("\n");
+}
+
static void test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(struct gsm48_multi_rate_conf *cfg)
{
uint16_t s15_s0;
printf("Input:\n");
- printf(" m4_75= %u smod= %u\n", cfg->m4_75, cfg->smod);
- printf(" m5_15= %u spare= %u\n", cfg->m5_15, cfg->spare);
- printf(" m5_90= %u icmi= %u\n", cfg->m5_90, cfg->icmi);
- printf(" m6_70= %u nscb= %u\n", cfg->m6_70, cfg->nscb);
- printf(" m7_40= %u ver= %u\n", cfg->m7_40, cfg->ver);
- printf(" m7_95= %u\n", cfg->m7_95);
- printf(" m10_2= %u\n", cfg->m10_2);
- printf(" m12_2= %u\n", cfg->m12_2);
+ print_mr_cfg(cfg);
s15_s0 = gsm0808_sc_cfg_from_gsm48_mr_cfg(cfg, true);
printf("Result (fr):\n");
- printf(" S15-S0 = %04x = 0b" OSMO_BIN_SPEC OSMO_BIN_SPEC "\n", s15_s0,
- OSMO_BIN_PRINT(s15_s0 >> 8), OSMO_BIN_PRINT(s15_s0));
+ print_s15_s0(s15_s0, true);
s15_s0 = gsm0808_sc_cfg_from_gsm48_mr_cfg(cfg, false);
printf("Result (hr):\n");
- printf(" S15-S0 = %04x = 0b" OSMO_BIN_SPEC OSMO_BIN_SPEC "\n", s15_s0,
- OSMO_BIN_PRINT(s15_s0 >> 8), OSMO_BIN_PRINT(s15_s0));
+ print_s15_s0(s15_s0, false);
printf("\n");
}
@@ -2025,20 +2301,12 @@ static void test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single(uint16_t s15_s0)
int rc;
printf("Input:\n");
- printf(" S15-S0 = %04x = 0b" OSMO_BIN_SPEC OSMO_BIN_SPEC "\n", s15_s0,
- OSMO_BIN_PRINT(s15_s0 >> 8), OSMO_BIN_PRINT(s15_s0));
+ print_s15_s0(s15_s0, true);
rc = gsm48_mr_cfg_from_gsm0808_sc_cfg(&cfg, s15_s0);
printf("Output:\n");
- printf(" m4_75= %u smod= %u\n", cfg.m4_75, cfg.smod);
- printf(" m5_15= %u spare= %u\n", cfg.m5_15, cfg.spare);
- printf(" m5_90= %u icmi= %u\n", cfg.m5_90, cfg.icmi);
- printf(" m6_70= %u nscb= %u\n", cfg.m6_70, cfg.nscb);
- printf(" m7_40= %u ver= %u\n", cfg.m7_40, cfg.ver);
- printf(" m7_95= %u\n", cfg.m7_95);
- printf(" m10_2= %u\n", cfg.m10_2);
- printf(" m12_2= %u\n", cfg.m12_2);
+ print_mr_cfg(&cfg);
if (rc != 0)
printf(" Result invalid!\n");
@@ -2046,7 +2314,7 @@ static void test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single(uint16_t s15_s0)
printf("\n");
}
-void test_gsm48_mr_cfg_from_gsm0808_sc_cfg()
+void test_gsm48_mr_cfg_from_gsm0808_sc_cfg(void)
{
printf("Testing gsm48_mr_cfg_from_gsm0808_sc_cfg():\n");
@@ -2231,7 +2499,7 @@ static const struct test_cell_id_matching_data test_cell_id_matching_tests[] = {
{ .id = cgi_23_042_23_5, .match_id = cgi_23_99_23_5, .expect_match = false, .expect_exact_match = false },
};
-static void test_cell_id_matching()
+static void test_cell_id_matching(void)
{
int i;
bool ok = true;
@@ -2394,7 +2662,7 @@ static const struct gsm0808_cell_id test_gsm0808_cell_id_to_from_cgi_data[] = {
{ .id_discr = 423 },
};
-static void test_gsm0808_cell_id_to_from_cgi()
+static void test_gsm0808_cell_id_to_from_cgi(void)
{
int i;
int j;
@@ -2478,12 +2746,17 @@ int main(int argc, char **argv)
test_enc_dec_aoip_trasp_addr_v4();
test_enc_dec_aoip_trasp_addr_v6();
+ test_enc_aoip_trasp_addr_msg_too_small();
test_gsm0808_enc_dec_speech_codec();
test_gsm0808_enc_dec_speech_codec_ext_with_cfg();
test_gsm0808_enc_dec_speech_codec_with_cfg();
test_gsm0808_enc_dec_speech_codec_list();
test_gsm0808_enc_dec_empty_speech_codec_list();
- test_gsm0808_enc_dec_channel_type();
+ test_gsm0808_enc_dec_channel_type_data();
+ test_gsm0808_enc_dec_channel_type_data_asym_pref();
+ test_gsm0808_enc_dec_channel_type_speech();
+ test_gsm0808_enc_dec_channel_type_sign();
+ test_gsm0808_dec_channel_type_err();
test_gsm0808_enc_dec_encrypt_info();
test_gsm0808_enc_dec_cell_id_list_lac();
@@ -2494,6 +2767,7 @@ int main(int argc, char **argv)
test_gsm0808_enc_dec_cell_id_list_multi_ci();
test_gsm0808_enc_dec_cell_id_list_multi_lac_and_ci();
test_gsm0808_enc_dec_cell_id_list_multi_global();
+ test_gsm0808_dec_cell_id_list_srvcc();
test_cell_id_list_add();
@@ -2515,6 +2789,7 @@ int main(int argc, char **argv)
test_gsm0808_cell_id_to_from_cgi();
test_dec_confusion();
+ test_dec_perform_location_report_sys5891();
printf("Done\n");
return EXIT_SUCCESS;
diff --git a/tests/gsm0808/gsm0808_test.ok b/tests/gsm0808/gsm0808_test.ok
index eaae7a69..2329fcdc 100644
--- a/tests/gsm0808/gsm0808_test.ok
+++ b/tests/gsm0808/gsm0808_test.ok
@@ -87,549 +87,549 @@ test_gsm0808_enc_dec_cell_id_lac_and_ci: encoded: 05 05 01 04 23 02 35 (rc = 7)
test_gsm0808_enc_dec_cell_id_global: encoded: 05 08 00 21 63 54 23 42 04 23 (rc = 10)
Testing gsm0808_sc_cfg_from_gsm48_mr_cfg():
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- ------- ------- ------- ------- -------
Result (fr):
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Result (hr):
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Input:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 ------- ------- ------- ------- ------- ------- -------
Result (fr):
- S15-S0 = 5701 = 0b0101011100000001
+ S15-S0 = 0x5701 = 0b0101011100000001
+ S0 4.75
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0701 = 0b0000011100000001
+ S15-S0 = 0x0701 = 0b0000011100000001
+ S0 4.75
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 1 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- m5_15=1 ------- ------- ------- ------- ------- -------
Result (fr):
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Result (hr):
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- m5_90=1 ------- ------- ------- ------- -------
Result (fr):
- S15-S0 = 5704 = 0b0101011100000100
+ S15-S0 = 0x5704 = 0b0101011100000100
+ S2 5.90
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0704 = 0b0000011100000100
+ S15-S0 = 0x0704 = 0b0000011100000100
+ S2 5.90
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 1 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- m6_70=1 ------- ------- ------- -------
Result (fr):
- S15-S0 = 1608 = 0b0001011000001000
+ S15-S0 = 0x1608 = 0b0001011000001000
+ S3 6.70
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
Result (hr):
- S15-S0 = 0608 = 0b0000011000001000
+ S15-S0 = 0x0608 = 0b0000011000001000
+ S3 6.70
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- ------- m7_40=1 ------- ------- -------
Result (fr):
- S15-S0 = 0410 = 0b0000010000010000
+ S15-S0 = 0x0410 = 0b0000010000010000
+ S4 7.40
+ S10 4.75 5.90 6.70 7.40
Result (hr):
- S15-S0 = 0410 = 0b0000010000010000
+ S15-S0 = 0x0410 = 0b0000010000010000
+ S4 7.40
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 1
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- ------- ------- m7_95=1 ------- -------
Result (fr):
- S15-S0 = 4020 = 0b0100000000100000
+ S15-S0 = 0x4020 = 0b0100000000100000
+ S5 7.95
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0020 = 0b0000000000100000
+ S15-S0 = 0x0020 = 0b0000000000100000
+ S5 7.95
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 1
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- ------- ------- ------- m10_2=1 -------
Result (fr):
- S15-S0 = 1040 = 0b0001000001000000
+ S15-S0 = 0x1040 = 0b0001000001000000
+ S6 10.2
+ S12 4.75 5.90 6.70 10.2
Result (hr):
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- ------- ------- ------- ------- m12_2=1
Result (fr):
- S15-S0 = 4080 = 0b0100000010000000
+ S15-S0 = 0x4080 = 0b0100000010000000
+ S7 12.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Input:
- m4_75= 1 smod= 0
- m5_15= 1 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 1 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 m5_15=1 m5_90=1 m6_70=1 ------- ------- ------- -------
Result (fr):
- S15-S0 = 570d = 0b0101011100001101
+ S15-S0 = 0x570d = 0b0101011100001101
+ S0 4.75
+ S2 5.90
+ S3 6.70
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 070d = 0b0000011100001101
+ S15-S0 = 0x070d = 0b0000011100001101
+ S0 4.75
+ S2 5.90
+ S3 6.70
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 1
- m10_2= 1
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- ------- m7_40=1 m7_95=1 m10_2=1 m12_2=1
Result (fr):
- S15-S0 = 54f0 = 0b0101010011110000
+ S15-S0 = 0x54f0 = 0b0101010011110000
+ S4 7.40
+ S5 7.95
+ S6 10.2
+ S7 12.2
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0430 = 0b0000010000110000
+ S15-S0 = 0x0430 = 0b0000010000110000
+ S4 7.40
+ S5 7.95
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 1 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 1
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- m5_90=1 m6_70=1 ------- ------- m10_2=1 m12_2=1
Result (fr):
- S15-S0 = 57cc = 0b0101011111001100
+ S15-S0 = 0x57cc = 0b0101011111001100
+ S2 5.90
+ S3 6.70
+ S6 10.2
+ S7 12.2
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 070c = 0b0000011100001100
+ S15-S0 = 0x070c = 0b0000011100001100
+ S2 5.90
+ S3 6.70
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 1 smod= 0
- m5_15= 1 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 1
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 m5_15=1 ------- ------- m7_40=1 m7_95=1 ------- -------
Result (fr):
- S15-S0 = 5731 = 0b0101011100110001
+ S15-S0 = 0x5731 = 0b0101011100110001
+ S0 4.75
+ S4 7.40
+ S5 7.95
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0731 = 0b0000011100110001
+ S15-S0 = 0x0731 = 0b0000011100110001
+ S0 4.75
+ S4 7.40
+ S5 7.95
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 1 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 1 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 1
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- m5_15=1 ------- m6_70=1 ------- m7_95=1 ------- m12_2=1
Result (fr):
- S15-S0 = 56a8 = 0b0101011010101000
+ S15-S0 = 0x56a8 = 0b0101011010101000
+ S3 6.70
+ S5 7.95
+ S7 12.2
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0628 = 0b0000011000101000
+ S15-S0 = 0x0628 = 0b0000011000101000
+ S3 6.70
+ S5 7.95
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 0
- m10_2= 1
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- m10_2=1 -------
Result (fr):
- S15-S0 = 5755 = 0b0101011101010101
+ S15-S0 = 0x5755 = 0b0101011101010101
+ S0 4.75
+ S2 5.90
+ S4 7.40
+ S6 10.2
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0717 = 0b0000011100010111
-
-Input:
- m4_75= 1 smod= 0
- m5_15= 1 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 1 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 1
- m10_2= 1
- m12_2= 1
+ S15-S0 = 0x0717 = 0b0000011100010111
+ S0 4.75
+ S1 4.75 5.90 7.40
+ S2 5.90
+ S4 7.40
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+
+Input:
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 m5_15=1 m5_90=1 m6_70=1 m7_40=1 m7_95=1 m10_2=1 m12_2=1
Result (fr):
- S15-S0 = 57ff = 0b0101011111111111
+ S15-S0 = 0x57ff = 0b0101011111111111
+ S0 4.75
+ S1 4.75 5.90 7.40 12.2
+ S2 5.90
+ S3 6.70
+ S4 7.40
+ S5 7.95
+ S6 10.2
+ S7 12.2
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 073f = 0b0000011100111111
-
-Input:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ S15-S0 = 0x073f = 0b0000011100111111
+ S0 4.75
+ S1 4.75 5.90 7.40
+ S2 5.90
+ S3 6.70
+ S4 7.40
+ S5 7.95
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+
+Input:
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Result (fr):
- S15-S0 = 5797 = 0b0101011110010111
+ S15-S0 = 0x5797 = 0b0101011110010111
+ S0 4.75
+ S1 4.75 5.90 7.40 12.2
+ S2 5.90
+ S4 7.40
+ S7 12.2
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0717 = 0b0000011100010111
-
-Input:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ S15-S0 = 0x0717 = 0b0000011100010111
+ S0 4.75
+ S1 4.75 5.90 7.40
+ S2 5.90
+ S4 7.40
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+
+Input:
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- -------
Result (fr):
- S15-S0 = 5715 = 0b0101011100010101
+ S15-S0 = 0x5715 = 0b0101011100010101
+ S0 4.75
+ S2 5.90
+ S4 7.40
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0717 = 0b0000011100010111
+ S15-S0 = 0x0717 = 0b0000011100010111
+ S0 4.75
+ S1 4.75 5.90 7.40
+ S2 5.90
+ S4 7.40
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Testing gsm48_mr_cfg_from_gsm0808_sc_cfg():
Input:
- S15-S0 = ff03 = 0b1111111100000011
+ S15-S0 = 0xff03 = 0b1111111100000011
+ S0 4.75
+ S1 4.75 5.90 7.40 12.2
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S11 (empty)
+ S12 4.75 5.90 6.70 10.2
+ S13 (empty)
+ S14 4.75 5.90 7.95 12.2
+ S15 (empty)
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Input:
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- ------- ------- -------
Result invalid!
Input:
- S15-S0 = ff06 = 0b1111111100000110
+ S15-S0 = 0xff06 = 0b1111111100000110
+ S1 4.75 5.90 7.40 12.2
+ S2 5.90
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S11 (empty)
+ S12 4.75 5.90 6.70 10.2
+ S13 (empty)
+ S14 4.75 5.90 7.95 12.2
+ S15 (empty)
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Input:
- S15-S0 = 3e08 = 0b0011111000001000
+ S15-S0 = 0x3e08 = 0b0011111000001000
+ S3 6.70
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S11 (empty)
+ S12 4.75 5.90 6.70 10.2
+ S13 (empty)
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 1 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- m6_70=1 ------- ------- ------- -------
Input:
- S15-S0 = 0c12 = 0b0000110000010010
+ S15-S0 = 0x0c12 = 0b0000110000010010
+ S1 4.75 5.90 7.40 12.2
+ S4 7.40
+ S10 4.75 5.90 6.70 7.40
+ S11 (empty)
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Input:
- S15-S0 = c020 = 0b1100000000100000
+ S15-S0 = 0xc020 = 0b1100000000100000
+ S5 7.95
+ S14 4.75 5.90 7.95 12.2
+ S15 (empty)
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 1
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- m7_95=1 ------- -------
Input:
- S15-S0 = 3040 = 0b0011000001000000
+ S15-S0 = 0x3040 = 0b0011000001000000
+ S6 10.2
+ S12 4.75 5.90 6.70 10.2
+ S13 (empty)
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 1
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- ------- m10_2=1 -------
Input:
- S15-S0 = c082 = 0b1100000010000010
+ S15-S0 = 0xc082 = 0b1100000010000010
+ S1 4.75 5.90 7.40 12.2
+ S7 12.2
+ S14 4.75 5.90 7.95 12.2
+ S15 (empty)
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Input:
- S15-S0 = 0001 = 0b0000000000000001
+ S15-S0 = 0x0001 = 0b0000000000000001
+ S0 4.75
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- ------- ------- ------- ------- ------- -------
Input:
- S15-S0 = 0002 = 0b0000000000000010
+ S15-S0 = 0x0002 = 0b0000000000000010
+ S1 4.75 5.90 7.40 12.2
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Input:
- S15-S0 = 0004 = 0b0000000000000100
+ S15-S0 = 0x0004 = 0b0000000000000100
+ S2 5.90
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- m5_90=1 ------- ------- ------- ------- -------
Input:
- S15-S0 = 0008 = 0b0000000000001000
+ S15-S0 = 0x0008 = 0b0000000000001000
+ S3 6.70
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 1 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- m6_70=1 ------- ------- ------- -------
Input:
- S15-S0 = 0010 = 0b0000000000010000
+ S15-S0 = 0x0010 = 0b0000000000010000
+ S4 7.40
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- m7_40=1 ------- ------- -------
Input:
- S15-S0 = 0020 = 0b0000000000100000
+ S15-S0 = 0x0020 = 0b0000000000100000
+ S5 7.95
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 1
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- m7_95=1 ------- -------
Input:
- S15-S0 = 0040 = 0b0000000001000000
+ S15-S0 = 0x0040 = 0b0000000001000000
+ S6 10.2
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 1
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- ------- m10_2=1 -------
Input:
- S15-S0 = 0080 = 0b0000000010000000
+ S15-S0 = 0x0080 = 0b0000000010000000
+ S7 12.2
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- ------- ------- m12_2=1
Input:
- S15-S0 = 0058 = 0b0000000001011000
+ S15-S0 = 0x0058 = 0b0000000001011000
+ S3 6.70
+ S4 7.40
+ S6 10.2
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 1 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 1
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- m6_70=1 m7_40=1 ------- m10_2=1 -------
Input:
- S15-S0 = 0021 = 0b0000000000100001
+ S15-S0 = 0x0021 = 0b0000000000100001
+ S0 4.75
+ S5 7.95
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 1
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- ------- ------- ------- m7_95=1 ------- -------
Input:
- S15-S0 = 0084 = 0b0000000010000100
+ S15-S0 = 0x0084 = 0b0000000010000100
+ S2 5.90
+ S7 12.2
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- m5_90=1 ------- ------- ------- ------- m12_2=1
Input:
- S15-S0 = 0086 = 0b0000000010000110
+ S15-S0 = 0x0086 = 0b0000000010000110
+ S1 4.75 5.90 7.40 12.2
+ S2 5.90
+ S7 12.2
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Input:
- S15-S0 = 000a = 0b0000000000001010
+ S15-S0 = 0x000a = 0b0000000000001010
+ S1 4.75 5.90 7.40 12.2
+ S3 6.70
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Result invalid!
Input:
- S15-S0 = 0079 = 0b0000000001111001
+ S15-S0 = 0x0079 = 0b0000000001111001
+ S0 4.75
+ S3 6.70
+ S4 7.40
+ S5 7.95
+ S6 10.2
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 1 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 1
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- ------- m6_70=1 m7_40=1 m7_95=1 ------- -------
Result invalid!
Input:
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- ------- ------- -------
Result invalid!
Input:
- S15-S0 = ffff = 0b1111111111111111
+ S15-S0 = 0xffff = 0b1111111111111111
+ S0 4.75
+ S1 4.75 5.90 7.40 12.2
+ S2 5.90
+ S3 6.70
+ S4 7.40
+ S5 7.95
+ S6 10.2
+ S7 12.2
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S11 (empty)
+ S12 4.75 5.90 6.70 10.2
+ S13 (empty)
+ S14 4.75 5.90 7.95 12.2
+ S15 (empty)
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Result invalid!
@@ -916,4 +916,5 @@ Testing decoding CONFUSION
Diagnostics error octet location 0 (Error location not determined)
Diagnostics error bit location 15 (Reserved value)
Diagnostics message that provoked the error: 00 03 25 03 25
+Testing decoding Perform Location Report SYS#5891
Done
diff --git a/tests/gsm23003/gsm23003_test.c b/tests/gsm23003/gsm23003_test.c
index eac5a115..77d3173e 100644
--- a/tests/gsm23003/gsm23003_test.c
+++ b/tests/gsm23003/gsm23003_test.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -58,7 +54,7 @@ static struct {
{ NULL, false },
};
-bool test_valid_imsi()
+bool test_valid_imsi(void)
{
int i;
bool pass = true;
@@ -101,7 +97,7 @@ static struct {
{ NULL, false },
};
-bool test_valid_msisdn()
+bool test_valid_msisdn(void)
{
int i;
bool pass = true;
@@ -142,7 +138,7 @@ static struct {
{ NULL, false, false },
};
-bool test_valid_imei()
+bool test_valid_imei(void)
{
int i;
bool pass = true;
@@ -189,7 +185,7 @@ static struct test_mnc_from_str test_mnc_from_strs[] = {
{ "023 ", { -EINVAL, 0, false } },
};
-static bool test_mnc_from_str()
+static bool test_mnc_from_str(void)
{
int i;
bool pass = true;
@@ -213,7 +209,7 @@ static bool test_mnc_from_str()
return pass;
}
-static bool test_gummei_name()
+static bool test_gummei_name(void)
{
static const struct osmo_gummei gummei = {
.plmn = { .mcc = 901, .mnc = 70 },
@@ -230,7 +226,7 @@ static bool test_gummei_name()
return pass;
}
-static bool test_domain_gen()
+static bool test_domain_gen(void)
{
static const struct osmo_gummei gummei = {
.plmn = { .mcc = 901, .mnc = 70 },
@@ -256,7 +252,7 @@ static bool test_domain_gen()
}
-static bool test_domain_parse()
+static bool test_domain_parse(void)
{
static const char *mme_dom_valid = "mmec01.mmegiA001.mme.epc.mnc070.mcc901.3gppnetwork.org";
static const char *home_dom_valid = "epc.mnc070.mcc901.3gppnetwork.org";
diff --git a/tests/gsm23236/gsm23236_test.c b/tests/gsm23236/gsm23236_test.c
index 14c5ce38..6cbdbeb2 100644
--- a/tests/gsm23236/gsm23236_test.c
+++ b/tests/gsm23236/gsm23236_test.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -142,7 +138,7 @@ struct nri_v_get_set_test nri_v_get_set_tests[] = {
},
};
-void test_nri_v_get_set()
+void test_nri_v_get_set(void)
{
struct nri_v_get_set_test *t;
@@ -226,7 +222,7 @@ struct nri_validate_tc nri_validate_tests[] = {
{ .nri = INT16_MAX, .nri_bitlen = 0, .expect_rc = 1 },
};
-void test_nri_validate()
+void test_nri_validate(void)
{
struct nri_validate_tc *t;
printf("\n%s()\n", __func__);
@@ -331,7 +327,7 @@ struct nri_range_validate_tc nri_range_validate_tests[] = {
};
-void test_nri_range_validate()
+void test_nri_range_validate(void)
{
struct nri_range_validate_tc *t;
printf("\n%s()\n", __func__);
@@ -361,7 +357,7 @@ void dump_list(const struct osmo_nri_ranges *nri_ranges)
printf("};\n");
}
-void test_nri_list()
+void test_nri_list(void)
{
struct osmo_nri_ranges *nri_ranges = osmo_nri_ranges_alloc(ctx);
printf("\n%s()\n", __func__);
@@ -549,7 +545,7 @@ void test_nri_list()
DEL(100, 1);
}
-void test_nri_limit_by_ranges()
+void test_nri_limit_by_ranges(void)
{
const uint8_t nri_bitlen = 8;
const int16_t expect_nri_vals[] = { 10, 20, 21, 30, 31, 32 };
@@ -598,7 +594,7 @@ void test_nri_limit_by_ranges()
}
}
-int main()
+int main(int argc, char **argv)
{
ctx = talloc_named_const(NULL, 0, "nri_test");
diff --git a/tests/gsm29205/gsm29205_test.c b/tests/gsm29205/gsm29205_test.c
index 44c3453c..6598f894 100644
--- a/tests/gsm29205/gsm29205_test.c
+++ b/tests/gsm29205/gsm29205_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/gsm/gsm29205.h>
@@ -32,7 +28,7 @@
#include <string.h>
#include <errno.h>
-static void test_gcr()
+static void test_gcr(void)
{
static const uint8_t res[] = {
0x03, /* .net_len */
diff --git a/tests/gsm44021/frame_csd_test.c b/tests/gsm44021/frame_csd_test.c
new file mode 100644
index 00000000..98efdacb
--- /dev/null
+++ b/tests/gsm44021/frame_csd_test.c
@@ -0,0 +1,93 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/isdn/v110.h>
+#include <osmocom/gsm/gsm44021.h>
+
+
+static void fill_v110_frame(struct osmo_v110_decoded_frame *fr)
+{
+ unsigned int i;
+
+ memset(fr, 0, sizeof(*fr));
+
+ /* we abuse the fact that ubit_t is 8bit so we can actually
+ * store integer values to clearly identify which bit ends up where */
+
+ /* D1..D48: 101..148 */
+ for (i = 0; i < ARRAY_SIZE(fr->d_bits); i++)
+ fr->d_bits[i] = 101 + i;
+ /* E1..E7: 201..207 */
+ for (i = 0; i < ARRAY_SIZE(fr->e_bits); i++)
+ fr->e_bits[i] = 201 + i;
+ /* S1..S9: 211..219 */
+ for (i = 0; i < ARRAY_SIZE(fr->s_bits); i++)
+ fr->s_bits[i] = 211 + i;
+ /* X1..X2: 221..222 */
+ for (i = 0; i < ARRAY_SIZE(fr->x_bits); i++)
+ fr->x_bits[i] = 221 + i;
+}
+
+
+static void test_frame_enc_12k_6k(void)
+{
+ struct osmo_v110_decoded_frame fr;
+ ubit_t bits[60];
+
+ printf("Testing Frame Encoding for 12k/6k radio interface rate\n");
+
+ fill_v110_frame(&fr);
+
+ /* run encoder and dump to stdout */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_csd_12k_6k_encode_frame(bits, sizeof(bits), &fr);
+ osmo_csd_ubit_dump(stdout, bits, sizeof(bits));
+
+ /* run decoder on what we just encoded */
+ memset(&fr, 0, sizeof(fr));
+ osmo_csd_12k_6k_decode_frame(&fr, bits, sizeof(bits));
+
+ /* re-encode and dump again 'expout' will match it. */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_csd_12k_6k_encode_frame(bits, sizeof(bits), &fr);
+ osmo_csd_ubit_dump(stdout, bits, sizeof(bits));
+}
+
+static void test_frame_enc_3k6(void)
+{
+ struct osmo_v110_decoded_frame fr;
+ ubit_t bits[36];
+
+ printf("Testing Frame Encoding for 3.6k radio interface rate\n");
+
+ fill_v110_frame(&fr);
+ /* different D-bit numbering for 3k6, see TS 44.021 Section 8.1.4 */
+ for (unsigned int i = 0; i < ARRAY_SIZE(fr.d_bits); i++)
+ fr.d_bits[i] = 101 + i/2;
+
+ /* run encoder and dump to stdout */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_csd_3k6_encode_frame(bits, sizeof(bits), &fr);
+ osmo_csd_ubit_dump(stdout, bits, sizeof(bits));
+
+ /* run decoder on what we just encoded */
+ memset(&fr, 0, sizeof(fr));
+ osmo_csd_3k6_decode_frame(&fr, bits, sizeof(bits));
+
+ /* re-encode and dump again 'expout' will match it. */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_csd_3k6_encode_frame(bits, sizeof(bits), &fr);
+ osmo_csd_ubit_dump(stdout, bits, sizeof(bits));
+}
+
+
+int main(int argc, char **argv)
+{
+ test_frame_enc_12k_6k();
+ printf("\n");
+ test_frame_enc_3k6();
+}
+
diff --git a/tests/gsm44021/frame_csd_test.ok b/tests/gsm44021/frame_csd_test.ok
new file mode 100644
index 00000000..1a5d3f25
--- /dev/null
+++ b/tests/gsm44021/frame_csd_test.ok
@@ -0,0 +1,31 @@
+Testing Frame Encoding for 12k/6k radio interface rate
+101 102 103 104 105 106 211
+107 108 109 110 111 112 221
+113 114 115 116 117 118 213
+119 120 121 122 123 124 214
+204 205 206 207 125 126 127
+128 129 130 216 131 132 133
+134 135 136 222 137 138 139
+140 141 142 218 143 144 145
+146 147 148 219
+101 102 103 104 105 106 211
+107 108 109 110 111 112 221
+113 114 115 116 117 118 213
+119 120 121 122 123 124 214
+204 205 206 207 125 126 127
+128 129 130 216 131 132 133
+134 135 136 222 137 138 139
+140 141 142 218 143 144 145
+146 147 148 219
+
+Testing Frame Encoding for 3.6k radio interface rate
+101 102 103 211 104 105 106 221
+107 108 109 213 110 111 112 214
+204 205 206 207 113 114 115 216
+116 117 118 222 119 120 121 218
+122 123 124 219
+101 102 103 211 104 105 106 221
+107 108 109 213 110 111 112 214
+204 205 206 207 113 114 115 216
+116 117 118 222 119 120 121 218
+122 123 124 219
diff --git a/tests/gsm48/rest_octets_test.c b/tests/gsm48/rest_octets_test.c
index beff6e44..6e11f941 100644
--- a/tests/gsm48/rest_octets_test.c
+++ b/tests/gsm48/rest_octets_test.c
@@ -1,5 +1,5 @@
/*
- * (C) 2021 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
* All Rights Reserved
*
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <string.h>
@@ -81,7 +77,7 @@ static const struct si13_test test_si13_arr[] = {
},
};
-static void test_si13()
+static void test_si13(void)
{
int i, rc;
uint8_t data[GSM_MACBLOCK_LEN];
diff --git a/tests/gsup/gsup_test.c b/tests/gsup/gsup_test.c
index 3d7ea401..fb3708ac 100644
--- a/tests/gsup/gsup_test.c
+++ b/tests/gsup/gsup_test.c
@@ -454,32 +454,32 @@ static void test_gsup_messages_dec_enc(void)
0x51, /* GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS */
};
- static const uint8_t send_e_process_access_signalling_req[] = {
- 0x40, /* OSMO_GSUP_MSGT_E_PROCESS_ACCESS_SIGNALLING_REQUEST */
+ static const uint8_t send_e_send_end_signal_res[] = {
+ 0x3E, /* OSMO_GSUP_MSGT_E_SEND_END_SIGNAL_RESULT */
TEST_IMSI_IE,
- /* Session ID and state (continue) */
+ /* Session ID and state (end) */
0x30, 0x04, 0xde, 0xad, 0xbe, 0xef,
- 0x31, 0x01, 0x02,
+ 0x31, 0x01, 0x03,
TEST_CLASS_INTER_MSC_IE,
TEST_SOURCE_NAME_IE,
TEST_DESTINATION_NAME_IE,
- TEST_AN_APDU_IE, /* (Handover Detect) */
+ TEST_AN_APDU_IE, /* (Handover Complete) */
};
- static const uint8_t send_e_send_end_signal_res[] = {
- 0x3E, /* OSMO_GSUP_MSGT_E_SEND_END_SIGNAL_RESULT */
+ static const uint8_t send_e_process_access_signalling_req[] = {
+ 0x40, /* OSMO_GSUP_MSGT_E_PROCESS_ACCESS_SIGNALLING_REQUEST */
TEST_IMSI_IE,
- /* Session ID and state (end) */
+ /* Session ID and state (continue) */
0x30, 0x04, 0xde, 0xad, 0xbe, 0xef,
- 0x31, 0x01, 0x03,
+ 0x31, 0x01, 0x02,
TEST_CLASS_INTER_MSC_IE,
TEST_SOURCE_NAME_IE,
TEST_DESTINATION_NAME_IE,
- TEST_AN_APDU_IE, /* (Handover Complete) */
+ TEST_AN_APDU_IE, /* (Handover Detect) */
};
static const uint8_t send_e_forward_access_signalling_req [] = {
diff --git a/tests/gsup/gsup_test.err b/tests/gsup/gsup_test.err
index 7b2fda97..73a0776b 100644
--- a/tests/gsup/gsup_test.err
+++ b/tests/gsup/gsup_test.err
@@ -164,7 +164,7 @@ DLGSUP Stopping DLGSUP logging
message 2: tested 21248 modifications, 2577 parse failures
message 3: tested 2816 modifications, 510 parse failures
message 4: tested 3584 modifications, 770 parse failures
- message 5: tested 20736 modifications, 4025 parse failures
+ message 5: tested 20736 modifications, 4517 parse failures
message 6: tested 3584 modifications, 771 parse failures
message 7: tested 3584 modifications, 770 parse failures
message 8: tested 2816 modifications, 510 parse failures
diff --git a/tests/i460_mux/i460_mux_test.c b/tests/i460_mux/i460_mux_test.c
index 7695cb4a..1be35e7d 100644
--- a/tests/i460_mux/i460_mux_test.c
+++ b/tests/i460_mux/i460_mux_test.c
@@ -1,7 +1,7 @@
#include <osmocom/core/utils.h>
-#include <osmocom/gsm/i460_mux.h>
+#include <osmocom/isdn/i460_mux.h>
static void bits_cb(struct osmo_i460_subchan *schan, void *user_data,
const ubit_t *bits, unsigned int num_bits)
diff --git a/tests/it_q/it_q_test.c b/tests/it_q/it_q_test.c
index 28e32d89..6025e392 100644
--- a/tests/it_q/it_q_test.c
+++ b/tests/it_q/it_q_test.c
@@ -68,6 +68,44 @@ static void tc_queue_length(void)
osmo_it_q_destroy(q1);
}
+static void tc_enqueue_dequeue(void)
+{
+ const unsigned int qlen = 12;
+ struct it_q_test1 *item;
+ struct osmo_it_q *q1;
+ int rc;
+
+ ENTER_TC;
+
+ printf("allocating q1\n");
+ q1 = osmo_it_q_alloc(OTC_GLOBAL, "q1", 12, NULL, NULL);
+ OSMO_ASSERT(q1);
+
+ printf("try dequeueing from an empty queue\n");
+ osmo_it_q_dequeue(q1, &item, list);
+ OSMO_ASSERT(item == NULL);
+
+ printf("adding queue entries up to the limit\n");
+ for (unsigned int i = 0; i < qlen; i++) {
+ item = talloc_zero(OTC_GLOBAL, struct it_q_test1);
+ rc = osmo_it_q_enqueue(q1, item, list);
+ OSMO_ASSERT(rc == 0);
+ }
+
+ printf("removing queue entries up to the limit\n");
+ for (unsigned int i = 0; i < qlen; i++) {
+ osmo_it_q_dequeue(q1, &item, list);
+ OSMO_ASSERT(item != NULL);
+ talloc_free(item);
+ }
+
+ printf("try dequeueing from an empty queue\n");
+ osmo_it_q_dequeue(q1, &item, list);
+ OSMO_ASSERT(item == NULL);
+
+ osmo_it_q_destroy(q1);
+}
+
static int g_read_cb_count;
static void q_read_cb(struct osmo_it_q *q, struct llist_head *item)
@@ -115,6 +153,7 @@ int main(int argc, char **argv)
{
tc_alloc();
tc_queue_length();
+ tc_enqueue_dequeue();
tc_eventfd();
return 0;
}
diff --git a/tests/it_q/it_q_test.ok b/tests/it_q/it_q_test.ok
index 7f102c61..e89b78d1 100644
--- a/tests/it_q/it_q_test.ok
+++ b/tests/it_q/it_q_test.ok
@@ -9,6 +9,13 @@ allocating q1
adding queue entries up to the limit
attempting to add more than the limit
+== Entering test case tc_enqueue_dequeue
+allocating q1
+try dequeueing from an empty queue
+adding queue entries up to the limit
+removing queue entries up to the limit
+try dequeueing from an empty queue
+
== Entering test case tc_eventfd
allocating q1
adding 30 queue entries up to the limit
diff --git a/tests/iuup/iuup_test.c b/tests/iuup/iuup_test.c
new file mode 100644
index 00000000..7798aa25
--- /dev/null
+++ b/tests/iuup/iuup_test.c
@@ -0,0 +1,767 @@
+#include <stdint.h>
+#include <stdio.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/select.h>
+
+#include <osmocom/gsm/prim.h>
+#include <osmocom/gsm/iuup.h>
+
+static void *iuup_test_ctx;
+
+static struct osmo_iuup_rnl_config def_configure_req = {
+ .transparent = false,
+ .active = true,
+ .supported_versions_mask = 0x0001,
+ .num_rfci = 3,
+ .num_subflows = 3,
+ .IPTIs_present = true,
+ .rfci = {
+ {.used = 1, .id = 0, .IPTI = 1, .subflow_sizes = {81, 103, 60} },
+ {.used = 1, .id = 1, .IPTI = 7, .subflow_sizes = {39, 0, 0} },
+ {.used = 1, .id = 2, .IPTI = 1, .subflow_sizes = {0, 0, 0} },
+ },
+ /* .delivery_err_sdu = All set to 0 (YES) by default, */
+ .t_init = { .t_ms = IUUP_TIMER_INIT_T_DEFAULT, .n_max = IUUP_TIMER_INIT_N_DEFAULT },
+ .t_ta = { .t_ms = IUUP_TIMER_TA_T_DEFAULT, .n_max = IUUP_TIMER_TA_N_DEFAULT },
+ .t_rc = { .t_ms = IUUP_TIMER_RC_T_DEFAULT, .n_max = IUUP_TIMER_RC_N_DEFAULT },
+};
+
+/* Frame 33, "Initialization", OS#4744 3g_call_23112021.pcapng
+IuUP
+ 1110 .... = PDU Type: Control Procedure (14)
+ .... 00.. = Ack/Nack: Procedure (0)
+ .... ..00 = Frame Number: 0
+ 0000 .... = Mode Version: 0x0
+ .... 0000 = Procedure: Initialization (0)
+ 1101 11.. = Header CRC: 0x37 [correct]
+ .... ..11 1001 1001 = Payload CRC: 0x399
+ 000. .... = Spare: 0x0
+ ...1 .... = TI: IPTIs present in frame (1)
+ .... 011. = Subflows: 3
+ .... ...0 = Chain Indicator: this frame is the last frame for the procedure (0)
+ RFCI 0 Initialization
+ 0... .... = RFCI 0 LRI: Not last RFCI (0x0)
+ .0.. .... = RFCI 0 LI: one octet used (0x0)
+ ..00 0000 = RFCI 0: 0
+ RFCI 0 Flow 0 Len: 81
+ RFCI 0 Flow 1 Len: 103
+ RFCI 0 Flow 2 Len: 60
+ RFCI 1 Initialization
+ 0... .... = RFCI 1 LRI: Not last RFCI (0x0)
+ .0.. .... = RFCI 1 LI: one octet used (0x0)
+ ..00 0001 = RFCI 1: 1
+ RFCI 1 Flow 0 Len: 39
+ RFCI 1 Flow 1 Len: 0
+ RFCI 1 Flow 2 Len: 0
+ RFCI 2 Initialization
+ 1... .... = RFCI 2 LRI: Last RFCI in current frame (0x1)
+ .0.. .... = RFCI 2 LI: one octet used (0x0)
+ ..00 0010 = RFCI 2: 2
+ RFCI 2 Flow 0 Len: 0
+ RFCI 2 Flow 1 Len: 0
+ RFCI 2 Flow 2 Len: 0
+ IPTIs
+ 0001 .... = RFCI 0 IPTI: 0x1
+ .... 0111 = RFCI 1 IPTI: 0x7
+ 0001 .... = RFCI 2 IPTI: 0x1
+ Iu UP Mode Versions Supported: 0x0001
+ 0... .... .... .... = Version 16: not supported (0x0)
+ .0.. .... .... .... = Version 15: not supported (0x0)
+ ..0. .... .... .... = Version 14: not supported (0x0)
+ ...0 .... .... .... = Version 13: not supported (0x0)
+ .... 0... .... .... = Version 12: not supported (0x0)
+ .... .0.. .... .... = Version 11: not supported (0x0)
+ .... ..0. .... .... = Version 10: not supported (0x0)
+ .... ...0 .... .... = Version 9: not supported (0x0)
+ .... .... 0... .... = Version 8: not supported (0x0)
+ .... .... .0.. .... = Version 7: not supported (0x0)
+ .... .... ..0. .... = Version 6: not supported (0x0)
+ .... .... ...0 .... = Version 5: not supported (0x0)
+ .... .... .... 0... = Version 4: not supported (0x0)
+ .... .... .... .0.. = Version 3: not supported (0x0)
+ .... .... .... ..0. = Version 2: not supported (0x0)
+ .... .... .... ...1 = Version 1: supported (0x1)
+ 0000 .... = RFCI Data Pdu Type: PDU type 0 (0x0)
+*/
+static const uint8_t iuup_initialization[] = {
+ 0xe0, 0x00, 0xdf, 0x99, 0x16, 0x00, 0x51, 0x67, 0x3c, 0x01, 0x27, 0x00,
+ 0x00, 0x82, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x01, 0x00
+};
+
+/* Frame 87, "Data RFCI=0 FN = 1", OS#4744 3g_call_23112021.pcapng
+IuUP
+ 0000 .... = PDU Type: Data with CRC (0)
+ .... 0001 = Frame Number: 1
+ 00.. .... = FQC: Frame Good (0)
+ ..00 0000 = RFCI: 0x00
+ 1110 00.. = Header CRC: 0x38 [correct]
+ .... ..11 1111 1111 = Payload CRC: 0x3ff
+ Payload Data: 08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740
+*/
+static const uint8_t iuup_data[] = {
+ 0x01, 0x00, 0xe3, 0xff, /*payload starts here: */ 0x08, 0x55, 0x6d, 0x94, 0x4c, 0x71, 0xa1, 0xa0,
+ 0x81, 0xe7, 0xea, 0xd2, 0x04, 0x24, 0x44, 0x80, 0x00, 0x0e, 0xcd, 0x82,
+ 0xb8, 0x11, 0x18, 0x00, 0x00, 0x97, 0xc4, 0x79, 0x4e, 0x77, 0x40
+};
+
+#define IUUP_MSGB_SIZE 4096
+
+static struct osmo_iuup_tnl_prim *itp_ctrl_nack_alloc(enum iuup_procedure proc_ind, enum iuup_error_cause error_cause, uint8_t fn)
+{
+ struct osmo_iuup_tnl_prim *tnp;
+ struct iuup_ctrl_nack *nack;
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(struct iuup_ctrl_nack));
+ nack = (struct iuup_ctrl_nack *) msgb_l2(tnp->oph.msg);
+ *nack = (struct iuup_ctrl_nack){
+ .hdr = {
+ .frame_nr = fn,
+ .ack_nack = IUUP_AN_NACK,
+ .pdu_type = IUUP_PDU_T_CONTROL,
+ .proc_ind = proc_ind,
+ .mode_version = 0,
+ .payload_crc_hi = 0,
+ .header_crc = 0,
+ .payload_crc_lo = 0,
+ },
+ .spare = 0,
+ .error_cause = error_cause,
+ };
+ nack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(tnp->oph.msg), msgb_l2len(tnp->oph.msg));
+ return tnp;
+}
+
+static struct osmo_iuup_tnl_prim *itp_ctrl_ack_alloc(enum iuup_procedure proc_ind, uint8_t fn)
+{
+ struct osmo_iuup_tnl_prim *tnp;
+ struct iuup_ctrl_ack *ack;
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(struct iuup_ctrl_ack));
+ ack = (struct iuup_ctrl_ack *) msgb_l2(tnp->oph.msg);
+ *ack = (struct iuup_ctrl_ack){
+ .hdr = {
+ .frame_nr = fn,
+ .ack_nack = IUUP_AN_ACK,
+ .pdu_type = IUUP_PDU_T_CONTROL,
+ .proc_ind = proc_ind,
+ .mode_version = 0,
+ .payload_crc_hi = 0,
+ .header_crc = 0,
+ .payload_crc_lo = 0,
+ },
+ };
+ ack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(tnp->oph.msg), msgb_l2len(tnp->oph.msg));
+ return tnp;
+}
+
+static void clock_override_set(long sec, long usec)
+{
+ osmo_gettimeofday_override_time.tv_sec = sec + usec / (1000*1000);
+ osmo_gettimeofday_override_time.tv_usec = usec % (1000*1000);
+ printf("sys={%lu.%06lu}, %s\n", osmo_gettimeofday_override_time.tv_sec,
+ osmo_gettimeofday_override_time.tv_usec, __func__);
+}
+
+void test_crc(void)
+{
+ int rc;
+
+ /* Frame 34, "Initialization ACK", OS#4744 3g_call_23112021.pcapng */
+ static const uint8_t iuup_initialization_ack[] = {
+ 0xe4, 0x00, 0xdf, 0x99, 0x16, 0x00, 0x51, 0x67, 0x3c, 0x01, 0x27, 0x00,
+ 0x00, 0x82, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x01, 0x00
+ };
+
+ printf("=== start: %s ===\n", __func__);
+
+ rc = osmo_iuup_compute_header_crc(iuup_initialization, sizeof(iuup_initialization));
+ printf("iuup_initialization: Header CRC = 0x%02x\n", rc);
+ rc = osmo_iuup_compute_payload_crc(iuup_initialization, sizeof(iuup_initialization));
+ printf("iuup_initialization: Payload CRC = 0x%03x\n", rc);
+
+ rc = osmo_iuup_compute_header_crc(iuup_initialization_ack, sizeof(iuup_initialization_ack));
+ printf("iuup_initialization_ack: Header CRC = 0x%02x\n", rc);
+ rc = osmo_iuup_compute_payload_crc(iuup_initialization_ack, sizeof(iuup_initialization_ack));
+ printf("iuup_initialization_ack: Payload CRC = 0x%03x\n", rc);
+
+ printf("=== end: %s ===\n", __func__);
+}
+
+
+/****************************
+ * test_tinit_timeout_retrans
+ ****************************/
+static unsigned int _tinit_timeout_retrans_user_rx_prim = 0;
+static int _tinit_timeout_retrans_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
+ printf("%s()\n", __func__);
+
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
+
+ OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_ERR_EVENT);
+ OSMO_ASSERT(irp->u.status.u.error_event.cause == IUUP_ERR_CAUSE_INIT_FAILURE_NET_TMR);
+ OSMO_ASSERT(irp->u.status.u.error_event.distance == IUUP_ERR_DIST_LOCAL);
+ _tinit_timeout_retrans_user_rx_prim++;
+ msgb_free(oph->msg);
+ return 0;
+}
+static unsigned int _tinit_timeout_retrans_transport_rx_prim = 0;
+static int _tinit_timeout_retrans_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
+ struct msgb *msg = oph->msg;
+
+ printf("%s()\n", __func__);
+ OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
+ printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
+ _tinit_timeout_retrans_transport_rx_prim++;
+
+ msgb_free(msg);
+ return 0;
+}
+void test_tinit_timeout_retrans(void)
+{
+ struct osmo_iuup_instance *iui;
+ struct osmo_iuup_rnl_prim *rnp;
+ int rc, i;
+
+ iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
+ OSMO_ASSERT(iui);
+ osmo_iuup_instance_set_user_prim_cb(iui, _tinit_timeout_retrans_user_prim_cb, NULL);
+ osmo_iuup_instance_set_transport_prim_cb(iui, _tinit_timeout_retrans_transport_prim_cb, NULL);
+
+ clock_override_set(0, 0);
+
+ /* Tx CONFIG.req */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.config = def_configure_req;
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ /* STATUS-INIT.req is transmitted automatically: */
+ OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == 1);
+
+ /* After one sec, INITIALIZATION msg is retransmitted */
+ for (i = 1; i < IUUP_TIMER_INIT_N_DEFAULT + 1; i++) {
+ clock_override_set(0, IUUP_TIMER_INIT_T_DEFAULT*1000 * i);
+ osmo_select_main(0);
+ OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == i + 1);
+ }
+ /* Last one should send an error event: */
+ OSMO_ASSERT(_tinit_timeout_retrans_user_rx_prim == 0);
+ clock_override_set(0, IUUP_TIMER_INIT_T_DEFAULT*1000 * i);
+ osmo_select_main(0);
+ OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == i);
+ OSMO_ASSERT(_tinit_timeout_retrans_user_rx_prim == 1);
+
+ /* Nothing else is received afterwards. osmo_select_main() will block forever. */
+ /*clock_override_set(i + 1, 0);
+ osmo_select_main(0);
+ OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == i);
+ OSMO_ASSERT(_tinit_timeout_retrans_user_rx_prim == 1);*/
+
+ osmo_iuup_instance_free(iui);
+}
+
+/****************************
+ * test_tinit_nack
+ ****************************/
+static unsigned int _init_nack_retrans_user_rx_prim = 0;
+static int _init_nack_retrans_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
+
+ printf("%s()\n", __func__);
+
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
+
+ OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_ERR_EVENT);
+ OSMO_ASSERT(irp->u.status.u.error_event.cause == IUUP_ERR_CAUSE_INIT_FAILURE_REP_NACK);
+ OSMO_ASSERT(irp->u.status.u.error_event.distance == IUUP_ERR_DIST_SECOND_FWD);
+ _init_nack_retrans_user_rx_prim++;
+ msgb_free(oph->msg);
+ return 0;
+}
+static int _init_nack_retrans_transport_rx_prim = 0;
+static int _init_nack_retrans_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
+ struct msgb *msg = oph->msg;
+
+ printf("%s()\n", __func__);
+ OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
+ printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
+ _init_nack_retrans_transport_rx_prim++;
+
+ msgb_free(msg);
+ return 0;
+}
+void test_init_nack_retrans(void)
+{
+ struct osmo_iuup_instance *iui;
+ struct osmo_iuup_rnl_prim *rnp;
+ struct osmo_iuup_tnl_prim *tnp;
+ int rc, i;
+
+ iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
+ OSMO_ASSERT(iui);
+ osmo_iuup_instance_set_user_prim_cb(iui, _init_nack_retrans_user_prim_cb, NULL);
+ osmo_iuup_instance_set_transport_prim_cb(iui, _init_nack_retrans_transport_prim_cb, NULL);
+
+ clock_override_set(0, 0);
+
+ /* Tx CONFIG.req */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.config = def_configure_req;
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ /* STATUS-INIT.req is transmitted automatically: */
+ OSMO_ASSERT(_init_nack_retrans_transport_rx_prim == 1);
+
+ /* After one sec, INITIALIZATION msg is retransmitted */
+ for (i = 1; i < IUUP_TIMER_INIT_N_DEFAULT + 1; i++) {
+ /* Send NACK: */
+ tnp = itp_ctrl_nack_alloc(IUUP_PROC_INIT, IUUP_ERR_CAUSE_MODE_VERSION_NOT_SUPPORTED, 0);
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ /* A new INIT is retransmitted: */
+ OSMO_ASSERT(_init_nack_retrans_transport_rx_prim == i + 1);
+ }
+ /* Last one should send an error event: */
+ OSMO_ASSERT(_init_nack_retrans_user_rx_prim == 0);
+ tnp = itp_ctrl_nack_alloc(IUUP_PROC_INIT, IUUP_ERR_CAUSE_MODE_VERSION_NOT_SUPPORTED, 0);
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ OSMO_ASSERT(_init_nack_retrans_transport_rx_prim == i);
+ OSMO_ASSERT(_init_nack_retrans_user_rx_prim == 1);
+
+ /* Nothing else is received afterwards. osmo_select_main() will block forever. */
+
+ osmo_iuup_instance_free(iui);
+}
+
+
+/****************************
+ * test_init_ack
+ ****************************/
+static unsigned int _init_ack_user_rx_prim = 0;
+static int _init_ack_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
+ struct msgb *msg = oph->msg;
+
+ printf("%s()\n", __func__);
+
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION));
+ printf("User: UL len=%u: %s\n", msgb_l3len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l3(msg), msgb_l3len(msg)));
+
+ _init_ack_user_rx_prim++;
+ msgb_free(oph->msg);
+ return 0;
+}
+static int _init_ack_transport_rx_prim = 0;
+static int _init_ack_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
+ struct msgb *msg = oph->msg;
+
+ printf("%s()\n", __func__);
+ OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
+ printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
+ _init_ack_transport_rx_prim++;
+
+ msgb_free(msg);
+ return 0;
+}
+void test_init_ack(void)
+{
+ struct osmo_iuup_instance *iui;
+ struct osmo_iuup_rnl_prim *rnp;
+ struct osmo_iuup_tnl_prim *tnp;
+ struct iuup_pdutype0_hdr *hdr0;
+ int rc;
+
+ iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
+ OSMO_ASSERT(iui);
+ osmo_iuup_instance_set_user_prim_cb(iui, _init_ack_user_prim_cb, NULL);
+ osmo_iuup_instance_set_transport_prim_cb(iui, _init_ack_transport_prim_cb, NULL);
+
+ clock_override_set(0, 0);
+
+ /* Tx CONFIG.req */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.config = def_configure_req;
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ /* STATUS-INIT.req is transmitted automatically: */
+ OSMO_ASSERT(_init_ack_transport_rx_prim == 1);
+
+ /* Send ACK: */
+ tnp = itp_ctrl_ack_alloc(IUUP_PROC_INIT, 0);
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ OSMO_ASSERT(_init_ack_transport_rx_prim == 1); /* Make sure there's no retrans */
+ OSMO_ASSERT(_init_ack_user_rx_prim == 0); /* Make sure there's no error event */
+
+ /* Send IuUP incoming data to the implementation: */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_data));
+ hdr0 = (struct iuup_pdutype0_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr0, iuup_data, sizeof(iuup_data));
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ /* We receive it in RNL: */
+ OSMO_ASSERT(_init_ack_user_rx_prim == 1);
+
+ /* Now in opposite direction, RNL->[IuuP]->TNL: */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.data.rfci = 0;
+ rnp->u.data.frame_nr = 1;
+ rnp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
+ rnp->oph.msg->l3h = msgb_put(rnp->oph.msg, sizeof(iuup_data) - 4);
+ memcpy(rnp->oph.msg->l3h, iuup_data + 4, sizeof(iuup_data) - 4);
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ OSMO_ASSERT(_init_ack_transport_rx_prim == 2); /* We receive data in TNL */
+
+ osmo_iuup_instance_free(iui);
+}
+
+/****************************
+ * test_passive_init
+ ****************************/
+static unsigned int _passive_init_user_rx_prim = 0;
+static int _passive_init_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
+ struct msgb *msg = oph->msg;
+
+ printf("%s()\n", __func__);
+
+ switch (_passive_init_user_rx_prim) {
+ case 0:
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
+ OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_INIT);
+ break;
+ case 1:
+ default:
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION));
+ printf("User: UL len=%u: %s\n", msgb_l3len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l3(msg), msgb_l3len(msg)));
+ }
+
+ _passive_init_user_rx_prim++;
+ msgb_free(oph->msg);
+ return 0;
+}
+static int _passive_init_transport_rx_prim = 0;
+static int _passive_init_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
+ struct msgb *msg;
+
+ printf("%s()\n", __func__);
+ msg = oph->msg;
+ OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
+ printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
+ _passive_init_transport_rx_prim++;
+
+ msgb_free(msg);
+ return 0;
+}
+void test_passive_init(void)
+{
+ /* Here we check the passive INIT code path, aka receiving INIT and returning INIT_ACK/NACK */
+ struct osmo_iuup_instance *iui;
+ struct osmo_iuup_rnl_prim *rnp;
+ struct osmo_iuup_tnl_prim *tnp;
+ struct iuup_pdutype14_hdr *hdr14;
+ struct iuup_pdutype0_hdr *hdr0;
+ int rc;
+
+ iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
+ OSMO_ASSERT(iui);
+ osmo_iuup_instance_set_user_prim_cb(iui, _passive_init_user_prim_cb, NULL);
+ osmo_iuup_instance_set_transport_prim_cb(iui, _passive_init_transport_prim_cb, NULL);
+
+ clock_override_set(0, 0);
+
+ /* Tx CONFIG.req */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.config = def_configure_req;
+ rnp->u.config.active = false;
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ /* STATUS-INIT.req is NOT transmitted automatically: */
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 0);
+
+ /* Send Init: */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_initialization));
+ hdr14 = (struct iuup_pdutype14_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr14, iuup_initialization, sizeof(iuup_initialization));
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 1); /* We receive an Init ACK */
+ OSMO_ASSERT(_passive_init_user_rx_prim == 1); /* We receive the Status-Init.ind */
+
+ /* Send IuUP incoming data to the implementation: */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_data));
+ hdr0 = (struct iuup_pdutype0_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr0, iuup_data, sizeof(iuup_data));
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ /* We receive it in RNL: */
+ OSMO_ASSERT(_passive_init_user_rx_prim == 2);
+
+ /* Now in opposite direction, RNL->[IuuP]->TNL: */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.data.rfci = 0;
+ rnp->u.data.frame_nr = 1;
+ rnp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
+ rnp->oph.msg->l3h = msgb_put(rnp->oph.msg, sizeof(iuup_data) - 4);
+ memcpy(rnp->oph.msg->l3h, iuup_data + 4, sizeof(iuup_data) - 4);
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 2); /* We receive data in TNL */
+
+ osmo_iuup_instance_free(iui);
+}
+
+/****************************
+ * test_passive_init_retrans
+ ****************************/
+static unsigned int _passive_init_retrans_user_rx_prim = 0;
+static int _passive_init_retrans_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
+ struct msgb *msg = oph->msg;
+
+ printf("%s()\n", __func__);
+
+ switch (_passive_init_retrans_user_rx_prim) {
+ case 0:
+ case 1:
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
+ OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_INIT);
+ break;
+ case 2:
+ default:
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION));
+ printf("User: UL len=%u: %s\n", msgb_l3len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l3(msg), msgb_l3len(msg)));
+ }
+
+ _passive_init_retrans_user_rx_prim++;
+ msgb_free(oph->msg);
+ return 0;
+}
+void test_passive_init_retrans(void)
+{
+ /* Here we check the passive INIT code path, aka receiving INIT and
+ * returning INIT_ACK/NACK. We emulate the peer not receiving the INIT
+ * ACK and hence retransmitting the INIT. The IuUP stack should then
+ * push the new INIT info up the stack and ACK it. */
+ struct osmo_iuup_instance *iui;
+ struct osmo_iuup_rnl_prim *rnp;
+ struct osmo_iuup_tnl_prim *tnp;
+ struct iuup_pdutype14_hdr *hdr14;
+ struct iuup_pdutype0_hdr *hdr0;
+ int rc;
+
+ /* reset global var, we reuse it together wth callback from test_passive_init(): */
+ _passive_init_transport_rx_prim = 0;
+
+ iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
+ OSMO_ASSERT(iui);
+ osmo_iuup_instance_set_user_prim_cb(iui, _passive_init_retrans_user_prim_cb, NULL);
+ osmo_iuup_instance_set_transport_prim_cb(iui, _passive_init_transport_prim_cb, NULL);
+
+ clock_override_set(0, 0);
+
+ /* Tx CONFIG.req */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.config = def_configure_req;
+ rnp->u.config.active = false;
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ /* STATUS-INIT.req is NOT transmitted automatically: */
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 0);
+
+ /* Send Init: */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_initialization));
+ hdr14 = (struct iuup_pdutype14_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr14, iuup_initialization, sizeof(iuup_initialization));
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 1); /* We receive an Init ACK */
+ OSMO_ASSERT(_passive_init_retrans_user_rx_prim == 1); /* We receive the Status-Init.ind */
+
+ /* Send Init (retrans): */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_initialization));
+ hdr14 = (struct iuup_pdutype14_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr14, iuup_initialization, sizeof(iuup_initialization));
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 2); /* We receive another Init ACK */
+ OSMO_ASSERT(_passive_init_retrans_user_rx_prim == 2); /* We receive another Status-Init.ind */
+
+ /* Send IuUP incoming data to the implementation: */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_data));
+ hdr0 = (struct iuup_pdutype0_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr0, iuup_data, sizeof(iuup_data));
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ /* We receive it in RNL: */
+ OSMO_ASSERT(_passive_init_retrans_user_rx_prim == 3);
+
+ /* Now in opposite direction, RNL->[IuuP]->TNL: */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.data.rfci = 0;
+ rnp->u.data.frame_nr = 1;
+ rnp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
+ rnp->oph.msg->l3h = msgb_put(rnp->oph.msg, sizeof(iuup_data) - 4);
+ memcpy(rnp->oph.msg->l3h, iuup_data + 4, sizeof(iuup_data) - 4);
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 3); /* We receive data in TNL */
+
+ osmo_iuup_instance_free(iui);
+}
+
+static int _decode_passive_init_2_rfci_no_iptis_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
+ printf("%s(): Initialization decoded fine!\n", __func__);
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
+ OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_INIT);
+ OSMO_ASSERT(irp->u.status.u.initialization.num_rfci == 2);
+ OSMO_ASSERT(irp->u.status.u.initialization.num_subflows == 3);
+ OSMO_ASSERT(irp->u.status.u.initialization.data_pdu_type == 0);
+ OSMO_ASSERT(irp->u.status.u.initialization.IPTIs_present == false);
+ msgb_free(oph->msg);
+ return 0;
+}
+static int _decode_passive_init_2_rfci_no_iptis_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
+ struct msgb *msg;
+ struct iuup_pdutype14_hdr *hdr;
+
+ printf("%s()\n", __func__);
+ msg = oph->msg;
+ OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
+ printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
+ hdr = msgb_l2(msg);
+ OSMO_ASSERT(hdr->pdu_type == IUUP_PDU_T_CONTROL);
+ OSMO_ASSERT(hdr->ack_nack == IUUP_AN_ACK);
+ msgb_free(msg);
+ return 0;
+}
+void test_decode_passive_init_2_rfci_no_iptis(void)
+{
+ /* Here we check the passive INIT code path, aka receiving INIT and returning INIT_ACK/NACK */
+ struct osmo_iuup_instance *iui;
+ struct osmo_iuup_rnl_prim *rnp;
+ struct osmo_iuup_tnl_prim *tnp;
+ struct iuup_pdutype14_hdr *hdr14;
+ int rc;
+
+ /* Frame 46, "Initialization", SYS#5969 call4_Iu_Iuh.pcap
+ 1110 .... = PDU Type: Control Procedure (14)
+ .... 00.. = Ack/Nack: Procedure (0)
+ .... ..00 = Frame Number: 0
+ 0000 .... = Mode Version: 0x0
+ .... 0000 = Procedure: Initialization (0)
+ 1101 11.. = Header CRC: 0x37 [correct]
+ .... ..01 1011 0100 = Payload CRC: 0x1b4
+ 000. .... = Spare: 0x0
+ ...0 .... = TI: IPTIs not present (0)
+ .... 011. = Subflows: 3
+ .... ...0 = Chain Indicator: this frame is the last frame for the procedure (0)
+ RFCI 1 Initialization
+ 0... .... = RFCI 0 LRI: Not last RFCI (0x0)
+ .0.. .... = RFCI 0 LI: one octet used (0x0)
+ ..00 0001 = RFCI 0: 1
+ RFCI 0 Flow 0 Len: 81
+ RFCI 0 Flow 1 Len: 103
+ RFCI 0 Flow 2 Len: 60
+ RFCI 6 Initialization
+ 1... .... = RFCI 1 LRI: Last RFCI in current frame (0x1)
+ .0.. .... = RFCI 1 LI: one octet used (0x0)
+ ..00 0110 = RFCI 1: 6
+ RFCI 1 Flow 0 Len: 39
+ RFCI 1 Flow 1 Len: 0
+ RFCI 1 Flow 2 Len: 0
+ Iu UP Mode Versions Supported: 0x0001
+ 0... .... .... .... = Version 16: not supported (0x0)
+ .0.. .... .... .... = Version 15: not supported (0x0)
+ ..0. .... .... .... = Version 14: not supported (0x0)
+ ...0 .... .... .... = Version 13: not supported (0x0)
+ .... 0... .... .... = Version 12: not supported (0x0)
+ .... .0.. .... .... = Version 11: not supported (0x0)
+ .... ..0. .... .... = Version 10: not supported (0x0)
+ .... ...0 .... .... = Version 9: not supported (0x0)
+ .... .... 0... .... = Version 8: not supported (0x0)
+ .... .... .0.. .... = Version 7: not supported (0x0)
+ .... .... ..0. .... = Version 6: not supported (0x0)
+ .... .... ...0 .... = Version 5: not supported (0x0)
+ .... .... .... 0... = Version 4: not supported (0x0)
+ .... .... .... .0.. = Version 3: not supported (0x0)
+ .... .... .... ..0. = Version 2: not supported (0x0)
+ .... .... .... ...1 = Version 1: supported (0x1)
+ 0000 .... = RFCI Data Pdu Type: PDU type 0 (0x0)
+ */
+ const uint8_t iuup_init[] = {
+ 0xe0, 0x00, 0xdd, 0xb4, 0x06, 0x01, 0x51, 0x67, 0x3c, 0x86, 0x27,
+ 0x00, 0x00, 0x00, 0x01, 0x00
+ };
+
+ iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
+ OSMO_ASSERT(iui);
+ osmo_iuup_instance_set_user_prim_cb(iui, _decode_passive_init_2_rfci_no_iptis_user_prim_cb, NULL);
+ osmo_iuup_instance_set_transport_prim_cb(iui, _decode_passive_init_2_rfci_no_iptis_transport_prim_cb, NULL);
+
+ clock_override_set(0, 0);
+
+ /* Tx CONFIG.req */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.config = def_configure_req;
+ rnp->u.config.active = false;
+
+ rc = osmo_iuup_rnl_prim_down(iui, rnp);
+ OSMO_ASSERT(rc == 0);
+
+ /* Send Init: */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_init));
+ hdr14 = (struct iuup_pdutype14_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr14, iuup_init, sizeof(iuup_init));
+
+ rc = osmo_iuup_tnl_prim_up(iui, tnp);
+ OSMO_ASSERT(rc == 0);
+
+ osmo_iuup_instance_free(iui);
+}
+
+int main(int argc, char **argv)
+{
+ iuup_test_ctx = talloc_named_const(NULL, 0, "iuup_test");
+ osmo_init_logging2(iuup_test_ctx, NULL);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_category(osmo_stderr_target, 1);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_category_filter(osmo_stderr_target, DLIUUP, 1, LOGL_DEBUG);
+ osmo_fsm_log_addr(false);
+
+ osmo_gettimeofday_override = true;
+
+ test_crc();
+ test_tinit_timeout_retrans();
+ test_init_nack_retrans();
+ test_init_ack();
+ test_passive_init();
+ test_passive_init_retrans();
+ test_decode_passive_init_2_rfci_no_iptis();
+
+ printf("OK.\n");
+}
diff --git a/tests/iuup/iuup_test.err b/tests/iuup/iuup_test.err
new file mode 100644
index 00000000..fe1ed397
--- /dev/null
+++ b/tests/iuup/iuup_test.err
@@ -0,0 +1,55 @@
+DLIUUP IuUP(test_tinit_timeout_retrans){NULL}: Allocated
+DLIUUP IuUP(test_tinit_timeout_retrans){NULL}: Received Event IuUP-CONFIG-req
+DLIUUP IuUP(test_tinit_timeout_retrans){NULL}: state_chg to Initialisation
+DLIUUP IuUP(test_tinit_timeout_retrans){Initialisation}: Timeout of T1
+DLIUUP IuUP(test_tinit_timeout_retrans){Initialisation}: Timeout of T1
+DLIUUP IuUP(test_tinit_timeout_retrans){Initialisation}: Timeout of T1
+DLIUUP IuUP(test_tinit_timeout_retrans){Initialisation}: Timeout of T1
+DLIUUP IuUP(test_tinit_timeout_retrans){Initialisation}: Deallocated
+DLIUUP IuUP(test_init_nack_retrans){NULL}: Allocated
+DLIUUP IuUP(test_init_nack_retrans){NULL}: Received Event IuUP-CONFIG-req
+DLIUUP IuUP(test_init_nack_retrans){NULL}: state_chg to Initialisation
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Received Event INIT_NACK
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Rx Initialization NACK N=0/3
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Received Event INIT_NACK
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Rx Initialization NACK N=1/3
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Received Event INIT_NACK
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Rx Initialization NACK N=2/3
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Received Event INIT_NACK
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Rx Initialization NACK N=3/3
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Deallocated
+DLIUUP IuUP(test_init_ack){NULL}: Allocated
+DLIUUP IuUP(test_init_ack){NULL}: Received Event IuUP-CONFIG-req
+DLIUUP IuUP(test_init_ack){NULL}: state_chg to Initialisation
+DLIUUP IuUP(test_init_ack){Initialisation}: Received Event LAST_INIT_ACK
+DLIUUP IuUP(test_init_ack){Initialisation}: state_chg to SMpSDU_Data_Transfer_Ready
+DLIUUP IuUP(test_init_ack){SMpSDU_Data_Transfer_Ready}: Received Event IuUP-DATA-ind
+DLIUUP IuUP(test_init_ack){SMpSDU_Data_Transfer_Ready}: Received Event IuUP-DATA-req
+DLIUUP IuUP(test_init_ack){SMpSDU_Data_Transfer_Ready}: Deallocated
+DLIUUP IuUP(test_passive_init){NULL}: Allocated
+DLIUUP IuUP(test_passive_init){NULL}: Received Event IuUP-CONFIG-req
+DLIUUP IuUP(test_passive_init){NULL}: state_chg to Initialisation
+DLIUUP IuUP(test_passive_init){Initialisation}: Received Event INIT
+DLIUUP IuUP(test_passive_init){Initialisation}: Tx Initialization ACK
+DLIUUP IuUP(test_passive_init){Initialisation}: state_chg to SMpSDU_Data_Transfer_Ready
+DLIUUP IuUP(test_passive_init){SMpSDU_Data_Transfer_Ready}: Received Event IuUP-DATA-ind
+DLIUUP IuUP(test_passive_init){SMpSDU_Data_Transfer_Ready}: Received Event IuUP-DATA-req
+DLIUUP IuUP(test_passive_init){SMpSDU_Data_Transfer_Ready}: Deallocated
+DLIUUP IuUP(test_passive_init_retrans){NULL}: Allocated
+DLIUUP IuUP(test_passive_init_retrans){NULL}: Received Event IuUP-CONFIG-req
+DLIUUP IuUP(test_passive_init_retrans){NULL}: state_chg to Initialisation
+DLIUUP IuUP(test_passive_init_retrans){Initialisation}: Received Event INIT
+DLIUUP IuUP(test_passive_init_retrans){Initialisation}: Tx Initialization ACK
+DLIUUP IuUP(test_passive_init_retrans){Initialisation}: state_chg to SMpSDU_Data_Transfer_Ready
+DLIUUP IuUP(test_passive_init_retrans){SMpSDU_Data_Transfer_Ready}: Received Event INIT
+DLIUUP IuUP(test_passive_init_retrans){SMpSDU_Data_Transfer_Ready}: Tx Initialization ACK
+DLIUUP IuUP(test_passive_init_retrans){SMpSDU_Data_Transfer_Ready}: Received Event IuUP-DATA-ind
+DLIUUP IuUP(test_passive_init_retrans){SMpSDU_Data_Transfer_Ready}: Received Event IuUP-DATA-req
+DLIUUP IuUP(test_passive_init_retrans){SMpSDU_Data_Transfer_Ready}: Deallocated
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){NULL}: Allocated
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){NULL}: Received Event IuUP-CONFIG-req
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){NULL}: state_chg to Initialisation
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){Initialisation}: Received Event INIT
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){Initialisation}: Tx Initialization ACK
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){Initialisation}: state_chg to SMpSDU_Data_Transfer_Ready
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){SMpSDU_Data_Transfer_Ready}: Deallocated
diff --git a/tests/iuup/iuup_test.ok b/tests/iuup/iuup_test.ok
new file mode 100644
index 00000000..57baba9c
--- /dev/null
+++ b/tests/iuup/iuup_test.ok
@@ -0,0 +1,61 @@
+=== start: test_crc ===
+iuup_initialization: Header CRC = 0x37
+iuup_initialization: Payload CRC = 0x399
+iuup_initialization_ack: Header CRC = 0x09
+iuup_initialization_ack: Payload CRC = 0x399
+=== end: test_crc ===
+sys={0.000000}, clock_override_set
+_tinit_timeout_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+sys={1.000000}, clock_override_set
+_tinit_timeout_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+sys={2.000000}, clock_override_set
+_tinit_timeout_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+sys={3.000000}, clock_override_set
+_tinit_timeout_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+sys={4.000000}, clock_override_set
+_tinit_timeout_retrans_user_prim_cb()
+sys={0.000000}, clock_override_set
+_init_nack_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+_init_nack_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+_init_nack_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+_init_nack_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+_init_nack_retrans_user_prim_cb()
+sys={0.000000}, clock_override_set
+_init_ack_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+_init_ack_user_prim_cb()
+User: UL len=31: 08 55 6d 94 4c 71 a1 a0 81 e7 ea d2 04 24 44 80 00 0e cd 82 b8 11 18 00 00 97 c4 79 4e 77 40
+_init_ack_transport_prim_cb()
+Transport: DL len=35: 01 00 e3 ff 08 55 6d 94 4c 71 a1 a0 81 e7 ea d2 04 24 44 80 00 0e cd 82 b8 11 18 00 00 97 c4 79 4e 77 40
+sys={0.000000}, clock_override_set
+_passive_init_user_prim_cb()
+_passive_init_transport_prim_cb()
+Transport: DL len=4: e4 00 24 00
+_passive_init_user_prim_cb()
+User: UL len=31: 08 55 6d 94 4c 71 a1 a0 81 e7 ea d2 04 24 44 80 00 0e cd 82 b8 11 18 00 00 97 c4 79 4e 77 40
+_passive_init_transport_prim_cb()
+Transport: DL len=35: 01 00 e3 ff 08 55 6d 94 4c 71 a1 a0 81 e7 ea d2 04 24 44 80 00 0e cd 82 b8 11 18 00 00 97 c4 79 4e 77 40
+sys={0.000000}, clock_override_set
+_passive_init_retrans_user_prim_cb()
+_passive_init_transport_prim_cb()
+Transport: DL len=4: e4 00 24 00
+_passive_init_retrans_user_prim_cb()
+_passive_init_transport_prim_cb()
+Transport: DL len=4: e4 00 24 00
+_passive_init_retrans_user_prim_cb()
+User: UL len=31: 08 55 6d 94 4c 71 a1 a0 81 e7 ea d2 04 24 44 80 00 0e cd 82 b8 11 18 00 00 97 c4 79 4e 77 40
+_passive_init_transport_prim_cb()
+Transport: DL len=35: 01 00 e3 ff 08 55 6d 94 4c 71 a1 a0 81 e7 ea d2 04 24 44 80 00 0e cd 82 b8 11 18 00 00 97 c4 79 4e 77 40
+sys={0.000000}, clock_override_set
+_decode_passive_init_2_rfci_no_iptis_user_prim_cb(): Initialization decoded fine!
+_decode_passive_init_2_rfci_no_iptis_transport_prim_cb()
+Transport: DL len=4: e4 00 24 00
+OK.
diff --git a/tests/lapd/lapd_test.c b/tests/lapd/lapd_test.c
index 48851f47..f2fccf87 100644
--- a/tests/lapd/lapd_test.c
+++ b/tests/lapd/lapd_test.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/application.h>
@@ -188,6 +184,7 @@ static int send(struct msgb *in_msg, struct lapdm_channel *chan)
/* LAPDm requires those... */
pp.u.data.chan_nr = 0;
pp.u.data.link_id = 0;
+ pp.u.data.fn = 0;
/* feed into the LAPDm code of libosmogsm */
rc = lapdm_phsap_up(&pp.oph, &chan->lapdm_dcch);
OSMO_ASSERT(rc == 0 || rc == -EBUSY);
@@ -210,6 +207,7 @@ static int send_buf(const uint8_t *buf, size_t len, struct lapdm_channel *chan)
/* LAPDm requires those... */
pp.u.data.chan_nr = 0;
pp.u.data.link_id = 0;
+ pp.u.data.fn = 0;
/* feed into the LAPDm code of libosmogsm */
rc = lapdm_phsap_up(&pp.oph, &chan->lapdm_dcch);
OSMO_ASSERT(rc == 0 || rc == -EBUSY);
@@ -380,7 +378,7 @@ static int ms_to_bts_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *_ctx
return 0;
}
-static void test_lapdm_polling()
+static void test_lapdm_polling(void)
{
printf("I do some very simple LAPDm test.\n");
@@ -474,7 +472,7 @@ static void test_lapdm_polling()
lapdm_channel_exit(&ms_to_bts_channel);
}
-static void test_lapdm_contention_resolution()
+static void test_lapdm_contention_resolution(void)
{
printf("I test contention resultion by having two mobiles collide and "
"first mobile repeating SABM.\n");
@@ -508,7 +506,7 @@ static void test_lapdm_contention_resolution()
cm2 = malloc(sizeof(cm));
memcpy(cm2, cm, sizeof(cm));
cm2[0] += 1;
- send_sabm(&bts_to_ms_channel, 0, cm2, sizeof(cm2));
+ send_sabm(&bts_to_ms_channel, 0, cm2, sizeof(cm));
free(cm2);
rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
OSMO_ASSERT(rc == -ENODEV);
@@ -527,7 +525,7 @@ static void test_lapdm_contention_resolution()
lapdm_channel_exit(&bts_to_ms_channel);
}
-static void test_lapdm_early_release()
+static void test_lapdm_early_release(void)
{
printf("I test RF channel release of an unestablished channel.\n");
@@ -607,7 +605,7 @@ static void lapdm_establish(const uint8_t *est_req, size_t est_req_size)
msgb_free(msg);
}
-static void test_lapdm_establishment()
+static void test_lapdm_establishment(void)
{
printf("I test RF channel establishment.\n");
printf("Testing SAPI3/SDCCH\n");
@@ -681,7 +679,7 @@ static void dump_queue(struct llist_head *head)
printf("\n");
}
-static void test_lapdm_desync()
+static void test_lapdm_desync(void)
{
printf("I test if desync problems exist in LAPDm\n");
diff --git a/tests/logging/logging_test.c b/tests/logging/logging_test.c
index f8739587..3548b1dd 100644
--- a/tests/logging/logging_test.c
+++ b/tests/logging/logging_test.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/logging.h>
@@ -82,6 +78,11 @@ int main(int argc, char **argv)
log_set_print_category(stderr_target, 1);
log_set_use_color(stderr_target, 0);
+ if (argc > 1 && !strcmp(argv[1], "wqueue"))
+ log_target_file_switch_to_wqueue(stderr_target);
+ else
+ log_target_file_switch_to_stream(stderr_target);
+
log_parse_category_mask(stderr_target, "DRLL:DCC");
log_parse_category_mask(stderr_target, "DRLL");
@@ -129,5 +130,13 @@ int main(int argc, char **argv)
log_set_category_filter(stderr_target, DLGLOBAL, 1, LOGL_DEBUG);
DEBUGP(DLGLOBAL, "You should see this (DLGLOBAL on DEBUG)\n");
+ /* Test printing of the filename */
+ log_set_print_filename2(stderr_target, LOG_FILENAME_BASENAME);
+
+ log_set_print_filename_pos(stderr_target, LOG_FILENAME_POS_HEADER_END);
+ DEBUGP(DLGLOBAL, "A message with source info printed first\n");
+ log_set_print_filename_pos(stderr_target, LOG_FILENAME_POS_LINE_END);
+ DEBUGP(DLGLOBAL, "A message with source info printed last\n");
+
return 0;
}
diff --git a/tests/logging/logging_test.err b/tests/logging/logging_test.err
index 17b3cad0..01ab8782 100644
--- a/tests/logging/logging_test.err
+++ b/tests/logging/logging_test.err
@@ -7,3 +7,5 @@ DLGLOBAL You should see this on DLGLOBAL (c)
DLGLOBAL You should see this on DLGLOBAL (d)
DLGLOBAL You should see this on DLGLOBAL (e)
DLGLOBAL You should see this (DLGLOBAL on DEBUG)
+DLGLOBAL logging_test.c:137 A message with source info printed first
+DLGLOBAL A message with source info printed last (logging_test.c:139)
diff --git a/tests/logging/logging_test_gsmtap.c b/tests/logging/logging_test_gsmtap.c
new file mode 100644
index 00000000..5ede5a47
--- /dev/null
+++ b/tests/logging/logging_test_gsmtap.c
@@ -0,0 +1,64 @@
+/* simple test for gsmtap logging */
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+
+#include <stdlib.h>
+
+static const struct log_info_cat default_categories[] = {};
+
+const struct log_info log_info = {
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+};
+
+extern struct log_info *osmo_log_info;
+
+int main(int argc, char **argv)
+{
+ struct log_target *stderr_target;
+ struct log_target *gsmtap_target;
+
+ log_init(&log_info, NULL);
+ stderr_target = log_target_create_stderr();
+ log_add_target(stderr_target);
+ log_set_all_filter(stderr_target, 1);
+ log_set_print_filename2(stderr_target, LOG_FILENAME_NONE);
+ log_set_print_category_hex(stderr_target, 0);
+ log_set_print_category(stderr_target, 1);
+ log_set_use_color(stderr_target, 0);
+ log_parse_category_mask(stderr_target, "DLGLOBAL,1");
+
+ gsmtap_target = log_target_create_gsmtap("127.0.0.2", 4729, "gsmtap", 1, 1);
+ log_add_target(gsmtap_target);
+ log_set_all_filter(gsmtap_target, 1);
+ log_parse_category_mask(gsmtap_target, "DLGLOBAL,1");
+
+ log_target_file_switch_to_stream(stderr_target);
+
+ log_set_category_filter(stderr_target, DLIO, 1, LOGL_DEBUG);
+
+ for (int i = 0; i < 200; i++)
+ DEBUGP(DLGLOBAL, "Repeating message (i = %d)\n", i);
+
+ for (int i = 0; i < 200; i++)
+ osmo_select_main(1);
+
+ return 0;
+}
diff --git a/tests/logging/logging_test_gsmtap.err b/tests/logging/logging_test_gsmtap.err
new file mode 100644
index 00000000..92016abb
--- /dev/null
+++ b/tests/logging/logging_test_gsmtap.err
@@ -0,0 +1,200 @@
+DLGLOBAL Repeating message (i = 0)
+DLGLOBAL Repeating message (i = 1)
+DLGLOBAL Repeating message (i = 2)
+DLGLOBAL Repeating message (i = 3)
+DLGLOBAL Repeating message (i = 4)
+DLGLOBAL Repeating message (i = 5)
+DLGLOBAL Repeating message (i = 6)
+DLGLOBAL Repeating message (i = 7)
+DLGLOBAL Repeating message (i = 8)
+DLGLOBAL Repeating message (i = 9)
+DLGLOBAL Repeating message (i = 10)
+DLGLOBAL Repeating message (i = 11)
+DLGLOBAL Repeating message (i = 12)
+DLGLOBAL Repeating message (i = 13)
+DLGLOBAL Repeating message (i = 14)
+DLGLOBAL Repeating message (i = 15)
+DLGLOBAL Repeating message (i = 16)
+DLGLOBAL Repeating message (i = 17)
+DLGLOBAL Repeating message (i = 18)
+DLGLOBAL Repeating message (i = 19)
+DLGLOBAL Repeating message (i = 20)
+DLGLOBAL Repeating message (i = 21)
+DLGLOBAL Repeating message (i = 22)
+DLGLOBAL Repeating message (i = 23)
+DLGLOBAL Repeating message (i = 24)
+DLGLOBAL Repeating message (i = 25)
+DLGLOBAL Repeating message (i = 26)
+DLGLOBAL Repeating message (i = 27)
+DLGLOBAL Repeating message (i = 28)
+DLGLOBAL Repeating message (i = 29)
+DLGLOBAL Repeating message (i = 30)
+DLGLOBAL Repeating message (i = 31)
+DLGLOBAL Repeating message (i = 32)
+DLGLOBAL Repeating message (i = 33)
+DLGLOBAL Repeating message (i = 34)
+DLGLOBAL Repeating message (i = 35)
+DLGLOBAL Repeating message (i = 36)
+DLGLOBAL Repeating message (i = 37)
+DLGLOBAL Repeating message (i = 38)
+DLGLOBAL Repeating message (i = 39)
+DLGLOBAL Repeating message (i = 40)
+DLGLOBAL Repeating message (i = 41)
+DLGLOBAL Repeating message (i = 42)
+DLGLOBAL Repeating message (i = 43)
+DLGLOBAL Repeating message (i = 44)
+DLGLOBAL Repeating message (i = 45)
+DLGLOBAL Repeating message (i = 46)
+DLGLOBAL Repeating message (i = 47)
+DLGLOBAL Repeating message (i = 48)
+DLGLOBAL Repeating message (i = 49)
+DLGLOBAL Repeating message (i = 50)
+DLGLOBAL Repeating message (i = 51)
+DLGLOBAL Repeating message (i = 52)
+DLGLOBAL Repeating message (i = 53)
+DLGLOBAL Repeating message (i = 54)
+DLGLOBAL Repeating message (i = 55)
+DLGLOBAL Repeating message (i = 56)
+DLGLOBAL Repeating message (i = 57)
+DLGLOBAL Repeating message (i = 58)
+DLGLOBAL Repeating message (i = 59)
+DLGLOBAL Repeating message (i = 60)
+DLGLOBAL Repeating message (i = 61)
+DLGLOBAL Repeating message (i = 62)
+DLGLOBAL Repeating message (i = 63)
+DLGLOBAL Repeating message (i = 64)
+DLGLOBAL Repeating message (i = 65)
+DLGLOBAL Repeating message (i = 66)
+DLGLOBAL Repeating message (i = 67)
+DLGLOBAL Repeating message (i = 68)
+DLGLOBAL Repeating message (i = 69)
+DLGLOBAL Repeating message (i = 70)
+DLGLOBAL Repeating message (i = 71)
+DLGLOBAL Repeating message (i = 72)
+DLGLOBAL Repeating message (i = 73)
+DLGLOBAL Repeating message (i = 74)
+DLGLOBAL Repeating message (i = 75)
+DLGLOBAL Repeating message (i = 76)
+DLGLOBAL Repeating message (i = 77)
+DLGLOBAL Repeating message (i = 78)
+DLGLOBAL Repeating message (i = 79)
+DLGLOBAL Repeating message (i = 80)
+DLGLOBAL Repeating message (i = 81)
+DLGLOBAL Repeating message (i = 82)
+DLGLOBAL Repeating message (i = 83)
+DLGLOBAL Repeating message (i = 84)
+DLGLOBAL Repeating message (i = 85)
+DLGLOBAL Repeating message (i = 86)
+DLGLOBAL Repeating message (i = 87)
+DLGLOBAL Repeating message (i = 88)
+DLGLOBAL Repeating message (i = 89)
+DLGLOBAL Repeating message (i = 90)
+DLGLOBAL Repeating message (i = 91)
+DLGLOBAL Repeating message (i = 92)
+DLGLOBAL Repeating message (i = 93)
+DLGLOBAL Repeating message (i = 94)
+DLGLOBAL Repeating message (i = 95)
+DLGLOBAL Repeating message (i = 96)
+DLGLOBAL Repeating message (i = 97)
+DLGLOBAL Repeating message (i = 98)
+DLGLOBAL Repeating message (i = 99)
+DLGLOBAL Repeating message (i = 100)
+DLGLOBAL Repeating message (i = 101)
+DLGLOBAL Repeating message (i = 102)
+DLGLOBAL Repeating message (i = 103)
+DLGLOBAL Repeating message (i = 104)
+DLGLOBAL Repeating message (i = 105)
+DLGLOBAL Repeating message (i = 106)
+DLGLOBAL Repeating message (i = 107)
+DLGLOBAL Repeating message (i = 108)
+DLGLOBAL Repeating message (i = 109)
+DLGLOBAL Repeating message (i = 110)
+DLGLOBAL Repeating message (i = 111)
+DLGLOBAL Repeating message (i = 112)
+DLGLOBAL Repeating message (i = 113)
+DLGLOBAL Repeating message (i = 114)
+DLGLOBAL Repeating message (i = 115)
+DLGLOBAL Repeating message (i = 116)
+DLGLOBAL Repeating message (i = 117)
+DLGLOBAL Repeating message (i = 118)
+DLGLOBAL Repeating message (i = 119)
+DLGLOBAL Repeating message (i = 120)
+DLGLOBAL Repeating message (i = 121)
+DLGLOBAL Repeating message (i = 122)
+DLGLOBAL Repeating message (i = 123)
+DLGLOBAL Repeating message (i = 124)
+DLGLOBAL Repeating message (i = 125)
+DLGLOBAL Repeating message (i = 126)
+DLGLOBAL Repeating message (i = 127)
+DLGLOBAL Repeating message (i = 128)
+DLGLOBAL Repeating message (i = 129)
+DLGLOBAL Repeating message (i = 130)
+DLGLOBAL Repeating message (i = 131)
+DLGLOBAL Repeating message (i = 132)
+DLGLOBAL Repeating message (i = 133)
+DLGLOBAL Repeating message (i = 134)
+DLGLOBAL Repeating message (i = 135)
+DLGLOBAL Repeating message (i = 136)
+DLGLOBAL Repeating message (i = 137)
+DLGLOBAL Repeating message (i = 138)
+DLGLOBAL Repeating message (i = 139)
+DLGLOBAL Repeating message (i = 140)
+DLGLOBAL Repeating message (i = 141)
+DLGLOBAL Repeating message (i = 142)
+DLGLOBAL Repeating message (i = 143)
+DLGLOBAL Repeating message (i = 144)
+DLGLOBAL Repeating message (i = 145)
+DLGLOBAL Repeating message (i = 146)
+DLGLOBAL Repeating message (i = 147)
+DLGLOBAL Repeating message (i = 148)
+DLGLOBAL Repeating message (i = 149)
+DLGLOBAL Repeating message (i = 150)
+DLGLOBAL Repeating message (i = 151)
+DLGLOBAL Repeating message (i = 152)
+DLGLOBAL Repeating message (i = 153)
+DLGLOBAL Repeating message (i = 154)
+DLGLOBAL Repeating message (i = 155)
+DLGLOBAL Repeating message (i = 156)
+DLGLOBAL Repeating message (i = 157)
+DLGLOBAL Repeating message (i = 158)
+DLGLOBAL Repeating message (i = 159)
+DLGLOBAL Repeating message (i = 160)
+DLGLOBAL Repeating message (i = 161)
+DLGLOBAL Repeating message (i = 162)
+DLGLOBAL Repeating message (i = 163)
+DLGLOBAL Repeating message (i = 164)
+DLGLOBAL Repeating message (i = 165)
+DLGLOBAL Repeating message (i = 166)
+DLGLOBAL Repeating message (i = 167)
+DLGLOBAL Repeating message (i = 168)
+DLGLOBAL Repeating message (i = 169)
+DLGLOBAL Repeating message (i = 170)
+DLGLOBAL Repeating message (i = 171)
+DLGLOBAL Repeating message (i = 172)
+DLGLOBAL Repeating message (i = 173)
+DLGLOBAL Repeating message (i = 174)
+DLGLOBAL Repeating message (i = 175)
+DLGLOBAL Repeating message (i = 176)
+DLGLOBAL Repeating message (i = 177)
+DLGLOBAL Repeating message (i = 178)
+DLGLOBAL Repeating message (i = 179)
+DLGLOBAL Repeating message (i = 180)
+DLGLOBAL Repeating message (i = 181)
+DLGLOBAL Repeating message (i = 182)
+DLGLOBAL Repeating message (i = 183)
+DLGLOBAL Repeating message (i = 184)
+DLGLOBAL Repeating message (i = 185)
+DLGLOBAL Repeating message (i = 186)
+DLGLOBAL Repeating message (i = 187)
+DLGLOBAL Repeating message (i = 188)
+DLGLOBAL Repeating message (i = 189)
+DLGLOBAL Repeating message (i = 190)
+DLGLOBAL Repeating message (i = 191)
+DLGLOBAL Repeating message (i = 192)
+DLGLOBAL Repeating message (i = 193)
+DLGLOBAL Repeating message (i = 194)
+DLGLOBAL Repeating message (i = 195)
+DLGLOBAL Repeating message (i = 196)
+DLGLOBAL Repeating message (i = 197)
+DLGLOBAL Repeating message (i = 198)
+DLGLOBAL Repeating message (i = 199)
diff --git a/tests/logging/logging_vty_test.c b/tests/logging/logging_vty_test.c
index e7019f61..c1a28533 100644
--- a/tests/logging/logging_vty_test.c
+++ b/tests/logging/logging_vty_test.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#define _GNU_SOURCE
@@ -82,7 +78,7 @@ DEFUN(log_sweep, log_sweep_cmd,
return CMD_SUCCESS;
}
-static void vty_commands_init()
+static void vty_commands_init(void)
{
install_element_ve(&log_sweep_cmd);
}
@@ -125,7 +121,7 @@ const struct log_info log_info = {
.num_cat = ARRAY_SIZE(default_categories),
};
-static void print_help()
+static void print_help(void)
{
printf( "options:\n"
" -h --help this text\n"
@@ -254,6 +250,7 @@ int main(int argc, char **argv)
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_level(osmo_stderr_target, 1);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_target_file_switch_to_wqueue(osmo_stderr_target);
if (cmdline_config.config_file) {
rc = vty_read_config_file(cmdline_config.config_file, NULL);
@@ -264,7 +261,7 @@ int main(int argc, char **argv)
}
}
- rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);
+ rc = telnet_init_default(root_ctx, NULL, 42042);
if (rc < 0)
return 2;
@@ -288,10 +285,10 @@ int main(int argc, char **argv)
osmo_select_main(0);
}
- log_fini();
+ talloc_report(tall_vty_ctx, stderr);
+ talloc_report_full(root_ctx, stderr);
- talloc_free(root_ctx);
- talloc_free(tall_vty_ctx);
+ log_fini();
return 0;
}
diff --git a/tests/logging/logging_vty_test.vty b/tests/logging/logging_vty_test.vty
index 99e8781f..da09be76 100644
--- a/tests/logging/logging_vty_test.vty
+++ b/tests/logging/logging_vty_test.vty
@@ -54,7 +54,7 @@ logging_vty_test# list
logging print level (0|1)
logging print file (0|1|basename) [last]
logging set-log-mask MASK
- logging level (aa|bb|ccc|dddd|eee|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro|lns|lbssgp|lnsdata|lnssignal) (debug|info|notice|error|fatal)
+ logging level (aa|bb|ccc|dddd|eee|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro|lns|lbssgp|lnsdata|lnssignal|liuup|lpfcp|lcsn1|lio) (debug|info|notice|error|fatal)
logging level set-all (debug|info|notice|error|fatal)
logging level force-all (debug|info|notice|error|fatal)
no logging level force-all
@@ -70,6 +70,10 @@ logging_vty_test# logging ?
set-log-mask Set the logmask of this logging target
level Set the log level for a specified category
+logging_vty_test# logging timestamp ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with current timestamp
+
logging_vty_test# logging level ?
... ! all
aa Antropomorphic Armadillos (AA)
@@ -109,6 +113,98 @@ logging_vty_test# logging level set-all ?
error Log error messages and higher levels
fatal Log only fatal messages
+logging_vty_test# logging print ?
+ extended-timestamp Configure log message timestamping
+ thread-id Configure log message logging Thread ID
+ category Configure log message
+ category-hex Configure log message
+ level Configure log message
+ file Configure log message
+
+logging_vty_test# logging print extended-timestamp ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with current timestamp with YYYYMMDDhhmmssnnn
+
+logging_vty_test# logging print thread-id ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with current Thread ID
+
+logging_vty_test# logging print category ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with category/subsystem name
+
+logging_vty_test# logging print category-hex ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with category/subsystem nr in hex ('<000b>')
+
+logging_vty_test# logging print level ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with the log level name
+
+logging_vty_test# logging print file ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with the source file and line
+ basename Prefix each log message with the source file's basename (strip leading paths) and line
+
+logging_vty_test# logging print file basename ?
+ [last] Log source file info at the end of a log line. If omitted, log source file info just before the log text.
+
+logging_vty_test# configure terminal
+logging_vty_test(config)# log stderr
+logging_vty_test(config-log)# show running-config
+...
+log stderr
+... !timestamp
+ logging timestamp 0
+... !timestamp
+
+logging_vty_test(config-log)# logging timestamp 1
+logging_vty_test(config-log)# show running-config
+...
+log stderr
+... !timestamp
+ logging timestamp 1
+... !timestamp
+
+logging_vty_test(config-log)# ### with 'extended-timestamp 1', 'logging timestamp' is not shown
+logging_vty_test(config-log)# logging print extended-timestamp 1
+logging_vty_test(config-log)# show running-config
+...
+log stderr
+... !timestamp
+ logging print extended-timestamp 1
+... !timestamp
+
+logging_vty_test(config-log)# ### 'logging timestamp 0' effect not shown while 'extended-timestamp' == 1
+logging_vty_test(config-log)# logging timestamp 0
+logging_vty_test(config-log)# show running-config
+...
+log stderr
+... !timestamp
+ logging print extended-timestamp 1
+... !timestamp
+
+logging_vty_test(config-log)# ### 'logging timestamp 1' remains set upon 'extended-timestamp 0'
+logging_vty_test(config-log)# logging timestamp 1
+logging_vty_test(config-log)# logging print extended-timestamp 0
+logging_vty_test(config-log)# show running-config
+...
+log stderr
+... !timestamp
+ logging timestamp 1
+... !timestamp
+
+logging_vty_test(config-log)# logging timestamp 0
+logging_vty_test(config-log)# show running-config
+...
+log stderr
+... !timestamp
+ logging timestamp 0
+... !timestamp
+
+logging_vty_test(config-log)# exit
+logging_vty_test(config)# no log stderr
+logging_vty_test(config)# exit
logging_vty_test# log-sweep
DAA DEBUG Log message for DAA on level LOGL_DEBUG
@@ -472,7 +568,7 @@ DEEE FATAL Log message for DEEE on level LOGL_FATAL
logging_vty_test# list
...
- logp (aa|bb|ccc|dddd|eee|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro|lns|lbssgp|lnsdata|lnssignal) (debug|info|notice|error|fatal) .LOGMESSAGE
+ logp (aa|bb|ccc|dddd|eee|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro|lns|lbssgp|lnsdata|lnssignal|liuup|lpfcp|lcsn1|lio) (debug|info|notice|error|fatal) .LOGMESSAGE
...
logging_vty_test# logp?
@@ -507,6 +603,10 @@ logging_vty_test# logp ?
lbssgp GPRS BSSGP layer
lnsdata GPRS NS layer data PDU
lnssignal GPRS NS layer signal PDU
+ liuup Iu UP layer
+ lpfcp libosmo-pfcp Packet Forwarding Control Protocol
+ lcsn1 libosmo-csn1 Concrete Syntax Notation 1 codec
+ lio libosmocore IO Subsystem
logging_vty_test# logp lglobal ?
debug Log debug messages and higher levels
diff --git a/tests/loggingrb/logging_test.err b/tests/loggingrb/logging_test.err
index b59d2e83..e070561f 100644
--- a/tests/loggingrb/logging_test.err
+++ b/tests/loggingrb/logging_test.err
@@ -1,3 +1,2 @@
-You should see this
-You should see this
- \ No newline at end of file
+You should see this
+You should see this
diff --git a/tests/loggingrb/loggingrb_test.c b/tests/loggingrb/loggingrb_test.c
index 19378c3f..fd11896a 100644
--- a/tests/loggingrb/loggingrb_test.c
+++ b/tests/loggingrb/loggingrb_test.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/logging.h>
diff --git a/tests/msgb/msgb_test.c b/tests/msgb/msgb_test.c
index ffaa1557..7966103b 100644
--- a/tests/msgb/msgb_test.c
+++ b/tests/msgb/msgb_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdlib.h>
@@ -65,7 +61,7 @@ static int osmo_panic_try(volatile int *exception, int setjmp_result)
return *exception == 0;
}
-static void test_msgb_api()
+static void test_msgb_api(void)
{
struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
unsigned char *cptr = NULL;
@@ -121,7 +117,7 @@ static void test_msgb_api()
msgb_free(msg);
}
-static void test_msgb_api_errors()
+static void test_msgb_api_errors(void)
{
struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
volatile int e = 0;
@@ -142,7 +138,7 @@ static void test_msgb_api_errors()
osmo_set_panic_handler(NULL);
}
-static void test_msgb_copy()
+static void test_msgb_copy(void)
{
struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
struct msgb *msg2;
@@ -165,19 +161,31 @@ static void test_msgb_copy()
OSMO_ASSERT(msgb_l1len(msg) == msgb_l1len(msg2));
OSMO_ASSERT(msgb_l2len(msg) == msgb_l2len(msg2));
OSMO_ASSERT(msgb_l3len(msg) == msgb_l3len(msg2));
- OSMO_ASSERT(msg->tail - msg->l4h == msg2->tail - msg2->l4h);
+ OSMO_ASSERT(msgb_l4len(msg) == msgb_l4len(msg2));
+
+ if (!msgb_eq_data_print(msg2, msg->data, msgb_length(msg)))
+ printf("copy test failed!\n");
- for (i = 0; i < msgb_length(msg2); i++)
- OSMO_ASSERT(msg2->data[i] == (uint8_t)i);
+ if (!msgb_eq_l1_data_print(msg2, msgb_l1(msg), msgb_l1len(msg)))
+ printf("copy test failed at L1!\n");
+ if (!msgb_eq_l2_data_print(msg2, msgb_l2(msg), msgb_l2len(msg)))
+ printf("copy test failed at L2!\n");
+ if (!msgb_eq_l3_data_print(msg2, msgb_l3(msg), msgb_l3len(msg)))
+ printf("copy test failed at L3!\n");
+ if (!msgb_eq_l4_data_print(msg2, msgb_l4(msg), msgb_l4len(msg)))
+ printf("copy test failed at L4!\n");
printf("Src: %s\n", msgb_hexdump(msg));
- printf("Dst: %s\n", msgb_hexdump(msg));
+ printf("Dst: %s\n", msgb_hexdump(msg2));
+
+ OSMO_ASSERT(msgb_test_invariant(msg));
+ OSMO_ASSERT(msgb_test_invariant(msg2));
msgb_free(msg);
msgb_free(msg2);
}
-static void test_msgb_resize_area()
+static void test_msgb_resize_area(void)
{
struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
int rc;
@@ -277,7 +285,7 @@ static void test_msgb_resize_area()
osmo_set_panic_handler(NULL);
}
-static void test_msgb_printf()
+static void test_msgb_printf(void)
{
struct msgb *msg;
struct msgb *msg_ref;
diff --git a/tests/msgfile/msgfile_test.c b/tests/msgfile/msgfile_test.c
index 2684b6a4..3fe173d6 100644
--- a/tests/msgfile/msgfile_test.c
+++ b/tests/msgfile/msgfile_test.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/msgfile.h>
diff --git a/tests/oap/oap_test.c b/tests/oap/oap_test.c
index 32676ca3..bebd6031 100644
--- a/tests/oap/oap_test.c
+++ b/tests/oap/oap_test.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/application.h>
diff --git a/tests/osmo-auc-gen/osmo-auc-gen_test.ok b/tests/osmo-auc-gen/osmo-auc-gen_test.ok
index 2840783a..2da0b024 100644
--- a/tests/osmo-auc-gen/osmo-auc-gen_test.ok
+++ b/tests/osmo-auc-gen/osmo-auc-gen_test.ok
@@ -1,7 +1,7 @@
> osmo-auc-gen -3 -a milenage -r 6a61050765caa32c90371370e5d6dc2d -k 1dc4f974325cce611e54f516dc1fec56 -o 2a48162ff3edca4adf0b7b5e527d6c16 -s 0
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 6a61050765caa32c90371370e5d6dc2d
@@ -9,6 +9,8 @@ AUTN: 790c5d80c47b0000716ce00883bc39e1
IK: 6cf555588bb61ab2ff23cd333c05ed09
CK: f0b29f50a7d873f30336473bdc35d04f
RES: f511d3a7f06e6a30
+IMS nonce: amEFB2XKoyyQNxNw5dbcLXkMXYDEewAAcWzgCIO8OeE=
+IMS res: 9RHTp/BuajA=
SRES: 057fb997
Kc: 60524000cc5e5407
SQN: 0
@@ -16,7 +18,7 @@ IND: 0
> osmo-auc-gen -3 -a milenage -r 6a61050765caa32c90371370e5d6dc2d -k 1dc4f974325cce611e54f516dc1fec56 -o 2a48162ff3edca4adf0b7b5e527d6c16 -s 1
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 6a61050765caa32c90371370e5d6dc2d
@@ -24,6 +26,8 @@ AUTN: 790c5d80c47a000058508ab3864e26a0
IK: 6cf555588bb61ab2ff23cd333c05ed09
CK: f0b29f50a7d873f30336473bdc35d04f
RES: f511d3a7f06e6a30
+IMS nonce: amEFB2XKoyyQNxNw5dbcLXkMXYDEegAAWFCKs4ZOJqA=
+IMS res: 9RHTp/BuajA=
SRES: 057fb997
Kc: 60524000cc5e5407
SQN: 1
@@ -31,7 +35,7 @@ IND: 1
> osmo-auc-gen -3 -a milenage -r 6a61050765caa32c90371370e5d6dc2d -k 1dc4f974325cce611e54f516dc1fec56 -o 2a48162ff3edca4adf0b7b5e527d6c16 -s 23
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 6a61050765caa32c90371370e5d6dc2d
@@ -39,6 +43,8 @@ AUTN: 790c5d80c46c0000e74d796ec095dbee
IK: 6cf555588bb61ab2ff23cd333c05ed09
CK: f0b29f50a7d873f30336473bdc35d04f
RES: f511d3a7f06e6a30
+IMS nonce: amEFB2XKoyyQNxNw5dbcLXkMXYDEbAAA5015bsCV2+4=
+IMS res: 9RHTp/BuajA=
SRES: 057fb997
Kc: 60524000cc5e5407
SQN: 23
@@ -46,7 +52,7 @@ IND: 23
> osmo-auc-gen -3 -a milenage -r 1dc4f974325cce611e54f516dc1fec56 -k 2a48162ff3edca4adf0b7b5e527d6c16 -o 6a61050765caa32c90371370e5d6dc2d -s 42
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 1dc4f974325cce611e54f516dc1fec56
@@ -54,6 +60,8 @@ AUTN: 434a46a71aeb0000fedc563f27a0916c
IK: d7213dd74860ccb8c14e54c0c4abc91c
CK: c350653d72f7a5bac3a27422e5186019
RES: 912cdfaadd7b0154
+IMS nonce: HcT5dDJczmEeVPUW3B/sVkNKRqca6wAA/txWPyegkWw=
+IMS res: kSzfqt17AVQ=
SRES: 4c57defe
Kc: 169d78081b24c007
SQN: 42
@@ -61,7 +69,7 @@ IND: 10
> osmo-auc-gen -3 -a milenage -r 2a48162ff3edca4adf0b7b5e527d6c16 -k 6a61050765caa32c90371370e5d6dc2d -o 1dc4f974325cce611e54f516dc1fec56 -s 99
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 2a48162ff3edca4adf0b7b5e527d6c16
@@ -69,6 +77,8 @@ AUTN: bfbf3332c91e0000d6199cad31d15f26
IK: 191a93c4396113bff6939d4f98e169a6
CK: 9c38d9089265ed5ea164e190a65c200d
RES: fd40205be2c9c7b2
+IMS nonce: KkgWL/PtykrfC3teUn1sFr+/MzLJHgAA1hmcrTHRXyY=
+IMS res: /UAgW+LJx7I=
SRES: 1f89e7e9
Kc: d2d5361395b9b74a
SQN: 99
@@ -76,7 +86,7 @@ IND: 3
> osmo-auc-gen -3 -a milenage -r 6a61050765caa32c90371370e5d6dc2d -k 2a48162ff3edca4adf0b7b5e527d6c16 -o 1dc4f974325cce611e54f516dc1fec56 -s 281474976710655
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 6a61050765caa32c90371370e5d6dc2d
@@ -84,6 +94,8 @@ AUTN: afb993e4f4b8000069cdeebb4a4b5b58
IK: c348c2fe2f3e1fb37a7ae1638163bd98
CK: e740c156278705a14e1a99ba6d31334f
RES: 7c04e86a67967fcd
+IMS nonce: amEFB2XKoyyQNxNw5dbcLa+5k+T0uAAAac3uu0pLW1g=
+IMS res: fAToameWf80=
SRES: 1b9297a7
Kc: 10687b71e4eb94c5
SQN: 281474976710655
@@ -91,7 +103,7 @@ IND: 31
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -99,6 +111,8 @@ AUTN: 8704f5ba55d30000541dde77ea5b1d8c
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpV0wAAVB3ed+pbHYw=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 32
@@ -107,7 +121,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind 5
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -115,6 +129,8 @@ AUTN: 8704f5ba55d6000079267a4b347ad890
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpV1gAAeSZ6SzR62JA=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 37
@@ -123,7 +139,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind 23
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -131,6 +147,8 @@ AUTN: 8704f5ba55c40000129ddaa4f5016e25
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpVxAAAEp3apPUBbiU=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 55
@@ -139,7 +157,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind 31
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -147,6 +165,8 @@ AUTN: 8704f5ba55cc00009d169f5ff89f6087
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpVzAAAnRafX/ifYIc=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 63
@@ -155,7 +175,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind-len 0
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -163,6 +183,8 @@ AUTN: 8704f5ba55eb0000d7fc4f7f19cfc180
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpV6wAA1/xPfxnPwYA=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 24
@@ -171,7 +193,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind-len 1
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -179,6 +201,8 @@ AUTN: 8704f5ba55eb0000d7fc4f7f19cfc180
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpV6wAA1/xPfxnPwYA=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 24
@@ -187,7 +211,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind-len 1 --ind 1
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -195,6 +219,8 @@ AUTN: 8704f5ba55ea0000aab06de3fd6c01af
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpV6gAAqrBt4/1sAa8=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 25
@@ -203,7 +229,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind-len 8
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -211,6 +237,8 @@ AUTN: 8704f5ba54f30000cbba2fbba3c5e242
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpU8wAAy7ovu6PF4kI=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 256
@@ -219,7 +247,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind-len 8 --ind 1
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -227,6 +255,8 @@ AUTN: 8704f5ba54f200008f8e14579da5ecbb
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpU8gAAj44UV52l7Ls=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 257
@@ -236,27 +266,27 @@ SQN.MS: 23
expecting error:
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind -1
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
expecting error:
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind 32
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
expecting error:
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind 42
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
expecting error:
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind-len 0 --ind 1
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
diff --git a/tests/osmo_io/osmo_io_test.c b/tests/osmo_io/osmo_io_test.c
new file mode 100644
index 00000000..93beef4f
--- /dev/null
+++ b/tests/osmo_io/osmo_io_test.c
@@ -0,0 +1,178 @@
+/*
+ * (C) 2023 by sysmocom s.f.m.c
+ * Author: Daniel Willmann <daniel@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/osmo_io.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/utils.h>
+
+#include "config.h"
+
+#define TEST_START() printf("Running %s\n", __func__)
+
+static uint8_t TESTDATA[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
+};
+
+
+static void *ctx = NULL;
+
+static void read_cb(struct osmo_io_fd *iofd, int rc, struct msgb *msg)
+{
+ printf("%s: read() msg with len=%d\n", osmo_iofd_get_name(iofd), rc);
+ if (msg)
+ printf("%s\n", osmo_hexdump(msgb_data(msg), msgb_length(msg)));
+
+ talloc_free(msg);
+}
+
+static void write_cb(struct osmo_io_fd *iofd, int rc, struct msgb *msg)
+{
+ uint8_t *buf;
+ printf("%s: write() returned rc=%d\n", osmo_iofd_get_name(iofd), rc);
+ if (rc == 0) {
+ msg = msgb_alloc(1024, "Test data");
+ buf = msgb_put(msg, sizeof(TESTDATA));
+ memcpy(buf, TESTDATA, sizeof(TESTDATA));
+
+ osmo_iofd_write_msgb(iofd, msg);
+ }
+}
+
+struct osmo_io_ops ioops_conn_read_write = {
+ .read_cb = read_cb,
+ .write_cb = write_cb,
+};
+
+static void test_connected(void)
+{
+ int fds[2] = {0, 0}, rc;
+ struct osmo_io_fd *iofd1, *iofd2;
+
+ TEST_START();
+
+ rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+ OSMO_ASSERT(rc == 0);
+
+ iofd1 = osmo_iofd_setup(ctx, fds[0], "ep1", OSMO_IO_FD_MODE_READ_WRITE, &ioops_conn_read_write, NULL);
+ osmo_iofd_register(iofd1, fds[0]);
+ iofd2 = osmo_iofd_setup(ctx, fds[1], "ep2", OSMO_IO_FD_MODE_READ_WRITE, &ioops_conn_read_write, NULL);
+ osmo_iofd_register(iofd2, fds[1]);
+ // Explicitly check if ep1 is connected through write_cb
+ osmo_iofd_notify_connected(iofd1);
+
+ /* Allow enough cycles to handle the messages */
+ for (int i = 0; i < 128; i++)
+ osmo_select_main(1);
+
+ osmo_iofd_free(iofd1);
+ osmo_iofd_free(iofd2);
+
+ for (int i = 0; i < 128; i++)
+ osmo_select_main(1);
+}
+
+static void recvfrom_cb(struct osmo_io_fd *iofd, int rc, struct msgb *msg,
+ const struct osmo_sockaddr *saddr)
+{
+ printf("%s: recvfrom() msg with len=%d\n", osmo_iofd_get_name(iofd), rc);
+ if (msg)
+ printf("%s\n", osmo_hexdump(msgb_data(msg), msgb_length(msg)));
+
+ talloc_free(msg);
+}
+
+static void sendto_cb(struct osmo_io_fd *iofd, int rc, struct msgb *msg,
+ const struct osmo_sockaddr *daddr)
+{
+ printf("%s: sendto() returned rc=%d\n", osmo_iofd_get_name(iofd), rc);
+}
+
+struct osmo_io_ops ioops_conn_recvfrom_sendto = {
+ .sendto_cb = sendto_cb,
+ .recvfrom_cb = recvfrom_cb,
+};
+
+static void test_unconnected(void)
+{
+ int fds[2] = {0, 0}, rc;
+ struct osmo_io_fd *iofd1, *iofd2;
+ struct msgb *msg;
+ uint8_t *buf;
+
+ TEST_START();
+
+ rc = socketpair(AF_UNIX, SOCK_DGRAM, 0, fds);
+ OSMO_ASSERT(rc == 0);
+
+ iofd1 = osmo_iofd_setup(ctx, fds[0], "ep1", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &ioops_conn_recvfrom_sendto, NULL);
+ osmo_iofd_register(iofd1, fds[0]);
+ iofd2 = osmo_iofd_setup(ctx, fds[1], "ep2", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &ioops_conn_recvfrom_sendto, NULL);
+ osmo_iofd_register(iofd2, fds[1]);
+
+ msg = msgb_alloc(1024, "Test data");
+ buf = msgb_put(msg, sizeof(TESTDATA));
+ memcpy(buf, TESTDATA, sizeof(TESTDATA));
+
+ osmo_iofd_sendto_msgb(iofd1, msg, 0, NULL);
+
+ /* Allow enough cycles to handle the messages */
+ for (int i = 0; i < 128; i++)
+ osmo_select_main(1);
+
+ osmo_iofd_free(iofd1);
+ osmo_iofd_free(iofd2);
+
+ for (int i = 0; i < 128; i++)
+ osmo_select_main(1);
+}
+static const struct log_info_cat default_categories[] = {
+};
+
+static struct log_info info = {
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+};
+
+int main(int argc, char *argv[])
+{
+ ctx = talloc_named_const(NULL, 0, "osmo_io_test");
+ osmo_init_logging2(ctx, &info);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_category(osmo_stderr_target, 0);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+
+ test_connected();
+ test_unconnected();
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/osmo_io/osmo_io_test.err b/tests/osmo_io/osmo_io_test.err
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/osmo_io/osmo_io_test.err
diff --git a/tests/osmo_io/osmo_io_test.ok b/tests/osmo_io/osmo_io_test.ok
new file mode 100644
index 00000000..6527c191
--- /dev/null
+++ b/tests/osmo_io/osmo_io_test.ok
@@ -0,0 +1,9 @@
+Running test_connected
+ep1: write() returned rc=0
+ep1: write() returned rc=16
+ep2: read() msg with len=16
+01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10
+Running test_unconnected
+ep1: sendto() returned rc=16
+ep2: recvfrom() msg with len=16
+01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10
diff --git a/tests/rlp/rlp_test.c b/tests/rlp/rlp_test.c
new file mode 100644
index 00000000..be26c99d
--- /dev/null
+++ b/tests/rlp/rlp_test.c
@@ -0,0 +1,217 @@
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/rlp.h>
+
+struct rlp_testcase {
+ const char *name;
+ const char *encoded_hex;
+ struct osmo_rlp_frame_decoded decoded;
+};
+
+
+const struct rlp_testcase testcases[] = {
+ {
+ .name = "XID1",
+ .encoded_hex = "f95f1100213d313d414e6108510600000000000000000000000000c13c6b",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_U,
+ .u_ftype = OSMO_RLP_U_FT_XID,
+ .s_ftype = 0,
+ .c_r = 1,
+ .p_f = 1,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 0,
+ .fcs = 0x6b3cc1,
+ .info = { 0x11, 0x00, 0x21, 0x3d, 0x31, 0x3d, 0x41, 0x4e, 0x61, 0x08,
+ 0x51, 0x06, },
+ .info_len = 25,
+ },
+ }, {
+ .name = "XID2",
+ .encoded_hex = "f95f1101213d313d41305106610774000008060000000000000000ba14a0",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_U,
+ .u_ftype = OSMO_RLP_U_FT_XID,
+ .s_ftype = 0,
+ .c_r = 1,
+ .p_f = 1,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 0,
+ .fcs = 0xa014ba,
+ .info = { 0x11, 0x01, 0x21, 0x3d, 0x31, 0x3d, 0x41, 0x30, 0x51, 0x06,
+ 0x61, 0x07, 0x74, 0x00, 0x00, 0x08, 0x06, },
+ .info_len = 25,
+ },
+ }, {
+ .name = "SABM",
+ .encoded_hex = "f91f0000000000000000000000000000000000000000000000000063b2f3",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_U,
+ .u_ftype = OSMO_RLP_U_FT_SABM,
+ .s_ftype = 0,
+ .c_r = 1,
+ .p_f = 1,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 0,
+ .fcs = 0xf3b263,
+ .info = {},
+ .info_len = 0,
+ },
+ }, {
+ .name = "UA",
+ .encoded_hex = "f8330000000000000000000000000000000000000000000000000029d801",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_U,
+ .u_ftype = OSMO_RLP_U_FT_UA,
+ .s_ftype = 0,
+ .c_r = 0,
+ .p_f = 1,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 0,
+ .fcs = 0x01d829,
+ .info = {},
+ .info_len = 0,
+ },
+ }, {
+ .name = "IS1",
+ .encoded_hex = "01001f000000000000000000000000000000000000000000000000f174ad",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_IS,
+ .u_ftype = 0,
+ .s_ftype = 0,
+ .c_r = 1,
+ .p_f = 0,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 0,
+ .fcs = 0xad74f1,
+ .info = { 0x1f, },
+ .info_len = 25,
+ },
+ }, {
+ .name = "IS2",
+ .encoded_hex = "010401661fffffffffffffffffffffffffffffffffffffffffffff388cd3",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_IS,
+ .u_ftype = 0,
+ .s_ftype = 0,
+ .c_r = 1,
+ .p_f = 0,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 1,
+ .fcs = 0xd38c38,
+ .info = { 0x01, 0x66, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff },
+ .info_len = 25,
+ },
+ }, {
+ .name = "DISC",
+ .encoded_hex = "f923000000000000000000000000000000000000000000000000007986f2",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_U,
+ .u_ftype = OSMO_RLP_U_FT_DISC,
+ .s_ftype = 0,
+ .c_r = 1,
+ .p_f = 1,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 0,
+ .fcs = 0xf28679,
+ .info = { },
+ .info_len = 0,
+ },
+ }
+};
+
+static void rlp_frame_print_u(const struct osmo_rlp_frame_decoded *rf)
+{
+ OSMO_ASSERT(rf->ftype == OSMO_RLP_FT_U);
+ printf("C/R=%u P/F=%u U %s (FCS=0x%06x) %s\n", rf->c_r, rf->p_f,
+ get_value_string(osmo_rlp_ftype_u_vals, rf->u_ftype),
+ rf->fcs,
+ rf->u_ftype == OSMO_RLP_U_FT_XID ? osmo_hexdump_nospc(rf->info, rf->info_len) : "");
+}
+
+static void rlp_frame_print_s(const struct osmo_rlp_frame_decoded *rf)
+{
+ OSMO_ASSERT(rf->ftype == OSMO_RLP_FT_S);
+ printf("C/R=%u P/F=%u S N(R)=%u %s (FCS=0x%06x)\n", rf->c_r, rf->p_f,
+ rf->n_r, get_value_string(osmo_rlp_ftype_s_vals, rf->s_ftype),
+ rf->fcs);
+}
+
+static void rlp_frame_print_is(const struct osmo_rlp_frame_decoded *rf)
+{
+ OSMO_ASSERT(rf->ftype == OSMO_RLP_FT_IS);
+ printf("C/R=%u P/F=%u IS N(R)=%u N(S)=%u %s (FCS=0x%06x) %s\n", rf->c_r, rf->p_f,
+ rf->n_r, rf->n_s, get_value_string(osmo_rlp_ftype_s_vals, rf->s_ftype),
+ rf->fcs, osmo_hexdump_nospc(rf->info, rf->info_len));
+}
+
+static void rlp_frame_print(const struct osmo_rlp_frame_decoded *rf)
+{
+ switch (rf->ftype) {
+ case OSMO_RLP_FT_U:
+ rlp_frame_print_u(rf);
+ break;
+ case OSMO_RLP_FT_S:
+ rlp_frame_print_s(rf);
+ break;
+ case OSMO_RLP_FT_IS:
+ rlp_frame_print_is(rf);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void execute_rlp_test(const struct rlp_testcase *tc)
+{
+ struct osmo_rlp_frame_decoded decoded;
+ uint8_t inbuf[240/8];
+ int rc;
+
+ printf("=== STARTING TESTCASE '%s'\n", tc->name);
+
+ rc = osmo_hexparse(tc->encoded_hex, inbuf, sizeof(inbuf));
+ OSMO_ASSERT(rc == 240/8);
+
+ printf("Decoding %s:\n", tc->encoded_hex);
+ rc = osmo_rlp_decode(&decoded, 0, inbuf, rc);
+ OSMO_ASSERT(rc == 0);
+
+ printf("Comparing...\n");
+ rlp_frame_print(&decoded);
+ if (memcmp(&decoded, &tc->decoded, sizeof(decoded))) {
+ printf("DOESN'T MATCH EXPECTED DECODE:\n");
+ rlp_frame_print(&tc->decoded);
+ }
+
+ printf("Reencoding...\n");
+ uint8_t reencoded[240/8];
+ rc = osmo_rlp_encode(reencoded, sizeof(reencoded), &tc->decoded);
+ OSMO_ASSERT(rc == 240/8);
+ if (memcmp(inbuf, reencoded, sizeof(inbuf)))
+ printf("DOESN'T MATCH EXPECTED ENCODE FROM ABOVE\n");
+}
+
+int main(int argc, char **argv)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(testcases); i++) {
+ const struct rlp_testcase *tc = &testcases[i];
+ execute_rlp_test(tc);
+ }
+
+}
diff --git a/tests/rlp/rlp_test.ok b/tests/rlp/rlp_test.ok
new file mode 100644
index 00000000..6d073dbd
--- /dev/null
+++ b/tests/rlp/rlp_test.ok
@@ -0,0 +1,35 @@
+=== STARTING TESTCASE 'XID1'
+Decoding f95f1100213d313d414e6108510600000000000000000000000000c13c6b:
+Comparing...
+C/R=1 P/F=1 U XID (FCS=0x6b3cc1) 1100213d313d414e6108510600000000000000000000000000
+Reencoding...
+=== STARTING TESTCASE 'XID2'
+Decoding f95f1101213d313d41305106610774000008060000000000000000ba14a0:
+Comparing...
+C/R=1 P/F=1 U XID (FCS=0xa014ba) 1101213d313d41305106610774000008060000000000000000
+Reencoding...
+=== STARTING TESTCASE 'SABM'
+Decoding f91f0000000000000000000000000000000000000000000000000063b2f3:
+Comparing...
+C/R=1 P/F=1 U SABM (FCS=0xf3b263)
+Reencoding...
+=== STARTING TESTCASE 'UA'
+Decoding f8330000000000000000000000000000000000000000000000000029d801:
+Comparing...
+C/R=0 P/F=1 U UA (FCS=0x01d829)
+Reencoding...
+=== STARTING TESTCASE 'IS1'
+Decoding 01001f000000000000000000000000000000000000000000000000f174ad:
+Comparing...
+C/R=1 P/F=0 IS N(R)=0 N(S)=0 RR (FCS=0xad74f1) 1f000000000000000000000000000000000000000000000000
+Reencoding...
+=== STARTING TESTCASE 'IS2'
+Decoding 010401661fffffffffffffffffffffffffffffffffffffffffffff388cd3:
+Comparing...
+C/R=1 P/F=0 IS N(R)=1 N(S)=0 RR (FCS=0xd38c38) 01661fffffffffffffffffffffffffffffffffffffffffffff
+Reencoding...
+=== STARTING TESTCASE 'DISC'
+Decoding f923000000000000000000000000000000000000000000000000007986f2:
+Comparing...
+C/R=1 P/F=1 U DISC (FCS=0xf28679)
+Reencoding...
diff --git a/tests/sercomm/sercomm_test.c b/tests/sercomm/sercomm_test.c
index 058c9eb4..9bffc0d8 100644
--- a/tests/sercomm/sercomm_test.c
+++ b/tests/sercomm/sercomm_test.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
diff --git a/tests/sim/sim_test.c b/tests/sim/sim_test.c
index 425ce11d..2e2eec58 100644
--- a/tests/sim/sim_test.c
+++ b/tests/sim/sim_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
diff --git a/tests/sms/sms_test.c b/tests/sms/sms_test.c
index 3e14529c..912c0829 100644
--- a/tests/sms/sms_test.c
+++ b/tests/sms/sms_test.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -219,7 +215,7 @@ static const struct test_case test_decode[] =
},
};
-static void test_octet_return()
+static void test_octet_return(void)
{
char out[256];
int oct, septets;
diff --git a/tests/smscb/cbsp_test.c b/tests/smscb/cbsp_test.c
new file mode 100644
index 00000000..2dbdded7
--- /dev/null
+++ b/tests/smscb/cbsp_test.c
@@ -0,0 +1,108 @@
+/*
+ * (C) 2022 by sysmocom - s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/gsm/protocol/gsm_48_049.h>
+#include <osmocom/gsm/cbsp.h>
+
+/*
+CBSP WRITE-REPLACE FAILURE
+ Message Type: WRITE-REPLACE FAILURE (3)
+ Message Length: 44
+ IE: Message Identifier: 0x0031
+ Information Element Identifier: Message Identifier (14)
+ Message Identifier: 0x0031
+ IE: New Serial Number: 0x4170
+ Information Element Identifier: New Serial Number (3)
+ New Serial Number: 0x4170
+ IE: Failure List: 2 items
+ Information Element Identifier: Failure List (9)
+ Information Element Length: 15
+ Failure List Item: MCC 901 International Mobile, shared code, MNC 70 Clementvale Baltic OÜ, LAC 0x0018, CI 0x0030: Cause Cell-identity-not-valid
+ Cell ID Discriminator: CGI (0)
+ Mobile Country Code (MCC): International Mobile, shared code (901)
+ Mobile Network Code (MNC): Clementvale Baltic OÜ (70)
+ Location Area Code (LAC): 0x0018
+ Cell Identifier (CI): 0x0030
+ Cause: Cell-identity-not-valid (0x03)
+ Failure List Item: LAC 02711, CI 0xc351: Cause LAI-or-LAC-not-valid
+ Cell ID Discriminator: LAC+CI (1)
+ Location Area Code (LAC): 0x2711
+ Cell Identifier (CI): 0xc351
+ Cause: LAI-or-LAC-not-valid (0x0f)
+ IE: Cell List (CGI): 2 items
+ Information Element Identifier: Cell List (4)
+ Information Element Length: 15
+ Cell ID Discriminator: CGI (0)
+ Cell List Item: MCC 901 International Mobile, shared code, MNC 70 Clementvale Baltic OÜ, LAC 0x0017, CI 0x002a
+ Mobile Country Code (MCC): International Mobile, shared code (901)
+ Mobile Network Code (MNC): Clementvale Baltic OÜ (70)
+ Location Area Code (LAC): 0x0017
+ Cell Identifier (CI): 0x002a
+ Cell List Item: MCC 901 International Mobile, shared code, MNC 70 Clementvale Baltic OÜ, LAC 0x0018, CI 0x002a
+ Mobile Country Code (MCC): International Mobile, shared code (901)
+ Mobile Network Code (MNC): Clementvale Baltic OÜ (70)
+ Location Area Code (LAC): 0x0018
+ Cell Identifier (CI): 0x002a
+ IE: Channel Indicator: basic channel
+ Information Element Identifier: Channel Indicator (18)
+ Channel Indicator: basic channel (0x00)
+*/
+static const char write_repl_fail_with_failure_list[] =
+ "0300002c0e003103417009000f0009f1070018003003012711c3510f04000f0009f1070017002a09f1070018002a1200";
+
+static struct msgb *msgb_from_hex(unsigned int size, const char *hex)
+{
+ struct msgb *msg = msgb_alloc(size, "test_cbsp");
+ OSMO_ASSERT(msg);
+ msg->l1h = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg)));
+ msg->l2h = msg->l1h + sizeof(struct cbsp_header);
+ return msg;
+}
+
+static void test_decode(void)
+{
+ struct msgb *msg;
+ struct osmo_cbsp_decoded *cbsp_dec;
+
+ printf("=== %s start ===\n", __func__);
+
+ msg = msgb_from_hex(sizeof(write_repl_fail_with_failure_list),
+ write_repl_fail_with_failure_list);
+
+ cbsp_dec = osmo_cbsp_decode(NULL, msg);
+ OSMO_ASSERT(cbsp_dec);
+
+ talloc_free(cbsp_dec);
+ msgb_free(msg);
+
+ printf("=== %s end ===\n", __func__);
+}
+
+int main(int argc, char **argv)
+{
+ test_decode();
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/smscb/cbsp_test.ok b/tests/smscb/cbsp_test.ok
new file mode 100644
index 00000000..a837de57
--- /dev/null
+++ b/tests/smscb/cbsp_test.ok
@@ -0,0 +1,2 @@
+=== test_decode start ===
+=== test_decode end ===
diff --git a/tests/smscb/gsm0341_test.c b/tests/smscb/gsm0341_test.c
index c400f5c8..966a00fd 100644
--- a/tests/smscb/gsm0341_test.c
+++ b/tests/smscb/gsm0341_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <string.h>
@@ -29,7 +25,7 @@
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
-struct gsm341_ms_message *gen_msg_from_text(uint16_t msg_id, const char *text)
+struct gsm341_ms_message *gen_msg_from_text(uint16_t msg_id, uint8_t msg_code, const char *text)
{
struct gsm341_ms_message *cbmsg;
int text_len = strlen(text);
@@ -38,11 +34,11 @@ struct gsm341_ms_message *gen_msg_from_text(uint16_t msg_id, const char *text)
uint8_t payload[text_len];
int payload_octets;
- srand(time(NULL));
+ //srand(time(NULL));
gsm_7bit_encode_n(payload, sizeof(payload), text, &payload_octets);
//cbmsg = gsm0341_build_msg(NULL, 0, rand(), 0, msg_id, 0x0f, 1, 1, payload, payload_octets);
- cbmsg = gsm0341_build_msg(NULL, 0, rand(), 0, msg_id, 0x00, 1, 1, payload, payload_octets);
+ cbmsg = gsm0341_build_msg(NULL, 0, msg_code, 0, msg_id, 0x00, 1, 1, payload, payload_octets);
printf("%s\n", osmo_hexdump_nospc((uint8_t *)cbmsg, sizeof(*cbmsg)+payload_octets));
@@ -54,6 +50,7 @@ int main(int argc, char **argv)
uint16_t msg_id = GSM341_MSGID_ETWS_CMAS_MONTHLY_TEST;
char *text = "Mahlzeit!";
char tbuf[GSM341_MAX_CHARS+1];
+ struct gsm341_ms_message *cbmsg;
if (argc > 1)
msg_id = atoi(argv[1]);
@@ -67,7 +64,8 @@ int main(int argc, char **argv)
sizeof(tbuf)-strlen(text));
tbuf[GSM341_MAX_CHARS] = 0;
- gen_msg_from_text(msg_id, tbuf);
+ cbmsg = gen_msg_from_text(msg_id, 1, tbuf);
+ talloc_free(cbmsg);
return EXIT_SUCCESS;
}
diff --git a/tests/smscb/gsm0341_test.ok b/tests/smscb/gsm0341_test.ok
new file mode 100644
index 00000000..600797c3
--- /dev/null
+++ b/tests/smscb/gsm0341_test.ok
@@ -0,0 +1 @@
+0010111c0011cd309aad2fa7e9a146a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d100
diff --git a/tests/smscb/smscb_test.c b/tests/smscb/smscb_test.c
index 5925f69b..3b6b74d3 100644
--- a/tests/smscb/smscb_test.c
+++ b/tests/smscb/smscb_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/gsm/protocol/gsm_03_41.h>
diff --git a/tests/sockaddr_str/sockaddr_str_test.c b/tests/sockaddr_str/sockaddr_str_test.c
index 64a61043..4e8500ba 100644
--- a/tests/sockaddr_str/sockaddr_str_test.c
+++ b/tests/sockaddr_str/sockaddr_str_test.c
@@ -18,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -72,21 +68,18 @@ const char *af_name(int af)
}
}
-static const struct value_string err_names[] = {
- { -EINVAL, "-EINVAL" },
- {}
-};
-
-static inline const char *err_name(int err)
-{ return get_value_string(err_names, err); }
-
static inline const char *rc_name(int rc)
{
- if (!rc)
+ switch (rc) {
+ case -EINVAL:
+ return "rc == -EINVAL";
+ case -EAFNOSUPPORT:
+ return "rc == -EAFNOSUPPORT";
+ case 0:
return "rc == 0";
- if (rc < 0)
- return "rc < 0";
- return "rc > 0";
+ default:
+ return rc < 0 ? "rc < 0" : "rc > 0";
+ }
}
void dump_oip(const struct osmo_sockaddr_str *oip)
@@ -94,7 +87,7 @@ void dump_oip(const struct osmo_sockaddr_str *oip)
printf("{ .af = %s, .ip = %s, .port = %u }\n", af_name(oip->af), osmo_quote_str(oip->ip, -1), oip->port);
}
-void sockaddr_str_test_conversions()
+void sockaddr_str_test_conversions(void)
{
int i;
char buf[1024];
@@ -239,7 +232,7 @@ void sockaddr_str_test_conversions()
}
-static void test_osmo_sockaddr_str_cmp()
+static void test_osmo_sockaddr_str_cmp(void)
{
int i;
printf("\n\n%s\n", __func__);
diff --git a/tests/sockaddr_str/sockaddr_str_test.ok b/tests/sockaddr_str/sockaddr_str_test.ok
index 910e9195..a8c16614 100644
--- a/tests/sockaddr_str/sockaddr_str_test.ok
+++ b/tests/sockaddr_str/sockaddr_str_test.ok
@@ -6,14 +6,14 @@
osmo_sockaddr_str_is_nonzero() = true
osmo_sockaddr_str_to_in_addr() rc == 0 in_addr=01020304
-> osmo_sockaddr_str_from_in_addr() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
osmo_sockaddr_str_to_32() rc == 0 uint8_t[4]=[ 01 02 03 04 ]
-> osmo_sockaddr_str_from_32() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
osmo_sockaddr_str_to_32h() rc == 0 uint8_t[4]=[ 04 03 02 01 ]
-> osmo_sockaddr_str_from_32h() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
osmo_sockaddr_str_to_sockaddr_in() rc == 0 sockaddr_in=02000005010203040000000000000000
-> osmo_sockaddr_str_from_sockaddr_in() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0200000501020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
@@ -25,14 +25,14 @@
osmo_sockaddr_str_is_nonzero() = false
osmo_sockaddr_str_to_in_addr() rc == 0 in_addr=00000000
-> osmo_sockaddr_str_from_in_addr() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 0 }
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
osmo_sockaddr_str_to_32() rc == 0 uint8_t[4]=[ 00 00 00 00 ]
-> osmo_sockaddr_str_from_32() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 0 }
osmo_sockaddr_str_to_32h() rc == 0 uint8_t[4]=[ 00 00 00 00 ]
-> osmo_sockaddr_str_from_32h() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 0 }
osmo_sockaddr_str_to_sockaddr_in() rc == 0 sockaddr_in=02000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr_in() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 0 }
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 0 }
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 0 }
@@ -44,14 +44,14 @@
osmo_sockaddr_str_is_nonzero() = true
osmo_sockaddr_str_to_in_addr() rc == 0 in_addr=ffffffff
-> osmo_sockaddr_str_from_in_addr() rc == 0 { .af = AF_INET, .ip = "255.255.255.255", .port = 65535 }
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
osmo_sockaddr_str_to_32() rc == 0 uint8_t[4]=[ ff ff ff ff ]
-> osmo_sockaddr_str_from_32() rc == 0 { .af = AF_INET, .ip = "255.255.255.255", .port = 65535 }
osmo_sockaddr_str_to_32h() rc == 0 uint8_t[4]=[ ff ff ff ff ]
-> osmo_sockaddr_str_from_32h() rc == 0 { .af = AF_INET, .ip = "255.255.255.255", .port = 65535 }
osmo_sockaddr_str_to_sockaddr_in() rc == 0 sockaddr_in=0200ffffffffffff0000000000000000
-> osmo_sockaddr_str_from_sockaddr_in() rc == 0 { .af = AF_INET, .ip = "255.255.255.255", .port = 65535 }
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0200ffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr() rc == 0 { .af = AF_INET, .ip = "255.255.255.255", .port = 65535 }
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "255.255.255.255", .port = 65535 }
@@ -61,13 +61,13 @@
OSMO_SOCKADDR_STR_FMT: '0.0.0.256:1'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=02000001000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0200000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EINVAL in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=02000001000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0200000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "0.0.0.256", .port = 1 }
@@ -75,26 +75,26 @@
OSMO_SOCKADDR_STR_FMT: 'not an ip address:1'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=02000001000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0200000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_from_str() rc < 0 { .af = AF_UNSPEC, .ip = "not an ip address", .port = 1 }
+ osmo_sockaddr_str_to_in_addr() rc == -EINVAL in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=02000001000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0200000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_from_str() rc == -EINVAL { .af = AF_UNSPEC, .ip = "not an ip address", .port = 1 }
{ .af = AF_INET6, .ip = "1:2:3::4", .port = 5 }
OSMO_SOCKADDR_STR_FMT: '[1:2:3::4]:5'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = true
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=00010002000300000000000000000004
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "1:2:3::4", .port = 5 }
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a000005000000000001000200030000000000000000000400000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "1:2:3::4", .port = 5 }
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0a00000500000000000100020003000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
@@ -106,12 +106,12 @@
OSMO_SOCKADDR_STR_FMT: '[::]:0'
osmo_sockaddr_str_is_set() = false
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=00000000000000000000000000000000
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "::", .port = 0 }
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a000000000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "::", .port = 0 }
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
@@ -123,12 +123,12 @@
OSMO_SOCKADDR_STR_FMT: '[::1]:0'
osmo_sockaddr_str_is_set() = false
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=00000000000000000000000000000001
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "::1", .port = 0 }
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a000000000000000000000000000000000000000000000100000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "::1", .port = 0 }
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0a00000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
@@ -140,12 +140,12 @@
OSMO_SOCKADDR_STR_FMT: '[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = true
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=ffffffffffffffffffffffffffffffff
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", .port = 65535 }
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a00ffff00000000ffffffffffffffffffffffffffffffff00000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", .port = 65535 }
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0a00ffff00000000ffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
@@ -157,13 +157,13 @@
OSMO_SOCKADDR_STR_FMT: '[FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = true
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=ffffffffffffffffffffffffffffffff
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", .port = 65535 }
DIFFERS!
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a00ffff00000000ffffffffffffffffffffffffffffffff00000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", .port = 65535 }
DIFFERS!
@@ -177,13 +177,13 @@
OSMO_SOCKADDR_STR_FMT: '[::fffff]:1'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=0a000001000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0a00000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=0a000001000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0a00000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET6, .ip = "::fffff", .port = 1 }
@@ -191,27 +191,27 @@
OSMO_SOCKADDR_STR_FMT: '[not an ip address]:1'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=0a000001000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0a00000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_from_str() rc < 0 { .af = AF_UNSPEC, .ip = "not an ip address", .port = 1 }
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=0a000001000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0a00000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_from_str() rc == -EINVAL { .af = AF_UNSPEC, .ip = "not an ip address", .port = 1 }
{ .af = AF_INET6, .ip = "1.2.3.4", .port = 5 }
OSMO_SOCKADDR_STR_FMT: '[1.2.3.4]:5'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=0a000005000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0a00000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=0a000005000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0a00000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
DIFFERS!
@@ -220,13 +220,13 @@
OSMO_SOCKADDR_STR_FMT: '1:2:3::4:5'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=02000005000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0200000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EINVAL in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=02000005000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0200000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET6, .ip = "1:2:3::4", .port = 5 }
DIFFERS!
@@ -235,13 +235,13 @@
OSMO_SOCKADDR_STR_FMT: '1.2.3.4:5'
osmo_sockaddr_str_is_set() = false
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
DIFFERS!
@@ -250,13 +250,13 @@
OSMO_SOCKADDR_STR_FMT: ':5'
osmo_sockaddr_str_is_set() = false
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=02000005000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0200000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EINVAL in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=02000005000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0200000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc < 0 { .af = AF_UNSPEC, .ip = "", .port = 5 }
@@ -264,13 +264,13 @@
OSMO_SOCKADDR_STR_FMT: '[]:5'
osmo_sockaddr_str_is_set() = false
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=0a000005000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0a00000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=0a000005000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0a00000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc < 0 { .af = AF_UNSPEC, .ip = "", .port = 5 }
@@ -280,14 +280,14 @@
osmo_sockaddr_str_is_nonzero() = false
osmo_sockaddr_str_to_in_addr() rc == 0 in_addr=01020304
-> osmo_sockaddr_str_from_in_addr() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 0 }
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
osmo_sockaddr_str_to_32() rc == 0 uint8_t[4]=[ 01 02 03 04 ]
-> osmo_sockaddr_str_from_32() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 0 }
osmo_sockaddr_str_to_32h() rc == 0 uint8_t[4]=[ 04 03 02 01 ]
-> osmo_sockaddr_str_from_32h() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 0 }
osmo_sockaddr_str_to_sockaddr_in() rc == 0 sockaddr_in=02000000010203040000000000000000
-> osmo_sockaddr_str_from_sockaddr_in() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 0 }
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0200000001020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 0 }
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 0 }
@@ -297,13 +297,13 @@
OSMO_SOCKADDR_STR_FMT: '1.2.3:4:5:0'
osmo_sockaddr_str_is_set() = false
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=02000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EINVAL in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=02000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET6, .ip = "1.2.3:4:5", .port = 0 }
DIFFERS!
@@ -312,13 +312,13 @@
OSMO_SOCKADDR_STR_FMT: '[::1:10.9.8.7]:1'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = true
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=0000000000000000000000010a090807
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "::1:a09:807", .port = 1 }
DIFFERS!
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a000001000000000000000000000000000000010a09080700000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "::1:a09:807", .port = 1 }
DIFFERS!
@@ -334,14 +334,14 @@
osmo_sockaddr_str_is_nonzero() = false
osmo_sockaddr_str_to_in_addr() rc == 0 in_addr=00000000
-> osmo_sockaddr_str_from_in_addr() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 5 }
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
osmo_sockaddr_str_to_32() rc == 0 uint8_t[4]=[ 00 00 00 00 ]
-> osmo_sockaddr_str_from_32() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 5 }
osmo_sockaddr_str_to_32h() rc == 0 uint8_t[4]=[ 00 00 00 00 ]
-> osmo_sockaddr_str_from_32h() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 5 }
osmo_sockaddr_str_to_sockaddr_in() rc == 0 sockaddr_in=02000005000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr_in() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 5 }
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0200000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 5 }
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 5 }
@@ -351,12 +351,12 @@
OSMO_SOCKADDR_STR_FMT: '[::]:5'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=00000000000000000000000000000000
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "::", .port = 5 }
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a000005000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "::", .port = 5 }
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0a00000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
@@ -368,13 +368,13 @@
OSMO_SOCKADDR_STR_FMT: '[0::]:5'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=00000000000000000000000000000000
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "::", .port = 5 }
DIFFERS!
- osmo_sockaddr_str_to_32() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_32h() rc < 0 uint8_t[4]=[ 00 00 00 00 ]
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a000005000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "::", .port = 5 }
DIFFERS!
diff --git a/tests/socket/socket_sctp_test.c b/tests/socket/socket_sctp_test.c
index 4ca52a36..5948abc0 100644
--- a/tests/socket/socket_sctp_test.c
+++ b/tests/socket/socket_sctp_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -35,7 +31,7 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/bits.h>
-#include "../config.h"
+#include "config.h"
void *ctx = NULL;
@@ -111,6 +107,7 @@ static int test_sockinit2_multiaddr(const char **addrv4_loc, const char **addrv6
addrv4_rem, addrv4_size, listen_port_v4,
OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
OSMO_ASSERT(fd >= 0);
+ close(fd);
printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) must fail on mixed IPv4 & IPv6\n");
fd = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP,
@@ -132,6 +129,7 @@ static int test_sockinit2_multiaddr(const char **addrv4_loc, const char **addrv6
addrv4_rem, addrv4_size, listen_port_v4,
OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
OSMO_ASSERT(fd >= 0);
+ close(fd);
printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv6\n");
fd = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP,
@@ -139,6 +137,7 @@ static int test_sockinit2_multiaddr(const char **addrv4_loc, const char **addrv6
addrv6_rem, addrv6_size, listen_port_v6,
OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
OSMO_ASSERT(fd >= 0);
+ close(fd);
close(listen_fd_v4);
close(listen_fd_v6);
diff --git a/tests/socket/socket_sctp_test.err b/tests/socket/socket_sctp_test.err
index 996d0922..7583a2a2 100644
--- a/tests/socket/socket_sctp_test.err
+++ b/tests/socket/socket_sctp_test.err
@@ -1,8 +1,8 @@
invalid: you have to specify either BIND or CONNECT flags
-Invalid v4 vs v6 in local vs remote addresses
-Invalid v4 vs v6 in local vs remote addresses
+Invalid v4 vs v6 in local vs remote addresses: local: v4 remote: v6
+Invalid v4 vs v6 in local vs remote addresses: local: v6 remote: v4
invalid: you have to specify either BIND or CONNECT flags
-Invalid v4 vs v6 in local vs remote addresses
-Invalid v4 vs v6 in local vs remote addresses
+Invalid v4 vs v6 in local vs remote addresses: local: v4 remote: v6
+Invalid v4 vs v6 in local vs remote addresses: local: v6 remote: v4
getaddrinfo(::1, 0) failed: Address family for hostname not supported
getaddrinfo(127.0.0.1, 0) failed: Address family for hostname not supported
diff --git a/tests/socket/socket_test.c b/tests/socket/socket_test.c
index 614db55c..34130b26 100644
--- a/tests/socket/socket_test.c
+++ b/tests/socket/socket_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -36,7 +32,7 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/bits.h>
-#include "../config.h"
+#include "config.h"
void *ctx = NULL;
@@ -154,7 +150,7 @@ static int test_sockinit2(void)
return 0;
}
-static int test_get_ip_and_port()
+static int test_get_ip_and_port(void)
{
int fd, rc;
char ip[INET6_ADDRSTRLEN] = { };
@@ -319,6 +315,7 @@ static void test_osa_str(void)
const char *result;
struct osmo_sockaddr localhost4 = {};
struct osmo_sockaddr localhost6 = {};
+ struct osmo_sockaddr osa = {};
localhost4.u.sin = (struct sockaddr_in){
.sin_family = AF_INET,
@@ -349,9 +346,11 @@ static void test_osa_str(void)
OSMO_ASSERT(!strncmp("[::1]:42", result, sizeof(buf)));
memset(&buf[0], 0, sizeof(buf));
- result = osmo_sockaddr_to_str_buf(buf, 8, &localhost6);
printf("Checking osmo_sockaddr_to_str_buf too short IPv6\n");
- OSMO_ASSERT(!strncmp("[::1]:4", result, sizeof(buf)));
+ result = osmo_sockaddr_to_str_buf(buf, 8, &localhost6);
+ OSMO_ASSERT(result == NULL);
+ osmo_sockaddr_to_str_buf2(buf, 8, &localhost6);
+ OSMO_ASSERT(!strncmp("[::1]:4", buf, sizeof(buf)));
memset(&buf[0], 0, sizeof(buf));
result = osmo_sockaddr_to_str_buf(buf, 5, &localhost6);
@@ -385,6 +384,75 @@ static void test_osa_str(void)
result = osmo_sockaddr_to_str(&localhost6);
printf("Checking osmo_sockaddr_to_str_buf long IPv6 port static buffer\n");
OSMO_ASSERT(!strncmp("[2003:1234:5678:90ab:cdef:1234:4321:4321]:23420", result, sizeof(buf)));
+
+ printf("Checking osmo_sockaddr_from_str_and_uint for 0.0.0.0\n");
+ OSMO_ASSERT(osmo_sockaddr_from_str_and_uint(&osa, "0.0.0.0", 1234) == 0);
+ OSMO_ASSERT(osmo_sockaddr_is_any(&osa));
+
+ printf("Checking osmo_sockaddr_from_str_and_uint for ::\n");
+ OSMO_ASSERT(osmo_sockaddr_from_str_and_uint(&osa, "::", 1234) == 0);
+ OSMO_ASSERT(osmo_sockaddr_is_any(&osa));
+
+ printf("Checking osmo_sockaddr_from_str_and_uint for 1.2.3.4\n");
+ OSMO_ASSERT(osmo_sockaddr_from_str_and_uint(&osa, "1.2.3.4", 1234) == 0);
+ OSMO_ASSERT(!osmo_sockaddr_is_any(&osa));
+}
+
+static void test_osa_netmask_prefixlen(void)
+{
+ struct osmo_sockaddr ipv4;
+ struct osmo_sockaddr ipv6;
+ int rc;
+
+ ipv4.u.sin = (struct sockaddr_in){
+ .sin_family = AF_INET,
+ };
+
+ ipv4.u.sin.sin_addr.s_addr = inet_addr("0.0.0.0");
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4);
+ OSMO_ASSERT(rc == 0);
+
+ ipv4.u.sin.sin_addr.s_addr = inet_addr("255.0.0.0");
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4);
+ OSMO_ASSERT(rc == 8);
+
+ ipv4.u.sin.sin_addr.s_addr = inet_addr("255.255.0.0");
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4);
+ OSMO_ASSERT(rc == 16);
+
+ ipv4.u.sin.sin_addr.s_addr = inet_addr("255.255.255.0");
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4);
+ OSMO_ASSERT(rc == 24);
+
+ ipv4.u.sin.sin_addr.s_addr = inet_addr("255.255.255.255");
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4);
+ OSMO_ASSERT(rc == 32);
+
+ ipv4.u.sin.sin_addr.s_addr = inet_addr("0.255.0.0");
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4);
+ /* FIXME: This shows the implementation is not that robust checking validity of input netmask: */
+ OSMO_ASSERT(rc == 8);
+
+ ipv6.u.sin6 = (struct sockaddr_in6){
+ .sin6_family = AF_INET6,
+ };
+
+ inet_pton(AF_INET6, "fe::", &ipv6.u.sin6.sin6_addr);
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6);
+ OSMO_ASSERT(rc == 7);
+
+ inet_pton(AF_INET6, "ff::", &ipv6.u.sin6.sin6_addr);
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6);
+ OSMO_ASSERT(rc == 8);
+
+ inet_pton(AF_INET6, "ff:ff::", &ipv6.u.sin6.sin6_addr);
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6);
+ OSMO_ASSERT(rc == 16);
+
+ inet_pton(AF_INET6, "ff:ff::ff", &ipv6.u.sin6.sin6_addr);
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6);
+ /* FIXME: This shows the implementation is not that robust checking validity of input netmask: */
+ OSMO_ASSERT(rc == 24);
}
const struct log_info_cat default_categories[] = {
@@ -409,6 +477,7 @@ int main(int argc, char *argv[])
test_get_ip_and_port();
test_sockinit_osa();
test_osa_str();
+ test_osa_netmask_prefixlen();
return EXIT_SUCCESS;
}
diff --git a/tests/socket/socket_test.ok b/tests/socket/socket_test.ok
index 236c0111..2b1c1006 100644
--- a/tests/socket/socket_test.ok
+++ b/tests/socket/socket_test.ok
@@ -31,3 +31,6 @@ Checking osmo_sockaddr_to_str_buf only 5 bytes IPv6
Checking osmo_sockaddr_to_str_buf long IPv6
Checking osmo_sockaddr_to_str_buf long IPv6 port
Checking osmo_sockaddr_to_str_buf long IPv6 port static buffer
+Checking osmo_sockaddr_from_str_and_uint for 0.0.0.0
+Checking osmo_sockaddr_from_str_and_uint for ::
+Checking osmo_sockaddr_from_str_and_uint for 1.2.3.4
diff --git a/tests/soft_uart/soft_uart_test.c b/tests/soft_uart/soft_uart_test.c
new file mode 100644
index 00000000..7280bdcd
--- /dev/null
+++ b/tests/soft_uart/soft_uart_test.c
@@ -0,0 +1,658 @@
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/soft_uart.h>
+
+#define SUART_TEST_BEGIN \
+ do { \
+ printf("\nExecuting %s\n", __func__); \
+ } while (0)
+
+static struct {
+ size_t data_len;
+ const uint8_t *data;
+} g_tx_cb_cfg;
+
+static void suart_rx_cb(void *priv, struct msgb *msg, unsigned int flags)
+{
+ fprintf(stdout, "%s(flags=%02x): %s\n",
+ __func__, flags, msgb_hexdump(msg));
+ msgb_free(msg);
+}
+
+static void suart_tx_cb(void *priv, struct msgb *msg)
+{
+ size_t n_bytes;
+
+ n_bytes = OSMO_MIN(g_tx_cb_cfg.data_len, msg->data_len);
+ if (g_tx_cb_cfg.data != NULL && n_bytes > 0)
+ memcpy(msgb_put(msg, n_bytes), g_tx_cb_cfg.data, n_bytes);
+
+ fprintf(stdout, "%s(len=%u/%u): %s\n",
+ __func__, msg->len, msg->data_len, msgb_hexdump(msg));
+}
+
+static void suart_status_change_cb(void *priv, unsigned int status)
+{
+ fprintf(stdout, "%s(status=0x%08x)\n", __func__, status);
+}
+
+static const struct osmo_soft_uart_cfg suart_test_default_cfg = {
+ .num_data_bits = 8,
+ .num_stop_bits = 1,
+ .parity_mode = OSMO_SUART_PARITY_NONE,
+ .rx_buf_size = 128,
+ .rx_cb = &suart_rx_cb,
+ .tx_cb = &suart_tx_cb,
+ .status_change_cb = &suart_status_change_cb,
+};
+
+static void test_rx_exec(struct osmo_soft_uart *suart,
+ const char *input)
+{
+ for (unsigned int i = 0; input[i] != '\0'; i++) {
+ ubit_t ubit;
+ int rc;
+
+ switch (input[i]) {
+ case '0':
+ case '1':
+ ubit = input[i] - '0';
+ rc = osmo_soft_uart_rx_ubits(suart, &ubit, 1);
+ OSMO_ASSERT(rc == 0); /* 0 on success */
+ break;
+ case 'F':
+ printf("%s() @ %u: flush the Rx buffer\n", __func__, i);
+ osmo_soft_uart_flush_rx(suart);
+ break;
+ case ' ': /* padding */
+ continue;
+ default:
+ printf("%s() @ %u: unknown opcode '%c'\n",
+ __func__, i, input[i]);
+ break;
+ }
+ }
+}
+
+static void test_rx(void)
+{
+ struct osmo_soft_uart_cfg cfg;
+ struct osmo_soft_uart *suart;
+
+ SUART_TEST_BEGIN;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &suart_test_default_cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ osmo_soft_uart_set_rx(suart, true);
+
+ printf("======== testing 8-N-1 (no data)\n");
+ test_rx_exec(suart, "F11111F11111F");
+
+ printf("======== testing 8-N-1 (fill up flush)\n");
+ cfg = suart_test_default_cfg;
+ cfg.rx_buf_size = 4;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_rx_exec(suart, "11111" /* no data */
+ "0 01111011 1"
+ "0 10110101 1"
+ "0 01111101 1"
+ "0 11110111 1" /* filled up, expect flush */
+ "0 00000000 1"
+ "0 01010101 1"
+ "0 10101010 1"
+ "0 11111111 1" /* filled up, expect flush */
+ "F" /* flush! (for sanity) */
+ );
+
+ printf("======== testing 8-N-1 (HELLO)\n");
+ cfg = suart_test_default_cfg;
+ cfg.num_stop_bits = 1;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_rx_exec(suart, "111111" /* no data */
+ "0 00010010 1F" /* 'H', flush! */
+ "0 10100010 1F" /* 'E', flush! */
+ "1111111111111" /* no data */
+ "0 00110010 1F" /* 'L', flush! */
+ "0 00110010 1F" /* 'L', flush! */
+ "1111111111111" /* no data */
+ "0 11110010 1F" /* 'O', flush! */
+ );
+
+ printf("======== testing 8-N-1 (framing errors)\n");
+ test_rx_exec(suart, "11111" /* no data */
+ "0 00000000 0" /* stop bit != 1, expect flush */
+ "0 01010101 0" /* stop bit != 1, expect flush */
+ "0 11111111 1" /* stop bit == 1, recovery */
+ "F" /* flush! */
+ );
+
+ printf("======== testing 8-N-2 (HELLO)\n");
+ cfg = suart_test_default_cfg;
+ cfg.num_stop_bits = 2;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_rx_exec(suart, "11111111" /* no data */
+ "0 00010010 1F1F" /* 'H', flush! */
+ "0 10100010 1F1F" /* 'E', flush! */
+ "111111111111111" /* no data */
+ "0 00110010 1F1F" /* 'L', flush! */
+ "0 00110010 1F1F" /* 'L', flush! */
+ "111111111111111" /* no data */
+ "0 11110010 1F1F" /* 'O', flush! */
+ );
+
+ printf("======== testing 8-N-2 (framing errors)\n");
+ test_rx_exec(suart, "11111" /* no data */
+ "0 00000000 00" /* stop bit != 1, expect flush */
+ "0 01010101 01" /* stop bit != 1, expect flush */
+ "0 10101010 10" /* stop bit != 1, expect flush */
+ "0 11111111 11" /* stop bit == 1, recovery */
+ "F" /* flush! (for sanity) */
+ );
+
+
+ printf("======== testing 8-E-1 (invalid parity)\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_EVEN;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_rx_exec(suart, "1111111" /* no data */
+ "0 00000000 1 1" /* odd parity, expect flush */
+ "0 10000000 0 1" /* odd parity, expect flush */
+ "0 11111111 1 1" /* odd parity, expect flush */
+ "F" /* flush! (for sanity) */
+ );
+ printf("======== testing 8-E-1 (valid parity)\n");
+ test_rx_exec(suart, "1111111" /* no data */
+ "0 00000000 0 1"
+ "0 11111111 0 1"
+ "0 01010101 0 1"
+ "0 10101010 0 1"
+ "F" /* flush! */
+ "0 00000001 1 1"
+ "0 00000111 1 1"
+ "0 00011111 1 1"
+ "0 01111111 1 1"
+ "F" /* flush! */
+ );
+
+ printf("======== testing 8-O-1 (invalid parity)\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_ODD;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_rx_exec(suart,
+ "0 00000000 0 1" /* even parity, expect flush */
+ "0 10000000 1 1" /* even parity, expect flush */
+ "0 11111111 0 1" /* even parity, expect flush */
+ "F" /* flush! (for sanity) */
+ );
+ printf("======== testing 8-O-1 (valid parity)\n");
+ test_rx_exec(suart, "1111111" /* no data */
+ "0 00000000 1 1"
+ "0 11111111 1 1"
+ "0 01010101 1 1"
+ "0 10101010 1 1"
+ "F" /* flush! */
+ "0 00000001 0 1"
+ "0 00000111 0 1"
+ "0 00011111 0 1"
+ "0 01111111 0 1"
+ "F" /* flush! */
+ );
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_rx_flush(void)
+{
+ struct osmo_soft_uart_cfg cfg;
+ struct osmo_soft_uart *suart;
+
+ SUART_TEST_BEGIN;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &suart_test_default_cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ printf("calling osmo_soft_uart_flush_rx() while Rx disabled\n");
+ osmo_soft_uart_flush_rx(suart);
+
+ printf("enabling the receiver\n");
+ osmo_soft_uart_set_rx(suart, true);
+
+ printf("calling osmo_soft_uart_flush_rx() while Rx enabled, but no data\n");
+ osmo_soft_uart_flush_rx(suart);
+
+ /* FIXME: this scenario demonstrates a problem that may occur when the user
+ * flushes the Rx buffer manually while the soft-UART state reflects flags
+ * of an incomplete symbol, for which we're waiting the stop bit. */
+ printf("testing corner case: manual flushing during a parity error (8-E-1)\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_EVEN;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_rx_exec(suart, "1111111" /* no data */
+ "0 01010101 0 1" /* even parity, correct */
+ "0 10101010 0 1" /* even parity, correct */
+ "0 11111111 1" /* odd parity, incorrect, but stop bit is pending */
+ "F" /* manual flush happens before receiving the stop bit */
+ "1" /* finally, the stop bit is received */
+ );
+ /* test_rx_exec() @ 47: flush the Rx buffer
+ * suart_rx_cb(flags=02): aa 55 <--- this is wrong, should be flags=00
+ * suart_rx_cb(flags=02): ff <--- this is expected due to odd parity */
+
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_tx_rx_exec_one(struct osmo_soft_uart *suart,
+ size_t n_bits_total, size_t n_bits_frame)
+{
+ ubit_t tx_buf[n_bits_total];
+ ubit_t *ptr = &tx_buf[0];
+ int rc;
+
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], n_bits_total);
+ OSMO_ASSERT(rc == n_bits_total);
+
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], n_bits_total);
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ printf("%s(n_bits_total=%zu):", __func__, n_bits_total);
+ while (n_bits_total > 0) {
+ size_t n_bits = OSMO_MIN(n_bits_frame, n_bits_total);
+ printf(" %s", osmo_ubit_dump(ptr, n_bits));
+ n_bits_total -= n_bits;
+ ptr += n_bits;
+ }
+ printf("\n");
+}
+
+static void test_tx_rx_exec(struct osmo_soft_uart *suart, size_t n_bits_frame)
+{
+ const uint8_t tx_data[][4] = {
+ { 0xde, 0xad, 0xbe, 0xef },
+ { 0x00, 0xaa, 0x55, 0xff },
+ { 0x01, 0x02, 0x04, 0x08 },
+ { 0x10, 0x20, 0x40, 0x80 },
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(tx_data); i++) {
+ g_tx_cb_cfg.data_len = 4;
+ g_tx_cb_cfg.data = tx_data[i];
+ test_tx_rx_exec_one(suart, 4 * n_bits_frame, n_bits_frame);
+ }
+
+ g_tx_cb_cfg.data_len = 0;
+ g_tx_cb_cfg.data = NULL;
+ test_tx_rx_exec_one(suart, 4 * n_bits_frame, n_bits_frame);
+}
+
+static void test_tx_rx(void)
+{
+ struct osmo_soft_uart_cfg cfg;
+ struct osmo_soft_uart *suart;
+ int rc;
+
+ SUART_TEST_BEGIN;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &suart_test_default_cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ /* expect -EAGAIN when the transmitter is not enabled */
+ rc = osmo_soft_uart_tx_ubits(suart, NULL, 42);
+ OSMO_ASSERT(rc == -EAGAIN);
+ /* expect -EAGAIN when the receiver is not enabled */
+ rc = osmo_soft_uart_rx_ubits(suart, NULL, 42);
+ OSMO_ASSERT(rc == -EAGAIN);
+
+ osmo_soft_uart_set_tx(suart, true);
+ osmo_soft_uart_set_rx(suart, true);
+
+ printf("======== testing 8-N-1\n");
+ test_tx_rx_exec(suart, (1 + 8 + 1));
+
+ printf("======== testing 8-N-2\n");
+ cfg = suart_test_default_cfg;
+ cfg.num_stop_bits = 2;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_tx_rx_exec(suart, (1 + 8 + 2));
+
+ printf("======== testing 8-E-1\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_EVEN;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_tx_rx_exec(suart, (1 + 8 + 1 + 1));
+
+ printf("======== testing 8-O-1\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_ODD;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_tx_rx_exec(suart, (1 + 8 + 1 + 1));
+
+ printf("======== testing 8-M-1\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_MARK;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_tx_rx_exec(suart, (1 + 8 + 1 + 1));
+
+ printf("======== testing 8-S-1\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_SPACE;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_tx_rx_exec(suart, (1 + 8 + 1 + 1));
+
+ printf("======== testing 6-N-1\n");
+ cfg = suart_test_default_cfg;
+ cfg.num_data_bits = 6;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_tx_rx_exec(suart, (1 + 6 + 1));
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_tx_rx_pull_n(unsigned int n)
+{
+ struct osmo_soft_uart *suart;
+ ubit_t tx_buf[32];
+ int rc;
+
+ SUART_TEST_BEGIN;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &suart_test_default_cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ osmo_soft_uart_set_tx(suart, true);
+ osmo_soft_uart_set_rx(suart, true);
+
+ g_tx_cb_cfg.data = (void *)"\x55";
+ g_tx_cb_cfg.data_len = 1;
+
+ printf("======== pulling %lu bits (%u at a time)\n", sizeof(tx_buf), n);
+ for (unsigned int i = 0; i < sizeof(tx_buf); i += n) {
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[i], n);
+ OSMO_ASSERT(rc == n);
+ }
+ printf("%s\n", osmo_ubit_dump(&tx_buf[0], sizeof(tx_buf)));
+
+ printf("======== feeding %lu bits into the receiver\n", sizeof(tx_buf));
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_modem_status(void)
+{
+ struct osmo_soft_uart *suart;
+ unsigned int status;
+
+ SUART_TEST_BEGIN;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &suart_test_default_cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ printf("initial status=0x%08x\n", osmo_soft_uart_get_status(suart));
+
+ printf("de-asserting DCD, which was not asserted\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DCD, false);
+ OSMO_ASSERT(osmo_soft_uart_get_status(suart) == 0x00); /* no change */
+
+ printf("asserting both RI and DCD, expecting the callback to be called twice\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_RI, true);
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DCD, true);
+ status = osmo_soft_uart_get_status(suart);
+ OSMO_ASSERT(status == (OSMO_SUART_STATUS_F_RI | OSMO_SUART_STATUS_F_DCD));
+
+ printf("de-asserting RI, expecting the callback to be called\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_RI, false);
+ status = osmo_soft_uart_get_status(suart);
+ OSMO_ASSERT(status == (OSMO_SUART_STATUS_F_DCD));
+
+ printf("resetting to 0x00, expecting the callback to be called\n");
+ osmo_soft_uart_set_status(suart, 0x00);
+ OSMO_ASSERT(osmo_soft_uart_get_status(suart) == 0x00);
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_flow_control_dtr_dsr(void)
+{
+ struct osmo_soft_uart_cfg cfg;
+ struct osmo_soft_uart *suart;
+ ubit_t tx_buf[40];
+ int rc;
+
+ SUART_TEST_BEGIN;
+
+ g_tx_cb_cfg.data = (void *)"\x42\x42\x42\x42";
+ g_tx_cb_cfg.data_len = 4;
+
+ cfg = suart_test_default_cfg;
+ cfg.flow_ctrl_mode = OSMO_SUART_FLOW_CTRL_DTR_DSR;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ osmo_soft_uart_set_tx(suart, true);
+ osmo_soft_uart_set_rx(suart, true);
+
+ /* expect the initial status to be 0 (all lines de-asserted) */
+ printf("initial status=0x%08x\n", osmo_soft_uart_get_status(suart));
+
+ memset(&tx_buf[0], 1, sizeof(tx_buf)); /* pre-initialize */
+
+ printf("expecting osmo_soft_uart_tx_ubits() to yield nothing\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+
+ printf("expecting osmo_soft_uart_rx_ubits() to yield nothing\n");
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ /* both DTR and DSR are asserted, expect both Rx and Tx to work */
+ printf("======== asserting both DTR and DSR\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DTR, true);
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DSR, true);
+
+ memset(&tx_buf[0], 1, sizeof(tx_buf)); /* pre-initialize */
+
+ printf("expecting osmo_soft_uart_tx_ubits() to "
+ "yield %zu bits (requesting %zu bits)\n",
+ sizeof(tx_buf), sizeof(tx_buf));
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == sizeof(tx_buf));
+ printf("%s\n", osmo_ubit_dump(&tx_buf[0], sizeof(tx_buf)));
+
+ printf("expecting osmo_soft_uart_rx_ubits() to "
+ "consume %zu bits and yield %zu chars\n",
+ sizeof(tx_buf), sizeof(tx_buf) / 10);
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ memset(&tx_buf[0], 1, sizeof(tx_buf)); /* pre-initialize */
+
+ /* make the transmitter consume one char, but pull only 2 bits */
+ printf("expecting osmo_soft_uart_tx_ubits() to "
+ "yield 2 bits (requesting 2 bits)\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], 2);
+ OSMO_ASSERT(rc == 2);
+
+ /* CTS gets de-asserted, the transmitter is shutting down */
+ printf("======== de-asserting DSR\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DSR, false);
+
+ /* expect only the remaining 8 bits to be pulled out */
+ printf("expecting osmo_soft_uart_tx_ubits() to "
+ "yield 8 bits (requesting %zu bits)\n", sizeof(tx_buf));
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[2], sizeof(tx_buf) - 2);
+ OSMO_ASSERT(rc == 8);
+
+ printf("expecting osmo_soft_uart_rx_ubits() to "
+ "consume %zu bits and yield a pending char\n", sizeof(tx_buf));
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_flow_control_rts_cts(void)
+{
+ struct osmo_soft_uart_cfg cfg;
+ struct osmo_soft_uart *suart;
+ ubit_t tx_buf[40];
+ int rc;
+
+ SUART_TEST_BEGIN;
+
+ g_tx_cb_cfg.data = (void *)"\x42\x42\x42\x42";
+ g_tx_cb_cfg.data_len = 4;
+
+ cfg = suart_test_default_cfg;
+ cfg.flow_ctrl_mode = OSMO_SUART_FLOW_CTRL_RTS_CTS;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ osmo_soft_uart_set_tx(suart, true);
+ osmo_soft_uart_set_rx(suart, true);
+
+ /* expect the initial status to be 0 (all lines de-asserted) */
+ printf("initial status=0x%08x\n", osmo_soft_uart_get_status(suart));
+
+ memset(&tx_buf[0], 1, sizeof(tx_buf)); /* pre-initialize */
+
+ printf("expecting osmo_soft_uart_tx_ubits() to yield nothing\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+
+ printf("expecting osmo_soft_uart_rx_ubits() to yield nothing\n");
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ /* both RTS/RTR and CTS are asserted, expect both Rx and Tx to work */
+ printf("======== asserting both CTS and RTS/RTR\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_CTS, true);
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_RTS_RTR, true);
+
+ memset(&tx_buf[0], 1, sizeof(tx_buf)); /* pre-initialize */
+
+ printf("expecting osmo_soft_uart_tx_ubits() to "
+ "yield %zu bits (requesting %zu bits)\n",
+ sizeof(tx_buf), sizeof(tx_buf));
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == sizeof(tx_buf));
+ printf("%s\n", osmo_ubit_dump(&tx_buf[0], sizeof(tx_buf)));
+
+ printf("expecting osmo_soft_uart_rx_ubits() to "
+ "consume %zu bits and yield %zu chars\n",
+ sizeof(tx_buf), sizeof(tx_buf) / 10);
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ memset(&tx_buf[0], 1, sizeof(tx_buf)); /* pre-initialize */
+
+ /* make the transmitter consume one char, but pull only 2 bits */
+ printf("expecting osmo_soft_uart_tx_ubits() to "
+ "yield 2 bits (requesting 2 bits)\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], 2);
+ OSMO_ASSERT(rc == 2);
+
+ /* CTS gets de-asserted, the transmitter is shutting down */
+ printf("======== de-asserting CTS\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_CTS, false);
+
+ /* expect only the remaining 8 bits to be pulled out */
+ printf("expecting osmo_soft_uart_tx_ubits() to "
+ "yield 8 bits (requesting %zu bits)\n", sizeof(tx_buf));
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[2], sizeof(tx_buf) - 2);
+ OSMO_ASSERT(rc == 8);
+
+ printf("expecting osmo_soft_uart_rx_ubits() to "
+ "consume %zu bits and yield a pending char\n", sizeof(tx_buf));
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_tx_pull(void)
+{
+ struct osmo_soft_uart *suart;
+ ubit_t tx_buf[25 * 2];
+ int rc;
+
+ SUART_TEST_BEGIN;
+
+ g_tx_cb_cfg.data = (void *)"\x42\x42\x42\x42\x42";
+ g_tx_cb_cfg.data_len = 5;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &suart_test_default_cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ osmo_soft_uart_set_tx(suart, true);
+
+ printf("pulling 25 bits (first time) out of the transmitter\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf) / 2);
+ OSMO_ASSERT(rc == 25);
+
+ printf("pulling 25 bits (second time) out of the transmitter\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[25], sizeof(tx_buf) / 2);
+ OSMO_ASSERT(rc == 25);
+
+ /* FIXME: we pull total 25 + 25 == 50 bits out of the transmitter, which is enough
+ * to fit 5 characters (assuming 8-N-1). However, the current impelementation would
+ * pull only 2 + 2 == characters total, wasting 5 + 5 == 10 bits for padding. */
+
+ osmo_soft_uart_free(suart);
+}
+
+int main(int argc, char **argv)
+{
+ test_rx();
+ test_rx_flush();
+ test_tx_rx();
+
+ /* test pulling small number of bits at a time */
+ test_tx_rx_pull_n(1);
+ test_tx_rx_pull_n(2);
+ test_tx_rx_pull_n(4);
+ test_tx_rx_pull_n(8);
+
+ test_tx_pull();
+
+ /* test flow control */
+ test_modem_status();
+ test_flow_control_dtr_dsr();
+ test_flow_control_rts_cts();
+
+ return 0;
+}
diff --git a/tests/soft_uart/soft_uart_test.ok b/tests/soft_uart/soft_uart_test.ok
new file mode 100644
index 00000000..dcd7ceb4
--- /dev/null
+++ b/tests/soft_uart/soft_uart_test.ok
@@ -0,0 +1,278 @@
+
+Executing test_rx
+======== testing 8-N-1 (no data)
+test_rx_exec() @ 0: flush the Rx buffer
+test_rx_exec() @ 6: flush the Rx buffer
+test_rx_exec() @ 12: flush the Rx buffer
+======== testing 8-N-1 (fill up flush)
+suart_rx_cb(flags=00): de ad be ef
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_rx_exec() @ 101: flush the Rx buffer
+======== testing 8-N-1 (HELLO)
+test_rx_exec() @ 18: flush the Rx buffer
+suart_rx_cb(flags=00): 48
+test_rx_exec() @ 31: flush the Rx buffer
+suart_rx_cb(flags=00): 45
+test_rx_exec() @ 57: flush the Rx buffer
+suart_rx_cb(flags=00): 4c
+test_rx_exec() @ 70: flush the Rx buffer
+suart_rx_cb(flags=00): 4c
+test_rx_exec() @ 96: flush the Rx buffer
+suart_rx_cb(flags=00): 4f
+======== testing 8-N-1 (framing errors)
+suart_rx_cb(flags=01): 00
+suart_rx_cb(flags=01): aa
+test_rx_exec() @ 41: flush the Rx buffer
+suart_rx_cb(flags=00): ff
+======== testing 8-N-2 (HELLO)
+test_rx_exec() @ 20: flush the Rx buffer
+test_rx_exec() @ 22: flush the Rx buffer
+suart_rx_cb(flags=00): 48
+test_rx_exec() @ 35: flush the Rx buffer
+test_rx_exec() @ 37: flush the Rx buffer
+suart_rx_cb(flags=00): 45
+test_rx_exec() @ 65: flush the Rx buffer
+test_rx_exec() @ 67: flush the Rx buffer
+suart_rx_cb(flags=00): 4c
+test_rx_exec() @ 80: flush the Rx buffer
+test_rx_exec() @ 82: flush the Rx buffer
+suart_rx_cb(flags=00): 4c
+test_rx_exec() @ 110: flush the Rx buffer
+test_rx_exec() @ 112: flush the Rx buffer
+suart_rx_cb(flags=00): 4f
+======== testing 8-N-2 (framing errors)
+suart_rx_cb(flags=01): 00
+suart_rx_cb(flags=01): aa
+suart_rx_cb(flags=01): 55
+test_rx_exec() @ 57: flush the Rx buffer
+suart_rx_cb(flags=00): ff
+======== testing 8-E-1 (invalid parity)
+suart_rx_cb(flags=02): 00
+suart_rx_cb(flags=02): 01
+suart_rx_cb(flags=02): ff
+test_rx_exec() @ 49: flush the Rx buffer
+======== testing 8-E-1 (valid parity)
+test_rx_exec() @ 63: flush the Rx buffer
+suart_rx_cb(flags=00): 00 ff aa 55
+test_rx_exec() @ 120: flush the Rx buffer
+suart_rx_cb(flags=00): 80 e0 f8 fe
+======== testing 8-O-1 (invalid parity)
+suart_rx_cb(flags=02): 00
+suart_rx_cb(flags=02): 01
+suart_rx_cb(flags=02): ff
+test_rx_exec() @ 42: flush the Rx buffer
+======== testing 8-O-1 (valid parity)
+test_rx_exec() @ 63: flush the Rx buffer
+suart_rx_cb(flags=00): 00 ff aa 55
+test_rx_exec() @ 120: flush the Rx buffer
+suart_rx_cb(flags=00): 80 e0 f8 fe
+
+Executing test_rx_flush
+calling osmo_soft_uart_flush_rx() while Rx disabled
+enabling the receiver
+calling osmo_soft_uart_flush_rx() while Rx enabled, but no data
+testing corner case: manual flushing during a parity error (8-E-1)
+test_rx_exec() @ 47: flush the Rx buffer
+suart_rx_cb(flags=02): aa 55
+suart_rx_cb(flags=02): ff
+
+Executing test_tx_rx
+======== testing 8-N-1
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): de ad be ef
+test_tx_rx_exec_one(n_bits_total=40): 0011110111 0101101011 0011111011 0111101111
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_tx_rx_exec_one(n_bits_total=40): 0000000001 0010101011 0101010101 0111111111
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=40): 0100000001 0010000001 0001000001 0000100001
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 40 80
+test_tx_rx_exec_one(n_bits_total=40): 0000010001 0000001001 0000000101 0000000011
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=40): 1111111111 1111111111 1111111111 1111111111
+======== testing 8-N-2
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): de ad be ef
+test_tx_rx_exec_one(n_bits_total=44): 00111101111 01011010111 00111110111 01111011111
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_tx_rx_exec_one(n_bits_total=44): 00000000011 00101010111 01010101011 01111111111
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=44): 01000000011 00100000011 00010000011 00001000011
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 40 80
+test_tx_rx_exec_one(n_bits_total=44): 00000100011 00000010011 00000001011 00000000111
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=44): 11111111111 11111111111 11111111111 11111111111
+======== testing 8-E-1
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): de ad be ef
+test_tx_rx_exec_one(n_bits_total=44): 00111101101 01011010111 00111110101 01111011111
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_tx_rx_exec_one(n_bits_total=44): 00000000001 00101010101 01010101001 01111111101
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=44): 01000000011 00100000011 00010000011 00001000011
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 40 80
+test_tx_rx_exec_one(n_bits_total=44): 00000100011 00000010011 00000001011 00000000111
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=44): 11111111111 11111111111 11111111111 11111111111
+======== testing 8-O-1
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): de ad be ef
+test_tx_rx_exec_one(n_bits_total=44): 00111101111 01011010101 00111110111 01111011101
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_tx_rx_exec_one(n_bits_total=44): 00000000011 00101010111 01010101011 01111111111
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=44): 01000000001 00100000001 00010000001 00001000001
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 40 80
+test_tx_rx_exec_one(n_bits_total=44): 00000100001 00000010001 00000001001 00000000101
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=44): 11111111111 11111111111 11111111111 11111111111
+======== testing 8-M-1
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): de ad be ef
+test_tx_rx_exec_one(n_bits_total=44): 00111101111 01011010111 00111110111 01111011111
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_tx_rx_exec_one(n_bits_total=44): 00000000011 00101010111 01010101011 01111111111
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=44): 01000000011 00100000011 00010000011 00001000011
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 40 80
+test_tx_rx_exec_one(n_bits_total=44): 00000100011 00000010011 00000001011 00000000111
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=44): 11111111111 11111111111 11111111111 11111111111
+======== testing 8-S-1
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): de ad be ef
+test_tx_rx_exec_one(n_bits_total=44): 00111101101 01011010101 00111110101 01111011101
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_tx_rx_exec_one(n_bits_total=44): 00000000001 00101010101 01010101001 01111111101
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=44): 01000000001 00100000001 00010000001 00001000001
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 40 80
+test_tx_rx_exec_one(n_bits_total=44): 00000100001 00000010001 00000001001 00000000101
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=44): 11111111111 11111111111 11111111111 11111111111
+======== testing 6-N-1
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): 1e 2d 3e 2f
+test_tx_rx_exec_one(n_bits_total=32): 00111101 01011011 00111111 01111011
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 2a 15 3f
+test_tx_rx_exec_one(n_bits_total=32): 00000001 00101011 01010101 01111111
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=32): 01000001 00100001 00010001 00001001
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 00 00
+test_tx_rx_exec_one(n_bits_total=32): 00000101 00000011 00000001 00000001
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=32): 11111111 11111111 11111111 11111111
+
+Executing test_tx_rx_pull_n
+======== pulling 32 bits (1 at a time)
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+01010101010101010101010101010101
+======== feeding 32 bits into the receiver
+suart_rx_cb(flags=00): 55 55 55
+
+Executing test_tx_rx_pull_n
+======== pulling 32 bits (2 at a time)
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+01010101010101010101010101010101
+======== feeding 32 bits into the receiver
+suart_rx_cb(flags=00): 55 55 55
+
+Executing test_tx_rx_pull_n
+======== pulling 32 bits (4 at a time)
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+01010101011101010101011101010101
+======== feeding 32 bits into the receiver
+suart_rx_cb(flags=00): 55 55
+
+Executing test_tx_rx_pull_n
+======== pulling 32 bits (8 at a time)
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+01010101011111110101010101111111
+======== feeding 32 bits into the receiver
+suart_rx_cb(flags=00): 55 55
+
+Executing test_tx_pull
+pulling 25 bits (first time) out of the transmitter
+suart_tx_cb(len=2/2): 42 42
+pulling 25 bits (second time) out of the transmitter
+suart_tx_cb(len=2/2): 42 42
+
+Executing test_modem_status
+initial status=0x00000000
+de-asserting DCD, which was not asserted
+asserting both RI and DCD, expecting the callback to be called twice
+suart_status_change_cb(status=0x00000008)
+suart_status_change_cb(status=0x0000000a)
+de-asserting RI, expecting the callback to be called
+suart_status_change_cb(status=0x00000002)
+resetting to 0x00, expecting the callback to be called
+suart_status_change_cb(status=0x00000000)
+
+Executing test_flow_control_dtr_dsr
+initial status=0x00000000
+expecting osmo_soft_uart_tx_ubits() to yield nothing
+expecting osmo_soft_uart_rx_ubits() to yield nothing
+======== asserting both DTR and DSR
+suart_status_change_cb(status=0x00000001)
+suart_status_change_cb(status=0x00000005)
+expecting osmo_soft_uart_tx_ubits() to yield 40 bits (requesting 40 bits)
+suart_tx_cb(len=4/4): 42 42 42 42
+0010000101001000010100100001010010000101
+expecting osmo_soft_uart_rx_ubits() to consume 40 bits and yield 4 chars
+suart_rx_cb(flags=00): 42 42 42 42
+expecting osmo_soft_uart_tx_ubits() to yield 2 bits (requesting 2 bits)
+suart_tx_cb(len=1/1): 42
+======== de-asserting DSR
+suart_status_change_cb(status=0x00000001)
+expecting osmo_soft_uart_tx_ubits() to yield 8 bits (requesting 40 bits)
+expecting osmo_soft_uart_rx_ubits() to consume 40 bits and yield a pending char
+suart_rx_cb(flags=00): 42
+
+Executing test_flow_control_rts_cts
+initial status=0x00000000
+expecting osmo_soft_uart_tx_ubits() to yield nothing
+expecting osmo_soft_uart_rx_ubits() to yield nothing
+======== asserting both CTS and RTS/RTR
+suart_status_change_cb(status=0x00000020)
+suart_status_change_cb(status=0x00000030)
+expecting osmo_soft_uart_tx_ubits() to yield 40 bits (requesting 40 bits)
+suart_tx_cb(len=4/4): 42 42 42 42
+0010000101001000010100100001010010000101
+expecting osmo_soft_uart_rx_ubits() to consume 40 bits and yield 4 chars
+suart_rx_cb(flags=00): 42 42 42 42
+expecting osmo_soft_uart_tx_ubits() to yield 2 bits (requesting 2 bits)
+suart_tx_cb(len=1/1): 42
+======== de-asserting CTS
+suart_status_change_cb(status=0x00000010)
+expecting osmo_soft_uart_tx_ubits() to yield 8 bits (requesting 40 bits)
+expecting osmo_soft_uart_rx_ubits() to consume 40 bits and yield a pending char
+suart_rx_cb(flags=00): 42
diff --git a/tests/stats/stats_test.c b/tests/stats/stats_test.c
index a4aedd38..eda4129b 100644
--- a/tests/stats/stats_test.c
+++ b/tests/stats/stats_test.c
@@ -1,6 +1,6 @@
/* tests for statistics */
/*
- * (C) 2015 sysmocom - s.m.f.c. GmbH
+ * (C) 2015 sysmocom - s.f.m.c. GmbH
*
* All Rights Reserved
*
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/application.h>
@@ -29,6 +25,8 @@
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stats.h>
+#include <stat_item_internal.h>
+
#include <stdio.h>
#include <inttypes.h>
@@ -88,11 +86,10 @@ static void stat_test(void)
struct osmo_stat_item_group *sgrp2;
const struct osmo_stat_item *sitem1, *sitem2;
- int rc;
int32_t value;
- int32_t next_id_a = 1;
- int32_t next_id_b = 1;
int i;
+ int64_t sum1;
+ int64_t sum2;
OSMO_ASSERT(statg != NULL);
@@ -110,131 +107,151 @@ static void stat_test(void)
sitem1 = osmo_stat_item_get_by_name(statg, "item.a");
OSMO_ASSERT(sitem1 != NULL);
- OSMO_ASSERT(sitem1 == statg->items[TEST_A_ITEM]);
+ OSMO_ASSERT(sitem1 == osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
sitem2 = osmo_stat_item_get_by_name(statg, "item.b");
OSMO_ASSERT(sitem2 != NULL);
OSMO_ASSERT(sitem2 != sitem1);
- OSMO_ASSERT(sitem2 == statg->items[TEST_B_ITEM]);
+ OSMO_ASSERT(sitem2 == osmo_stat_item_group_get_item(statg, TEST_B_ITEM));
- value = osmo_stat_item_get_last(statg->items[TEST_A_ITEM]);
+ /* No value set yet, expecting default value from osmo_stat_item_desc definition above. */
+ value = osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
OSMO_ASSERT(value == -1);
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 0);
-
- osmo_stat_item_set(statg->items[TEST_A_ITEM], 1);
-
- value = osmo_stat_item_get_last(statg->items[TEST_A_ITEM]);
- OSMO_ASSERT(value == 1);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 1);
+ /* No value set yet, expecting new value in all fields */
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 1);
+ sum1 = 1;
+ value = osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
OSMO_ASSERT(value == 1);
+ OSMO_ASSERT(sitem1->value.n == 1);
+ OSMO_ASSERT(sitem1->value.min == 1);
+ OSMO_ASSERT(sitem1->value.last == 1);
+ OSMO_ASSERT(sitem1->value.max == 1);
+ OSMO_ASSERT(sitem1->value.sum == 1);
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 0);
-
+ sum2 = 0;
for (i = 2; i <= 32; i++) {
- osmo_stat_item_set(statg->items[TEST_A_ITEM], i);
- osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + i);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 1);
- OSMO_ASSERT(value == i);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &next_id_b, &value);
- OSMO_ASSERT(rc == 1);
- OSMO_ASSERT(value == 1000 + i);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), i);
+ sum1 += i;
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_B_ITEM), 1000 + i);
+ sum2 += 1000 + i;
}
+ OSMO_ASSERT(sitem1->value.n == 32);
+ OSMO_ASSERT(sitem1->value.min == 1);
+ OSMO_ASSERT(sitem1->value.last == 32);
+ OSMO_ASSERT(sitem1->value.max == 32);
+ OSMO_ASSERT(sitem1->value.sum == sum1);
+
+ OSMO_ASSERT(sitem2->value.n == 31);
+ OSMO_ASSERT(sitem2->value.min == 1002);
+ OSMO_ASSERT(sitem2->value.last == 1032);
+ OSMO_ASSERT(sitem2->value.max == 1032);
+ OSMO_ASSERT(sitem2->value.sum == sum2);
/* check if dec & inc is working */
- osmo_stat_item_set(statg->items[TEST_A_ITEM], 42);
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 1);
- OSMO_ASSERT(value == 42);
-
- osmo_stat_item_dec(statg->items[TEST_A_ITEM], 21);
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 1);
- OSMO_ASSERT(value == 21);
-
- osmo_stat_item_inc(statg->items[TEST_A_ITEM], 21);
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 1);
- OSMO_ASSERT(value == 42);
-
- /* Keep 2 in FIFO */
- osmo_stat_item_set(statg->items[TEST_A_ITEM], 33);
- osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + 33);
-
- for (i = 34; i <= 64; i++) {
- osmo_stat_item_set(statg->items[TEST_A_ITEM], i);
- osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + i);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 1);
- OSMO_ASSERT(value == i-1);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &next_id_b, &value);
- OSMO_ASSERT(rc == 1);
- OSMO_ASSERT(value == 1000 + i-1);
- }
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 1);
- OSMO_ASSERT(value == 64);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &next_id_b, &value);
- OSMO_ASSERT(rc == 1);
- OSMO_ASSERT(value == 1000 + 64);
-
- /* Overrun FIFOs */
- for (i = 65; i <= 96; i++) {
- osmo_stat_item_set(statg->items[TEST_A_ITEM], i);
- osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + i);
- }
-
- fprintf(stderr, "Skipping %d values\n", 93 - 65);
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 93 - 65 + 1);
- OSMO_ASSERT(value == 93);
-
- for (i = 94; i <= 96; i++) {
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 1);
- OSMO_ASSERT(value == i);
- }
-
- fprintf(stderr, "Skipping %d values\n", 90 - 65);
- rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &next_id_b, &value);
- OSMO_ASSERT(rc == 90 - 65 + 1);
- OSMO_ASSERT(value == 1000 + 90);
-
- for (i = 91; i <= 96; i++) {
- rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &next_id_b, &value);
- OSMO_ASSERT(rc == 1);
- OSMO_ASSERT(value == 1000 + i);
- }
-
- /* Test Discard (single item) */
- osmo_stat_item_set(statg->items[TEST_A_ITEM], 97);
- rc = osmo_stat_item_discard(statg->items[TEST_A_ITEM], &next_id_a);
- OSMO_ASSERT(rc == 1);
-
- rc = osmo_stat_item_discard(statg->items[TEST_A_ITEM], &next_id_a);
- OSMO_ASSERT(rc == 0);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 0);
-
- osmo_stat_item_set(statg->items[TEST_A_ITEM], 98);
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 1);
- OSMO_ASSERT(value == 98);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &next_id_a, &value);
- OSMO_ASSERT(rc == 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 42);
+ sum1 += 42;
+ OSMO_ASSERT(sitem1->value.n == 33);
+ OSMO_ASSERT(sitem1->value.min == 1);
+ OSMO_ASSERT(sitem1->value.last == 42);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 42);
+ OSMO_ASSERT(sitem1->value.max == 42);
+ OSMO_ASSERT(sitem1->value.sum == sum1);
+
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 21);
+ sum1 += 42 - 21;
+ OSMO_ASSERT(sitem1->value.n == 34);
+ OSMO_ASSERT(sitem1->value.min == 1);
+ OSMO_ASSERT(sitem1->value.last == 21);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 21);
+ OSMO_ASSERT(sitem1->value.max == 42);
+ OSMO_ASSERT(sitem1->value.sum == sum1);
+
+ osmo_stat_item_inc(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 21);
+ sum1 += 42;
+ OSMO_ASSERT(sitem1->value.n == 35);
+ OSMO_ASSERT(sitem1->value.min == 1);
+ OSMO_ASSERT(sitem1->value.last == 42);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 42);
+ OSMO_ASSERT(sitem1->value.max == 42);
+ OSMO_ASSERT(sitem1->value.sum == sum1);
+
+ /* Test item flush, reporting period elapsing */
+ osmo_stat_item_flush(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
+ OSMO_ASSERT(sitem1->value.n == 0);
+ OSMO_ASSERT(sitem1->value.min == 42);
+ OSMO_ASSERT(sitem1->value.last == 42);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 42);
+ OSMO_ASSERT(sitem1->value.max == 42);
+ OSMO_ASSERT(sitem1->value.sum == 0);
+
+ /* Still see the previous reporting period in reported.* */
+ OSMO_ASSERT(sitem1->reported.n == 35);
+ OSMO_ASSERT(sitem1->reported.min == 1);
+ OSMO_ASSERT(sitem1->reported.last == 42);
+ OSMO_ASSERT(sitem1->reported.max == 42);
+ OSMO_ASSERT(sitem1->reported.sum == sum1);
+
+ /* After a flush, the first item replaces the last, min and max */
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 97);
+ OSMO_ASSERT(sitem1->value.n == 1);
+ OSMO_ASSERT(sitem1->value.min == 97);
+ OSMO_ASSERT(sitem1->value.last == 97);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 97);
+ OSMO_ASSERT(sitem1->value.max == 97);
+ OSMO_ASSERT(sitem1->value.sum == 97);
+
+ /* ...and still see the previous reporting period in reported.* */
+ OSMO_ASSERT(sitem1->reported.n == 35);
+ OSMO_ASSERT(sitem1->reported.min == 1);
+ OSMO_ASSERT(sitem1->reported.last == 42);
+ OSMO_ASSERT(sitem1->reported.max == 42);
+ OSMO_ASSERT(sitem1->reported.sum == sum1);
+
+ /* If an entire reporting period elapses without a new value, the last seen value remains. */
+ osmo_stat_item_flush(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
+ osmo_stat_item_flush(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
+ OSMO_ASSERT(sitem1->value.n == 0);
+ OSMO_ASSERT(sitem1->value.min == 97);
+ OSMO_ASSERT(sitem1->value.last == 97);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 97);
+ OSMO_ASSERT(sitem1->value.max == 97);
+ OSMO_ASSERT(sitem1->value.sum == 0);
+
+ /* now the previous reporting period got turned around */
+ OSMO_ASSERT(sitem1->reported.n == 0);
+ OSMO_ASSERT(sitem1->reported.min == 97);
+ OSMO_ASSERT(sitem1->reported.last == 97);
+ OSMO_ASSERT(sitem1->reported.max == 97);
+ OSMO_ASSERT(sitem1->reported.sum == 0);
+
+ /* Another empty reporting period, everything remained the same. */
+ osmo_stat_item_flush(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
+ OSMO_ASSERT(sitem1->value.n == 0);
+ OSMO_ASSERT(sitem1->value.min == 97);
+ OSMO_ASSERT(sitem1->value.last == 97);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 97);
+ OSMO_ASSERT(sitem1->value.max == 97);
+ OSMO_ASSERT(sitem1->value.sum == 0);
+ OSMO_ASSERT(sitem1->reported.n == 0);
+ OSMO_ASSERT(sitem1->reported.min == 97);
+ OSMO_ASSERT(sitem1->reported.last == 97);
+ OSMO_ASSERT(sitem1->reported.max == 97);
+ OSMO_ASSERT(sitem1->reported.sum == 0);
+
+ /* Test Reset, place back to default value. The previously reported value remains the same. */
+ osmo_stat_item_reset(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
+ OSMO_ASSERT(sitem1->value.n == 0);
+ OSMO_ASSERT(sitem1->value.min == -1);
+ OSMO_ASSERT(sitem1->value.last == -1);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == -1);
+ OSMO_ASSERT(sitem1->value.max == -1);
+ OSMO_ASSERT(sitem1->value.sum == 0);
+ OSMO_ASSERT(sitem1->reported.n == 0);
+ OSMO_ASSERT(sitem1->reported.min == 97);
+ OSMO_ASSERT(sitem1->reported.last == 97);
+ OSMO_ASSERT(sitem1->reported.max == 97);
+ OSMO_ASSERT(sitem1->reported.sum == 0);
osmo_stat_item_group_free(statg);
@@ -246,7 +263,8 @@ static void stat_test(void)
/* define a special stats reporter for testing */
-static int send_count;
+static int sent_counter_vals;
+static int sent_stat_item_vals;
enum {
OSMO_STATS_REPORTER_TEST = OSMO_STATS_REPORTER_LOG + 1,
@@ -265,7 +283,7 @@ static int stats_reporter_test_send_counter(struct osmo_stats_reporter *srep,
group_name, ctrg ? ctrg->idx : 0,
desc->name, (long long)value, (long long)delta);
- send_count += 1;
+ sent_counter_vals++;
return 0;
}
@@ -279,7 +297,7 @@ static int stats_reporter_test_send_item(struct osmo_stats_reporter *srep,
statg->desc->group_name_prefix, statg->idx,
desc->name, value, desc->unit ? desc->unit : "");
- send_count += 1;
+ sent_stat_item_vals++;
return 0;
}
@@ -310,8 +328,19 @@ static struct osmo_stats_reporter *stats_reporter_create_test(const char *name)
return srep;
}
+static void _do_report(int expect_counter_vals, int expect_stat_item_vals, int line)
+{
+ sent_counter_vals = 0;
+ sent_stat_item_vals = 0;
+ osmo_stats_report();
+ fprintf(stderr, "reported: %d counter vals, %d stat item vals\n", sent_counter_vals, sent_stat_item_vals);
+ OSMO_ASSERT(sent_counter_vals == expect_counter_vals);
+ OSMO_ASSERT(sent_stat_item_vals == expect_stat_item_vals);
+}
+
+#define do_report(A, B) _do_report(A, B, __LINE__)
-static void test_reporting()
+static void test_reporting(void)
{
struct osmo_stats_reporter *srep1, *srep2, *srep;
struct osmo_stat_item_group *statg1, *statg2;
@@ -363,36 +392,28 @@ static void test_reporting()
OSMO_ASSERT(rc >= 0);
fprintf(stderr, "report (initial):\n");
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 20);
+ do_report(12, 8);
fprintf(stderr, "report (srep1 global):\n");
/* force single flush */
osmo_stats_reporter_set_max_class(srep1, OSMO_STATS_CLASS_GLOBAL);
srep1->force_single_flush = 1;
srep2->force_single_flush = 1;
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 10);
+ do_report(6, 4);
fprintf(stderr, "report (srep1 peer):\n");
/* force single flush */
osmo_stats_reporter_set_max_class(srep1, OSMO_STATS_CLASS_PEER);
srep1->force_single_flush = 1;
srep2->force_single_flush = 1;
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 14);
+ do_report(6, 8);
fprintf(stderr, "report (srep1 subscriber):\n");
/* force single flush */
osmo_stats_reporter_set_max_class(srep1, OSMO_STATS_CLASS_SUBSCRIBER);
srep1->force_single_flush = 1;
srep2->force_single_flush = 1;
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 20);
+ do_report(12, 8);
fprintf(stderr, "report (srep2 disabled):\n");
/* force single flush */
@@ -400,47 +421,39 @@ static void test_reporting()
srep2->force_single_flush = 1;
rc = osmo_stats_reporter_disable(srep2);
OSMO_ASSERT(rc >= 0);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 10);
+ do_report(6, 4);
fprintf(stderr, "report (srep2 enabled, no flush forced):\n");
rc = osmo_stats_reporter_enable(srep2);
OSMO_ASSERT(rc >= 0);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 10);
+ do_report(6, 4);
fprintf(stderr, "report (should be empty):\n");
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 0);
+ do_report(0, 0);
fprintf(stderr, "report (group 1, counter 1 update):\n");
- rate_ctr_inc(&ctrg1->ctr[TEST_A_CTR]);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 2);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctrg1, TEST_A_CTR));
+ do_report(2, 0);
fprintf(stderr, "report (group 1, item 1 update):\n");
- osmo_stat_item_set(statg1->items[TEST_A_ITEM], 10);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 2);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg1, TEST_A_ITEM), 10);
+ do_report(0, 2);
- fprintf(stderr, "report (group 1, item 1 update twice):\n");
- osmo_stat_item_set(statg1->items[TEST_A_ITEM], 10);
- osmo_stat_item_set(statg1->items[TEST_A_ITEM], 10);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 2);
+ fprintf(stderr, "report (group 1, item 1 update twice, with same value):\n");
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg1, TEST_A_ITEM), 10);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg1, TEST_A_ITEM), 10);
+ do_report(0, 0);
fprintf(stderr, "report (group 1, item 1 update twice, check max):\n");
- osmo_stat_item_set(statg1->items[TEST_A_ITEM], 20);
- osmo_stat_item_set(statg1->items[TEST_A_ITEM], 10);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 2);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg1, TEST_A_ITEM), 20);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg1, TEST_A_ITEM), 10);
+ do_report(0, 2);
+
+ fprintf(stderr, "report (group 1, item 1 no update, send last item (!= last max), OS#5215):\n");
+ do_report(0, 2);
+
+ fprintf(stderr, "report (group 1, item 1 no update, nothing to send):\n");
+ do_report(0, 0);
fprintf(stderr, "report (remove statg1, ctrg1):\n");
/* force single flush */
@@ -448,40 +461,30 @@ static void test_reporting()
srep2->force_single_flush = 1;
osmo_stat_item_group_free(statg1);
rate_ctr_group_free(ctrg1);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 12);
+ do_report(8, 4);
fprintf(stderr, "report (remove srep1):\n");
/* force single flush */
srep1->force_single_flush = 1;
srep2->force_single_flush = 1;
osmo_stats_reporter_free(srep1);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 6);
+ do_report(4, 2);
fprintf(stderr, "report (remove statg2):\n");
/* force single flush */
srep2->force_single_flush = 1;
osmo_stat_item_group_free(statg2);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 4);
+ do_report(4, 0);
fprintf(stderr, "report (remove srep2):\n");
/* force single flush */
srep2->force_single_flush = 1;
osmo_stats_reporter_free(srep2);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 0);
+ do_report(0, 0);
fprintf(stderr, "report (remove ctrg2, should be empty):\n");
rate_ctr_group_free(ctrg2);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 0);
+ do_report(0, 0);
rate_ctr_group_free(ctrg3);
diff --git a/tests/stats/stats_test.err b/tests/stats/stats_test.err
index 69104d9c..4acd35d0 100644
--- a/tests/stats/stats_test.err
+++ b/tests/stats/stats_test.err
@@ -1,7 +1,3 @@
-Skipping 28 values
-DLSTATS ERROR item.a: 28 stats values skipped
-Skipping 25 values
-DLSTATS ERROR item.b: 25 stats values skipped
Start test: test_reporting
DLGLOBAL ERROR counter group 'ctr-test:one' already exists for index 2, instead using index 3. This is a software bug that needs fixing.
DLGLOBAL ERROR 'ctr-test.one_dot' is not a valid counter group identifier
@@ -11,26 +7,27 @@ DLGLOBAL NOTICE counter group name mangled: 'ctr.b' -> 'ctr:b'
test1: open
test2: open
report (initial):
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
test1: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
test1: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
test1: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
test1: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
test1: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
test1: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
- test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
- test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
test1: item p= g=test.one i=1 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
+reported: 12 counter vals, 8 stat item vals
report (srep1 global):
test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
@@ -42,6 +39,7 @@ report (srep1 global):
test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
+reported: 6 counter vals, 4 stat item vals
report (srep1 peer):
test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
@@ -49,35 +47,37 @@ report (srep1 peer):
test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
- test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
- test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
test1: item p= g=test.one i=1 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
+reported: 6 counter vals, 8 stat item vals
report (srep1 subscriber):
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
test1: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
test1: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
test1: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
test1: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
test1: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
test1: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
- test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
- test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
test1: item p= g=test.one i=1 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
+reported: 12 counter vals, 8 stat item vals
report (srep2 disabled):
test2: close
test1: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
@@ -90,6 +90,7 @@ report (srep2 disabled):
test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
test1: item p= g=test.one i=1 n=item.a v=-1 u=ma
test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
+reported: 6 counter vals, 4 stat item vals
report (srep2 enabled, no flush forced):
test2: open
test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
@@ -102,32 +103,43 @@ report (srep2 enabled, no flush forced):
test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
+reported: 6 counter vals, 4 stat item vals
report (should be empty):
+reported: 0 counter vals, 0 stat item vals
report (group 1, counter 1 update):
- test2: counter p= g=ctr-test:one i=1 n=ctr:a v=1 d=1
test1: counter p= g=ctr-test:one i=1 n=ctr:a v=1 d=1
+ test2: counter p= g=ctr-test:one i=1 n=ctr:a v=1 d=1
+reported: 2 counter vals, 0 stat item vals
report (group 1, item 1 update):
- test2: item p= g=test.one i=1 n=item.a v=10 u=ma
test1: item p= g=test.one i=1 n=item.a v=10 u=ma
-report (group 1, item 1 update twice):
test2: item p= g=test.one i=1 n=item.a v=10 u=ma
- test1: item p= g=test.one i=1 n=item.a v=10 u=ma
+reported: 0 counter vals, 2 stat item vals
+report (group 1, item 1 update twice, with same value):
+reported: 0 counter vals, 0 stat item vals
report (group 1, item 1 update twice, check max):
- test2: item p= g=test.one i=1 n=item.a v=20 u=ma
test1: item p= g=test.one i=1 n=item.a v=20 u=ma
+ test2: item p= g=test.one i=1 n=item.a v=20 u=ma
+reported: 0 counter vals, 2 stat item vals
+report (group 1, item 1 no update, send last item (!= last max), OS#5215):
+ test1: item p= g=test.one i=1 n=item.a v=10 u=ma
+ test2: item p= g=test.one i=1 n=item.a v=10 u=ma
+reported: 0 counter vals, 2 stat item vals
+report (group 1, item 1 no update, nothing to send):
+reported: 0 counter vals, 0 stat item vals
report (remove statg1, ctrg1):
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
test1: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
test1: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
test1: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
test1: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
+reported: 8 counter vals, 4 stat item vals
report (remove srep1):
test1: close
test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
@@ -136,12 +148,16 @@ report (remove srep1):
test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
+reported: 4 counter vals, 2 stat item vals
report (remove statg2):
test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+reported: 4 counter vals, 0 stat item vals
report (remove srep2):
test2: close
+reported: 0 counter vals, 0 stat item vals
report (remove ctrg2, should be empty):
+reported: 0 counter vals, 0 stat item vals
End test: test_reporting
diff --git a/tests/stats/stats_vty_test.c b/tests/stats/stats_vty_test.c
new file mode 100644
index 00000000..29cbf5ec
--- /dev/null
+++ b/tests/stats/stats_vty_test.c
@@ -0,0 +1,88 @@
+/*
+ * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <signal.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/stats.h>
+#include <osmocom/vty/vty.h>
+
+static void *root_ctx = NULL;
+static int quit = 0;
+
+static void signal_handler(int signal)
+{
+ fprintf(stdout, "signal %u received\n", signal);
+
+ switch (signal) {
+ case SIGINT:
+ case SIGTERM:
+ quit++;
+ break;
+ }
+}
+
+static struct vty_app_info vty_info = {
+ .name = "stats_vty_test",
+};
+
+static const struct log_info_cat default_categories[] = { };
+
+const struct log_info log_info = {
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+};
+
+int main(int argc, char **argv)
+{
+ int rc;
+
+ root_ctx = talloc_named_const(NULL, 0, "stats_vty_test");
+
+ osmo_init_logging2(root_ctx, &log_info);
+
+ vty_info.tall_ctx = root_ctx;
+ vty_init(&vty_info);
+
+ osmo_stats_vty_add_cmds();
+
+ rc = telnet_init_default(root_ctx, NULL, 42042);
+ if (rc < 0)
+ return 2;
+
+ signal(SIGINT, &signal_handler);
+ signal(SIGTERM, &signal_handler);
+ osmo_init_ignore_signals();
+
+ while (!quit)
+ osmo_select_main(0);
+
+ talloc_free(tall_vty_ctx);
+ talloc_free(root_ctx);
+
+ return 0;
+}
diff --git a/tests/stats/stats_vty_test.vty b/tests/stats/stats_vty_test.vty
new file mode 100644
index 00000000..8732d50b
--- /dev/null
+++ b/tests/stats/stats_vty_test.vty
@@ -0,0 +1,217 @@
+stats_vty_test> en
+stats_vty_test# configure terminal
+stats_vty_test(config)# list
+...
+ stats reporter statsd [NAME]
+ no stats reporter statsd [NAME]
+ stats reporter log [NAME]
+ no stats reporter log [NAME]
+ stats interval <0-65535>
+ stats-tcp interval <0-65535>
+...
+
+stats_vty_test(config)# ### No reporters shall be configured by default
+stats_vty_test(config)# show running-config
+... !stats reporter
+
+
+stats_vty_test(config)# ### Create a statsd reporter
+stats_vty_test(config)# stats reporter statsd
+stats_vty_test(config-stats)# list
+...
+ local-ip ADDR
+ no local-ip
+ remote-ip ADDR
+ remote-port <1-65535>
+ mtu <100-65535>
+ no mtu
+ prefix PREFIX
+ no prefix
+ level (global|peer|subscriber)
+ enable
+ disable
+ flush-period <0-65535>
+...
+
+stats_vty_test(config-stats)# show running-config
+...
+stats interval 5
+stats reporter statsd
+ level global
+ no prefix
+ disable
+...
+
+stats_vty_test(config-stats)# level subscriber
+stats_vty_test(config-stats)# prefix statsd-prefix
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+ level subscriber
+ prefix statsd-prefix
+...
+
+stats_vty_test(config-stats)# remote-ip 192.168.1.200
+stats_vty_test(config-stats)# remote-port 6969
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+... !local-ip
+
+stats_vty_test(config-stats)# local-ip 192.168.1.100
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+ local-ip 192.168.1.100
+...
+
+stats_vty_test(config-stats)# no local-ip
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+... !local-ip
+
+stats_vty_test(config-stats)# mtu 1337
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+ mtu 1337
+...
+
+stats_vty_test(config-stats)# no mtu
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+... !mtu
+
+stats_vty_test(config-stats)# flush-period 43556
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+ level subscriber
+ prefix statsd-prefix
+ flush-period 43556
+...
+
+stats_vty_test(config-stats)# flush-period 0
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+... !flush-period
+
+stats_vty_test(config-stats)# enable
+stats_vty_test(config-stats)# exit
+stats_vty_test(config)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+ level subscriber
+ prefix statsd-prefix
+ enable
+...
+
+
+stats_vty_test(config)# ### Create a statsd reporter
+stats_vty_test(config)# stats reporter log
+stats_vty_test(config-stats)# level peer
+stats_vty_test(config-stats)# prefix log-prefix
+stats_vty_test(config-stats)# enable
+stats_vty_test(config-stats)# exit
+stats_vty_test(config)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+ level subscriber
+ prefix statsd-prefix
+ enable
+stats reporter log
+ level peer
+ prefix log-prefix
+ enable
+...
+
+
+stats_vty_test(config)# ### Create an additional statsd reporter
+stats_vty_test(config)# stats reporter statsd statsd-foo
+stats_vty_test(config-stats)# level global
+stats_vty_test(config-stats)# prefix statsd-one-prefix
+stats_vty_test(config-stats)# remote-ip 192.168.2.200
+stats_vty_test(config-stats)# remote-port 9696
+stats_vty_test(config-stats)# flush-period 1
+stats_vty_test(config-stats)# exit
+
+stats_vty_test(config)# ### Create an additional log reporter
+stats_vty_test(config)# stats reporter log log-bar
+stats_vty_test(config-stats)# level global
+stats_vty_test(config-stats)# prefix log-bar-prefix
+stats_vty_test(config-stats)# flush-period 2
+stats_vty_test(config-stats)# exit
+
+stats_vty_test(config)# ### Create an additional log reporter
+stats_vty_test(config)# stats reporter log log-zoo
+stats_vty_test(config-stats)# level global
+stats_vty_test(config-stats)# prefix log-zoo-prefix
+stats_vty_test(config-stats)# flush-period 3
+stats_vty_test(config-stats)# exit
+
+stats_vty_test(config)# ### We should have 5 reporters now
+stats_vty_test(config)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+ level subscriber
+ prefix statsd-prefix
+ enable
+stats reporter log
+ level peer
+ prefix log-prefix
+ enable
+stats reporter statsd statsd-foo
+ remote-ip 192.168.2.200
+ remote-port 9696
+ level global
+ prefix statsd-one-prefix
+ flush-period 1
+ disable
+stats reporter log log-bar
+ level global
+ prefix log-bar-prefix
+ flush-period 2
+ disable
+stats reporter log log-zoo
+ level global
+ prefix log-zoo-prefix
+ flush-period 3
+ disable
+...
+
+
+stats_vty_test(config)# ### Test removing reporters
+stats_vty_test(config)# no stats reporter statsd statsd-foo
+stats_vty_test(config)# no stats reporter log log-bar
+stats_vty_test(config)# no stats reporter log log-zoo
+stats_vty_test(config)# show running-config
+... !(foo|bar|zoo)
+
+stats_vty_test(config)# no stats reporter statsd statsd-foo
+% There is no such statsd reporter with name 'statsd-foo'
+stats_vty_test(config)# no stats reporter log log-zoo
+% There is no such log reporter with name 'log-zoo'
+
+
+stats_vty_test(config)# stats interval 1337
+stats_vty_test(config)# show running-config
+...
+stats interval 1337
+...
diff --git a/tests/strrb/strrb_test.c b/tests/strrb/strrb_test.c
index 4282d1aa..cdeaa346 100644
--- a/tests/strrb/strrb_test.c
+++ b/tests/strrb/strrb_test.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
diff --git a/tests/tdef/tdef_test.c b/tests/tdef/tdef_test.c
index d188eed2..710a4aa0 100644
--- a/tests/tdef/tdef_test.c
+++ b/tests/tdef/tdef_test.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
@@ -41,6 +37,9 @@ static struct osmo_tdef tdefs[] = {
{ .T=3, .default_val=50, .unit=OSMO_TDEF_M, .desc="50m" },
{ .T=4, .default_val=100, .unit=OSMO_TDEF_CUSTOM, .desc="100 potatoes" },
+ { .T=-5, .default_val=100, .unit=OSMO_TDEF_MS, .desc="X-100ms" },
+ { .T=-6, .default_val=100, .unit=OSMO_TDEF_US, .desc="X-100us" },
+
{ .T=7, .default_val=50, .desc="Water Boiling Timeout", .min_val=20, .max_val=800 }, // default is .unit=OSMO_TDEF_S == 0
{ .T=8, .default_val=300, .desc="Tea brewing" },
{ .T=9, .default_val=5, .unit=OSMO_TDEF_M, .desc="Let tea cool down before drinking" },
@@ -87,14 +86,15 @@ static struct osmo_tdef tdefs_range[] = {
printf("osmo_tdef_get(%d, %s)\t= %lu\n", T, osmo_tdef_unit_name(AS_UNIT), val); \
} while (0)
-void print_tdef_info(unsigned int T)
+void print_tdef_info(int T)
{
const struct osmo_tdef *t = osmo_tdef_get_entry(tdefs, T);
if (!t) {
- printf("T%d=NULL", T);
+ printf(OSMO_T_FMT "=NULL", OSMO_T_FMT_ARGS(T));
return;
}
- printf("T%d=%lu%s", T, t->val, osmo_tdef_unit_name(t->unit));
+ printf(OSMO_T_FMT "=%lu%s",
+ OSMO_T_FMT_ARGS(T), t->val, osmo_tdef_unit_name(t->unit));
if (t->val != t->default_val)
printf("(def=%lu)", t->default_val);
printf("\n");
@@ -110,7 +110,7 @@ static void test_tdef_get(bool test_range)
osmo_tdefs_reset(tdefs); // make all values the default
for (i = 0; i < ARRAY_SIZE(tdefs)-1; i++) {
- unsigned int T = tdefs[i].T;
+ int T = tdefs[i].T;
print_tdef_info(T);
for (as_unit = OSMO_TDEF_S; as_unit <= OSMO_TDEF_US; as_unit++) {
print_tdef_get_short(tdefs, T, as_unit);
@@ -121,7 +121,7 @@ static void test_tdef_get(bool test_range)
return;
for (i = 0; i < ARRAY_SIZE(tdefs_range)-1; i++) {
- unsigned int T = tdefs_range[i].T;
+ int T = tdefs_range[i].T;
print_tdef_info(T);
for (as_unit = OSMO_TDEF_S; as_unit <= OSMO_TDEF_US; as_unit++) {
print_tdef_get_short(tdefs_range, T, as_unit);
@@ -129,7 +129,7 @@ static void test_tdef_get(bool test_range)
}
}
-static void test_tdef_get_nonexisting()
+static void test_tdef_get_nonexisting(void)
{
printf("\n%s()\n", __func__);
@@ -140,13 +140,14 @@ static void test_tdef_get_nonexisting()
print_tdef_get(tdefs, 5, OSMO_TDEF_US);
}
-static void test_tdef_set_and_get()
+static void test_tdef_set_and_get(void)
{
struct osmo_tdef *t;
printf("\n%s()\n", __func__);
printf("setting 7 = 42\n");
t = osmo_tdef_get_entry(tdefs, 7);
+ OSMO_ASSERT(t != NULL);
OSMO_ASSERT(osmo_tdef_val_in_range(t, 42));
t->val = 42;
print_tdef_info(7);
@@ -199,6 +200,8 @@ enum test_tdef_fsm_states {
S_B,
S_C,
S_D,
+ S_E,
+ S_F,
S_G,
S_H,
S_I,
@@ -208,6 +211,7 @@ enum test_tdef_fsm_states {
S_M,
S_N,
S_O,
+ /* ... gap ... */
S_X,
S_Y,
S_Z,
@@ -219,6 +223,9 @@ static const struct osmo_tdef_state_timeout test_tdef_state_timeouts[32] = {
[S_C] = { .T = 3 },
[S_D] = { .T = 4 },
+ [S_E] = { .T = -5 },
+ [S_F] = { .T = -6 },
+
[S_G] = { .T = 7 },
[S_H] = { .T = 8 },
[S_I] = { .T = 9 },
@@ -249,31 +256,15 @@ static const struct osmo_fsm_state test_tdef_fsm_states[] = {
#define DEF_STATE(NAME) \
[S_##NAME] = { \
.name = #NAME, \
- .out_state_mask = 0 \
- | S(S_A) \
- | S(S_B) \
- | S(S_C) \
- | S(S_D) \
- | S(S_G) \
- | S(S_H) \
- | S(S_I) \
- | S(S_J) \
- | S(S_K) \
- | S(S_L) \
- | S(S_M) \
- | S(S_N) \
- | S(S_O) \
- | S(S_X) \
- | S(S_Y) \
- | S(S_Z) \
- , \
+ .out_state_mask = 0xffffffff, \
}
DEF_STATE(A),
DEF_STATE(B),
DEF_STATE(C),
DEF_STATE(D),
-
+ DEF_STATE(E),
+ DEF_STATE(F),
DEF_STATE(G),
DEF_STATE(H),
DEF_STATE(I),
@@ -320,7 +311,7 @@ const struct timeval fake_time_start_time = { 123, 456 };
osmo_timers_update(); \
} while (0)
-void fake_time_start()
+void fake_time_start(void)
{
struct timespec *clock_override;
@@ -357,9 +348,9 @@ static void print_fsm_state(struct osmo_fsm_inst *fi)
osmo_fsm_state_name(&test_tdef_fsm, NEXT_STATE), rc); \
} else { \
struct osmo_tdef *t = osmo_tdef_get_entry(tdefs, st->T); \
- printf(" --> %s (configured as T%d%s %lu %s) rc=%d;\t", \
+ printf(" --> %s (configured as " OSMO_T_FMT "%s %lu %s) rc=%d;\t", \
osmo_fsm_state_name(&test_tdef_fsm, NEXT_STATE), \
- st->T, st->keep_timer ? "(keep_timer)" : "", \
+ OSMO_T_FMT_ARGS(st->T), st->keep_timer ? " (keep_timer)" : "", \
t? t->val : 0, t? osmo_tdef_unit_name(t->unit) : "-", \
rc); \
} \
@@ -386,7 +377,8 @@ static void test_tdef_state_timeout(bool test_range)
test_tdef_fsm_state_chg(tdefs, S_B);
test_tdef_fsm_state_chg(tdefs, S_C);
test_tdef_fsm_state_chg(tdefs, S_D);
-
+ test_tdef_fsm_state_chg(tdefs, S_E);
+ test_tdef_fsm_state_chg(tdefs, S_F);
test_tdef_fsm_state_chg(tdefs, S_G);
test_tdef_fsm_state_chg(tdefs, S_H);
test_tdef_fsm_state_chg(tdefs, S_I);
@@ -478,9 +470,16 @@ int main(int argc, char **argv)
osmo_init_logging2(ctx, NULL);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_level(osmo_stderr_target, 1);
log_set_print_category(osmo_stderr_target, 1);
+ log_set_print_category_hex(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
+ osmo_fsm_log_addr(false);
+ osmo_fsm_log_timeouts(true);
+
+ log_set_category_filter(osmo_stderr_target, DLGLOBAL, 1, LOGL_DEBUG);
+
OSMO_ASSERT(osmo_fsm_register(&test_tdef_fsm) == 0);
test_tdef_get(argc > 1);
diff --git a/tests/tdef/tdef_test.err b/tests/tdef/tdef_test.err
new file mode 100644
index 00000000..8e5860d7
--- /dev/null
+++ b/tests/tdef/tdef_test.err
@@ -0,0 +1,25 @@
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){A}: Allocated
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){A}: State change to A (T1, 100s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){A}: State change to B (T2, 100ms)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){B}: State change to C (T3, 3000s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){C}: State change to D (T4, 100s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){D}: State change to E (X5, 100ms)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){E}: State change to F (X6, 1ms)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){F}: State change to G (T7, 50s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){G}: State change to H (T8, 300s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){H}: State change to I (T9, 300s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){I}: State change to J (T10, 1200s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){J}: State change to K (keeping T10, 1076.954s remaining)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){K}: State change to A (T1, 100s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){A}: State change to K (keeping T1, 76.954s remaining)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){K}: State change to A (T1, 100s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){A}: State change to L (keeping T1, 76.954s remaining)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){L}: State change to O (no timeout)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){O}: State change to L (T123, 1s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){L}: State change to O (no timeout)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){O}: State change to X (no timeout)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){X}: State change to Y (T666, 999s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){Y}: State change to Z (no timeout)
+DLGLOBAL ERROR tdef_test(test_tdef_state_timeout){Z}: transition to state B not permitted!
+DLGLOBAL ERROR tdef_test(test_tdef_state_timeout){Z}: transition to state C not permitted!
+DLGLOBAL ERROR tdef_test(test_tdef_state_timeout){Z}: transition to state D not permitted!
diff --git a/tests/tdef/tdef_test.ok b/tests/tdef/tdef_test.ok
index 4c97dabb..6ceaffcc 100644
--- a/tests/tdef/tdef_test.ok
+++ b/tests/tdef/tdef_test.ok
@@ -24,6 +24,18 @@ osmo_tdef_get(4, ms) = 100
osmo_tdef_get(4, m) = 100
osmo_tdef_get(4, custom-unit) = 100
osmo_tdef_get(4, us) = 100
+X5=100ms
+osmo_tdef_get(-5, s) = 1
+osmo_tdef_get(-5, ms) = 100
+osmo_tdef_get(-5, m) = 1
+osmo_tdef_get(-5, custom-unit) = 100
+osmo_tdef_get(-5, us) = 100000
+X6=100us
+osmo_tdef_get(-6, s) = 1
+osmo_tdef_get(-6, ms) = 1
+osmo_tdef_get(-6, m) = 1
+osmo_tdef_get(-6, custom-unit) = 100
+osmo_tdef_get(-6, us) = 100
T7=50s
osmo_tdef_get(7, s) = 50
osmo_tdef_get(7, ms) = 50000
@@ -153,9 +165,11 @@ osmo_tdef_get(7, s) = 50
test_tdef_state_timeout()
state=A T=0, no timeout
--> A (configured as T1 100 s) rc=0; state=A T=1, 100.000000 s remaining
- --> B (configured as T2 100 ms) rc=0; state=B T=2, 1.000000 s remaining
+ --> B (configured as T2 100 ms) rc=0; state=B T=2, 0.100000 s remaining
--> C (configured as T3 50 m) rc=0; state=C T=3, 3000.000000 s remaining
--> D (configured as T4 100 custom-unit) rc=0; state=D T=4, 100.000000 s remaining
+ --> E (configured as X5 100 ms) rc=0; state=E T=-5, 0.100000 s remaining
+ --> F (configured as X6 100 us) rc=0; state=F T=-6, 0.001000 s remaining
--> G (configured as T7 50 s) rc=0; state=G T=7, 50.000000 s remaining
--> H (configured as T8 300 s) rc=0; state=H T=8, 300.000000 s remaining
--> I (configured as T9 5 m) rc=0; state=I T=9, 300.000000 s remaining
@@ -163,17 +177,17 @@ state=A T=0, no timeout
- test keep_timer:
Time passes: 123.045678 s
state=J T=10, 1076.954322 s remaining
- --> K (configured as T0(keep_timer) 1 custom-unit) rc=0; state=K T=10, 1076.954322 s remaining
+ --> K (configured as T0 (keep_timer) 1 custom-unit) rc=0; state=K T=10, 1076.954322 s remaining
--> A (configured as T1 100 s) rc=0; state=A T=1, 100.000000 s remaining
Time passes: 23.045678 s
state=A T=1, 76.954322 s remaining
- --> K (configured as T0(keep_timer) 1 custom-unit) rc=0; state=K T=1, 76.954322 s remaining
+ --> K (configured as T0 (keep_timer) 1 custom-unit) rc=0; state=K T=1, 76.954322 s remaining
--> A (configured as T1 100 s) rc=0; state=A T=1, 100.000000 s remaining
Time passes: 23.045678 s
state=A T=1, 76.954322 s remaining
- --> L (configured as T123(keep_timer) 1 s) rc=0; state=L T=1, 76.954322 s remaining
+ --> L (configured as T123 (keep_timer) 1 s) rc=0; state=L T=1, 76.954322 s remaining
--> O (no timer configured for this state) rc=0; state=O T=0, no timeout
- --> L (configured as T123(keep_timer) 1 s) rc=0; state=L T=123, 1.000000 s remaining
+ --> L (configured as T123 (keep_timer) 1 s) rc=0; state=L T=123, 1.000000 s remaining
- test T=0:
--> O (no timer configured for this state) rc=0; state=O T=0, no timeout
- test no timer:
diff --git a/tests/tdef/tdef_vty_test_config_root.c b/tests/tdef/tdef_vty_config_root_test.c
index 92113e87..8c46d958 100644
--- a/tests/tdef/tdef_vty_test_config_root.c
+++ b/tests/tdef/tdef_vty_config_root_test.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
@@ -45,7 +41,7 @@
/* ------------------- HERE IS THE INTERESTING TDEF RELEVANT PART ------------------- */
/* This example keeps several separate timer groups and offers 'timer' VTY commands at the root of the config node. See
- * the tdef_vty_test_config_root.vty transcript test.
+ * the tdef_vty_config_root_test.vty transcript test.
*/
static struct osmo_tdef tdefs_test[] = {
@@ -102,7 +98,7 @@ enum tdef_vty_test_nodes {
/* This example puts 'timer' configuration commands directly at the root of the CONFIG_NODE.
* This TIMER_NODE is merely needed as a hook for the vty_write() command, but becomes an empty node in the VTY docs.
* It is possible to cheat around needing this if you choose to config_write_timer() in another root nodes' write cb.
- * Another example using a 'network' subnode is \ref tdef_vty_test_config_subnode.c */
+ * Another example using a 'network' subnode is \ref tdef_vty_config_subnode_test.c */
static struct cmd_node timer_node = {
TIMER_NODE,
"%s(config-timer)# ",
@@ -115,7 +111,7 @@ static int config_write_timer(struct vty *vty)
return CMD_SUCCESS;
}
-static void timer_init_vty()
+static void timer_init_vty(void)
{
/* Again, this is merely to get a vty write hook, see above. */
install_node(&timer_node, config_write_timer);
@@ -127,7 +123,7 @@ static void timer_init_vty()
void *root_ctx = NULL;
-static void print_help()
+static void print_help(void)
{
printf( "options:\n"
" -h --help this text\n"
@@ -264,7 +260,7 @@ int main(int argc, char **argv)
}
}
- rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);
+ rc = telnet_init_default(root_ctx, NULL, 42042);
if (rc < 0)
return 2;
diff --git a/tests/tdef/tdef_vty_test_config_root.vty b/tests/tdef/tdef_vty_config_root_test.vty
index 6a53b805..6a53b805 100644
--- a/tests/tdef/tdef_vty_test_config_root.vty
+++ b/tests/tdef/tdef_vty_config_root_test.vty
diff --git a/tests/tdef/tdef_vty_test_config_subnode.c b/tests/tdef/tdef_vty_config_subnode_test.c
index ce851f50..e3e165da 100644
--- a/tests/tdef/tdef_vty_test_config_subnode.c
+++ b/tests/tdef/tdef_vty_config_subnode_test.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
@@ -46,7 +42,7 @@
/* This example keeps a single global timer group and offers a custom 'timer' VTY command in a 'network' subnode below
* the CONFIG_NODE.
- * the tdef_vty_test_config_subnode.vty transcript test.
+ * the tdef_vty_config_subnode_test.vty transcript test.
*/
static struct osmo_tdef global_tdefs[] = {
@@ -106,7 +102,7 @@ static int config_write_gsmnet(struct vty *vty)
return CMD_SUCCESS;
}
-static void gsmnet_init_vty()
+static void gsmnet_init_vty(void)
{
install_node(&gsmnet_node, config_write_gsmnet);
install_element(CONFIG_NODE, &cfg_net_cmd);
@@ -120,7 +116,7 @@ static void gsmnet_init_vty()
void *root_ctx = NULL;
-static void print_help()
+static void print_help(void)
{
printf( "options:\n"
" -h --help this text\n"
@@ -257,7 +253,7 @@ int main(int argc, char **argv)
}
}
- rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);
+ rc = telnet_init_default(root_ctx, NULL, 42042);
if (rc < 0)
return 2;
diff --git a/tests/tdef/tdef_vty_test_config_subnode.vty b/tests/tdef/tdef_vty_config_subnode_test.vty
index 2605f71d..2605f71d 100644
--- a/tests/tdef/tdef_vty_test_config_subnode.vty
+++ b/tests/tdef/tdef_vty_config_subnode_test.vty
diff --git a/tests/tdef/tdef_vty_test_dynamic.c b/tests/tdef/tdef_vty_dynamic_test.c
index 20dae535..b646c54e 100644
--- a/tests/tdef/tdef_vty_test_dynamic.c
+++ b/tests/tdef/tdef_vty_dynamic_test.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
@@ -183,7 +179,7 @@ static int config_write_member(struct vty *vty)
return CMD_SUCCESS;
}
-static void member_init_vty()
+static void member_init_vty(void)
{
install_node(&member_node, config_write_member);
install_element(CONFIG_NODE, &cfg_member_cmd);
@@ -194,7 +190,7 @@ static void member_init_vty()
/* ------------------- THE REST is just boilerplate osmo main() ------------------- */
-static void print_help()
+static void print_help(void)
{
printf( "options:\n"
" -h --help this text\n"
@@ -331,7 +327,7 @@ int main(int argc, char **argv)
}
}
- rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);
+ rc = telnet_init_default(root_ctx, NULL, 42042);
if (rc < 0)
return 2;
diff --git a/tests/tdef/tdef_vty_test_dynamic.vty b/tests/tdef/tdef_vty_dynamic_test.vty
index 6aae746b..6aae746b 100644
--- a/tests/tdef/tdef_vty_test_dynamic.vty
+++ b/tests/tdef/tdef_vty_dynamic_test.vty
diff --git a/tests/testsuite.at b/tests/testsuite.at
index cb842294..9ff64acb 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -36,7 +36,7 @@ AT_CLEANUP
AT_SETUP([bitvec])
AT_KEYWORDS([bitvec])
cat $abs_srcdir/bitvec/bitvec_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/bitvec/bitvec_test], [0], [expout], [ignore])
+AT_CHECK([$abs_top_builddir/tests/bitvec/bitvec_test], [0], [expout])
AT_CLEANUP
AT_SETUP([bitcomp])
@@ -102,6 +102,18 @@ cat $abs_srcdir/smscb/smscb_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/smscb/smscb_test], [0], [expout])
AT_CLEANUP
+AT_SETUP([smscb_gsm0341])
+AT_KEYWORDS([smscb_gsm0341])
+cat $abs_srcdir/smscb/gsm0341_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/smscb/gsm0341_test], [0], [expout])
+AT_CLEANUP
+
+AT_SETUP([smscb_cbsp])
+AT_KEYWORDS([smscb_cbsp])
+cat $abs_srcdir/smscb/cbsp_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/smscb/cbsp_test], [0], [expout])
+AT_CLEANUP
+
AT_SETUP([ussd])
AT_KEYWORDS([ussd])
cat $abs_srcdir/ussd/ussd_test.ok > expout
@@ -114,6 +126,12 @@ cat $abs_srcdir/auth/milenage_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/auth/milenage_test], [0], [expout], [ignore])
AT_CLEANUP
+AT_SETUP([auth_xor2g])
+AT_KEYWORDS([auth_xor2g])
+cat $abs_srcdir/auth/xor2g_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/auth/xor2g_test], [0], [expout], [ignore])
+AT_CLEANUP
+
AT_SETUP([comp128])
AT_KEYWORDS([comp128])
cat $abs_srcdir/comp128/comp128_test.ok > expout
@@ -153,7 +171,8 @@ AT_CLEANUP
AT_SETUP([gsm0408])
AT_KEYWORDS([gsm0408])
cat $abs_srcdir/gsm0408/gsm0408_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/gsm0408/gsm0408_test], [0], [expout], [ignore])
+cat $abs_srcdir/gsm0408/gsm0408_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/gsm0408/gsm0408_test], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([gsm48_rest_octets])
@@ -168,11 +187,24 @@ cat $abs_srcdir/gprs/gprs_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/gprs/gprs_test], [0], [expout], [ignore])
AT_CLEANUP
-AT_SETUP([logging])
-AT_KEYWORDS([logging])
+AT_SETUP([logging_stream])
+AT_KEYWORDS([logging_stream])
+cat $abs_srcdir/logging/logging_test.ok > expout
+cat $abs_srcdir/logging/logging_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/logging/logging_test stream], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([logging_wqueue])
+AT_KEYWORDS([logging_wqueue])
cat $abs_srcdir/logging/logging_test.ok > expout
cat $abs_srcdir/logging/logging_test.err > experr
-AT_CHECK([$abs_top_builddir/tests/logging/logging_test], [0], [expout], [experr])
+AT_CHECK([$abs_top_builddir/tests/logging/logging_test wqueue], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([logging_gsmtap])
+AT_KEYWORDS([logging_gsmtap])
+cat $abs_srcdir/logging/logging_test_gsmtap.err > experr
+AT_CHECK([$abs_top_builddir/tests/logging/logging_test_gsmtap 3>&1 1>&2 2>&3 |grep -v "enqueueing message failed" 3>&1 1>&2 2>&3 ], [], [ignore], [experr])
AT_CLEANUP
AT_SETUP([codec])
@@ -230,13 +262,15 @@ AT_CLEANUP
AT_SETUP([gprs-ns])
AT_KEYWORDS([gprs-ns])
cat $abs_srcdir/gb/gprs_ns_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/gb/gprs_ns_test], [0], [expout], [ignore])
+cat $abs_srcdir/gb/gprs_ns_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/gb/gprs_ns_test], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([gprs-ns2])
AT_KEYWORDS([gprs-ns2])
cat $abs_srcdir/gb/gprs_ns2_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/gb/gprs_ns2_test], [0], [expout], [ignore])
+cat $abs_srcdir/gb/gprs_ns2_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/gb/gprs_ns2_test], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([utils])
@@ -329,7 +363,6 @@ AT_SETUP([socket])
AT_KEYWORDS([socket])
cat $abs_srcdir/socket/socket_test.ok > expout
cat $abs_srcdir/socket/socket_test.err > experr
-touch experr
AT_CHECK([$abs_top_builddir/tests/socket/socket_test], [0], [expout], [experr])
AT_CLEANUP
@@ -338,7 +371,6 @@ AT_KEYWORDS([socket_sctp])
AT_SKIP_IF([! test -e $abs_top_builddir/tests/socket/socket_sctp_test])
cat $abs_srcdir/socket/socket_sctp_test.ok > expout
cat $abs_srcdir/socket/socket_sctp_test.err > experr
-touch experr
AT_CHECK([$abs_top_builddir/tests/socket/socket_sctp_test], [0], [expout], [experr])
AT_CLEANUP
@@ -382,7 +414,8 @@ AT_CLEANUP
AT_SETUP([tdef])
AT_KEYWORDS([tdef])
cat $abs_srcdir/tdef/tdef_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/tdef/tdef_test], [0], [expout], [ignore])
+cat $abs_srcdir/tdef/tdef_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/tdef/tdef_test], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([sockaddr_str])
@@ -446,3 +479,73 @@ AT_KEYWORDS([it_q])
cat $abs_srcdir/it_q/it_q_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/it_q/it_q_test], [0], [expout], [ignore])
AT_CLEANUP
+
+AT_SETUP([base64])
+AT_KEYWORDS([base64])
+cat $abs_srcdir/base64/base64_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/base64/base64_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([time_cc])
+AT_KEYWORDS([time_cc])
+cat $abs_srcdir/time_cc/time_cc_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/time_cc/time_cc_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([iuup])
+AT_KEYWORDS([iuup])
+cat $abs_srcdir/iuup/iuup_test.ok > expout
+cat $abs_srcdir/iuup/iuup_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/iuup/iuup_test], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([v110_frame_test])
+AT_KEYWORDS([v110_frame_test])
+cat $abs_srcdir/v110/frame_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/v110/frame_test], [], [expout],[])
+AT_CLEANUP
+
+AT_SETUP([v110_ra1_test])
+AT_KEYWORDS([v110_ra1_test])
+cat $abs_srcdir/v110/ra1_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/v110/ra1_test], [], [expout],[])
+AT_CLEANUP
+
+AT_SETUP([v110_ta_test])
+AT_KEYWORDS([v110_ta_test])
+cat $abs_srcdir/v110/ta_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/v110/ta_test], [], [], [experr])
+AT_CLEANUP
+
+AT_SETUP([gsm44021_frame_csd_test])
+AT_KEYWORDS([gsm44021_frame_csd_test])
+cat $abs_srcdir/gsm44021/frame_csd_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/gsm44021/frame_csd_test], [], [expout],[])
+AT_CLEANUP
+
+AT_SETUP([osmo_io])
+AT_KEYWORDS([osmo_io])
+cat $abs_srcdir/osmo_io/osmo_io_test.ok > expout
+cat $abs_srcdir/osmo_io/osmo_io_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/osmo_io/osmo_io_test], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([osmo_io (uring)])
+AT_KEYWORDS([osmo_io (uring)])
+AT_SKIP_IF([ test "$ENABLE_URING" != "yes" || test "$ENABLE_URING_TESTS" != "yes" ])
+cat $abs_srcdir/osmo_io/osmo_io_test.ok > expout
+cat $abs_srcdir/osmo_io/osmo_io_test.err > experr
+AT_CHECK([LIBOSMO_IO_BACKEND=IO_URING $abs_top_builddir/tests/osmo_io/osmo_io_test], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([soft_uart])
+AT_KEYWORDS([soft_uart])
+cat $abs_srcdir/soft_uart/soft_uart_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/soft_uart/soft_uart_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([rlp])
+AT_KEYWORDS([rlp])
+cat $abs_srcdir/rlp/rlp_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/rlp/rlp_test], [0], [expout], [ignore])
+AT_CLEANUP
diff --git a/tests/time_cc/time_cc_test.c b/tests/time_cc/time_cc_test.c
new file mode 100644
index 00000000..e4a5aaf3
--- /dev/null
+++ b/tests/time_cc/time_cc_test.c
@@ -0,0 +1,768 @@
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * 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 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 <inttypes.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/time_cc.h>
+
+enum my_ctrs {
+ CTR_CEIL,
+ CTR_ROUND,
+ CTR_FLOOR,
+};
+
+const struct rate_ctr_desc my_ctr_desc[] = {
+ [CTR_CEIL] = {"ceil", "testing round_threshold_usec = 1"},
+ [CTR_ROUND] = {"round", "testing round_threshold_usec = 0 = gran_usec/2"},
+ [CTR_FLOOR] = {"floor", "testing round_threshold_usec = gran_usec"},
+};
+
+const struct rate_ctr_group_desc my_ctrg_desc = {
+ "time_cc_test",
+ "Counters for osmo_time_cc test",
+ 0,
+ ARRAY_SIZE(my_ctr_desc),
+ my_ctr_desc,
+};
+
+struct rate_ctr_group *my_ctrg;
+
+
+enum my_obj_timers {
+ T_GRAN = -23,
+ T_ROUND_THRESH = -24,
+ T_FORGET_SUM = -25,
+};
+
+struct osmo_tdef g_my_obj_tdefs[] = {
+ { .T = T_GRAN, .default_val = 0, .unit = OSMO_TDEF_MS, .desc = "flag_cc granularity, or zero for 1 second" },
+ { .T = T_ROUND_THRESH, .default_val = 0, .unit = OSMO_TDEF_MS,
+ .desc = "flag_cc rounding threshold, or zero for half a granularity" },
+ { .T = T_FORGET_SUM, .default_val = 0, .unit = OSMO_TDEF_MS,
+ .desc = "flag_cc inactivity forget period, or zero to not forget any timings" },
+ {}
+};
+
+
+struct my_obj {
+ struct osmo_time_cc flag_cc_ceil;
+ struct osmo_time_cc flag_cc_round;
+ struct osmo_time_cc flag_cc_floor;
+};
+
+void my_obj_init(struct my_obj *my_obj)
+{
+ osmo_time_cc_init(&my_obj->flag_cc_ceil);
+ my_obj->flag_cc_ceil.cfg = (struct osmo_time_cc_cfg){
+ .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, CTR_CEIL),
+ .round_threshold_usec = 1,
+ .T_gran = T_GRAN,
+ .T_forget_sum = T_FORGET_SUM,
+ .T_defs = g_my_obj_tdefs,
+ };
+
+ osmo_time_cc_init(&my_obj->flag_cc_round);
+ my_obj->flag_cc_round.cfg = (struct osmo_time_cc_cfg){
+ .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, CTR_ROUND),
+ .T_gran = T_GRAN,
+ .T_round_threshold = T_ROUND_THRESH,
+ .T_forget_sum = T_FORGET_SUM,
+ .T_defs = g_my_obj_tdefs,
+ };
+
+ osmo_time_cc_init(&my_obj->flag_cc_floor);
+ my_obj->flag_cc_floor.cfg = (struct osmo_time_cc_cfg){
+ .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, CTR_FLOOR),
+ .round_threshold_usec = UINT64_MAX, /* always >= gran_usec */
+ .T_gran = T_GRAN,
+ .T_forget_sum = T_FORGET_SUM,
+ .T_defs = g_my_obj_tdefs,
+ };
+}
+
+void my_obj_event(struct my_obj *my_obj, bool flag)
+{
+ osmo_time_cc_set_flag(&my_obj->flag_cc_ceil, flag);
+ osmo_time_cc_set_flag(&my_obj->flag_cc_round, flag);
+ osmo_time_cc_set_flag(&my_obj->flag_cc_floor, flag);
+}
+
+void my_obj_destruct(struct my_obj *my_obj)
+{
+ osmo_time_cc_cleanup(&my_obj->flag_cc_ceil);
+ osmo_time_cc_cleanup(&my_obj->flag_cc_round);
+ osmo_time_cc_cleanup(&my_obj->flag_cc_floor);
+}
+
+static const struct log_info_cat log_categories[] = {
+};
+
+static const struct log_info log_info = {
+ .cat = log_categories,
+ .num_cat = ARRAY_SIZE(log_categories),
+};
+
+int main(int argc, char **argv)
+{
+ void *ctx = talloc_named_const(NULL, 0, "time_cc_test");
+ struct timespec *now;
+ struct my_obj my_obj = {0};
+
+ osmo_init_logging2(ctx, &log_info);
+
+ /* enable override for CLOCK_MONOTONIC */
+ osmo_clock_override_enable(CLOCK_MONOTONIC, true);
+ now = osmo_clock_override_gettimespec(CLOCK_MONOTONIC);
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+
+ /* enable override for osmo_gettimeofday(), for osmo_timer_schedule() */
+ osmo_gettimeofday_override = true;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ my_ctrg = rate_ctr_group_alloc(ctx, &my_ctrg_desc, 0);
+
+#define CHECK_RATE_CTRS(exp_ceil, exp_round, exp_floor) do { \
+ printf("%d CHECK_RATE_CTRS(" #exp_ceil ", " #exp_round ", " #exp_floor ")", \
+ my_obj.flag_cc_round.flag_state); \
+ while (osmo_select_main_ctx(1) > 0); \
+ if (exp_ceil != my_obj.flag_cc_ceil.cfg.rate_ctr->current \
+ || exp_round != my_obj.flag_cc_round.cfg.rate_ctr->current \
+ || exp_floor != my_obj.flag_cc_floor.cfg.rate_ctr->current) \
+ printf("\n ERROR on line %d: ctr_ceil=%"PRIu64" ctr_round=%"PRIu64" ctr_floor=%"PRIu64"\n", \
+ __LINE__, \
+ my_obj.flag_cc_ceil.cfg.rate_ctr->current, \
+ my_obj.flag_cc_round.cfg.rate_ctr->current, \
+ my_obj.flag_cc_floor.cfg.rate_ctr->current); \
+ else \
+ printf(" ok\n"); \
+ } while (0)
+
+#define ADD_MILLISECS_NO_SELECT(ms) do { \
+ osmo_clock_override_add(CLOCK_MONOTONIC, ms / 1000, (uint64_t)(ms % 1000) * 1000000); \
+ osmo_gettimeofday_override_add(ms / 1000, (uint64_t)(ms % 1000) * 1000); \
+ printf("%d ADD_MILLISECS(" #ms ") --> %ld.%03ld", my_obj.flag_cc_round.flag_state, \
+ now->tv_sec, now->tv_nsec/1000000); \
+ printf("\n"); \
+ } while (0)
+
+#define ADD_MILLISECS(ms) do { \
+ ADD_MILLISECS_NO_SELECT(ms); \
+ while (osmo_select_main_ctx(1) > 0); \
+ } while (0)
+
+#define FLAG(VAL) do { \
+ printf(" flag: %s -> %s\n", my_obj.flag_cc_round.flag_state ? "TRUE" : "FALSE", VAL ? "TRUE" : "FALSE"); \
+ my_obj_event(&my_obj, VAL); \
+ } while (0)
+
+ /*
+ * sum ^
+ * | ________
+ * | /
+ * | /
+ * | /
+ * 3*gran --+--------------------------------------+
+ * | /:
+ * | / :
+ * | - - - - - - - - - - - - - - - - - / :
+ * | /. :
+ * | / . :
+ * 2*gran --+--------------------------------+ . :
+ * | /: . :
+ * | / : . :
+ * | - - - - - - - - - -_________/ : . :
+ * | / . : . :
+ * | / . : . :
+ * 1*gran --+-----------------+ . : . :
+ * | /: . : . :
+ * | / : . : . :
+ * | - - - - - - -/ : . : . :
+ * | /. : . : . :
+ * | ....-------' . : . : . :
+ * 0 +----------------------------------------------------------> elapsed time
+ * . : . : . :
+ * _ _ _______ ____________
+ * flag: __| |_| |____| . : |_______|. : . : |__________
+ * f t f t f t . : f t. : . : f
+ * round_threshold_usec : . : . : . :
+ * = 1 usec: 0 1 . :2 . :3 . :4 = "ceil()"
+ * = 0 == gran_usec/2: 0 1 : 2 : 3 : = "round()"
+ * = gran_usec: 0 1 2 3 = "floor()"
+ */
+
+ printf("\n----------- cumulating time, without forget_sum\n\n");
+
+ my_obj_init(&my_obj);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ ADD_MILLISECS(100);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS(1);
+ /* flag has been true for 0.001s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(99);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(100);
+
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(100);
+ /* flag has been true for 0.2s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(300);
+
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(299);
+ /* flag has been true for 0.499s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(1);
+ /* flag has been true for 0.5s */
+ CHECK_RATE_CTRS(1, 1, 0);
+ ADD_MILLISECS(499);
+ /* flag has been true for 0.999s */
+ CHECK_RATE_CTRS(1, 1, 0);
+ ADD_MILLISECS(1);
+ /* flag has been true for 1.0s */
+ CHECK_RATE_CTRS(1, 1, 1);
+ ADD_MILLISECS(1);
+ /* flag has been true for 1.001s */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(299);
+ /* flag has been true for 1.3s */
+ CHECK_RATE_CTRS(2, 1, 1);
+ FLAG(false);
+ CHECK_RATE_CTRS(2, 1, 1);
+
+ ADD_MILLISECS(400);
+
+ CHECK_RATE_CTRS(2, 1, 1);
+ FLAG(true);
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(199);
+ /* flag has been true for 1.499s */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(2);
+ /* flag has been true for 1.501s */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS(498);
+ /* flag has been true for 1.999s */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS(2);
+ /* flag has been true for 2.001s */
+ CHECK_RATE_CTRS(3, 2, 2);
+ ADD_MILLISECS(500);
+ /* flag has been true for 2.501s */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS(498);
+ /* flag has been true for 2.999s */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS(3);
+ /* flag has been true for 3.003s */
+ CHECK_RATE_CTRS(4, 3, 3);
+ ADD_MILLISECS(200);
+ /* flag has been true for 3.203s */
+ CHECK_RATE_CTRS(4, 3, 3);
+ FLAG(false);
+ CHECK_RATE_CTRS(4, 3, 3);
+
+ ADD_MILLISECS(4321);
+ CHECK_RATE_CTRS(4, 3, 3);
+
+ FLAG(true);
+ CHECK_RATE_CTRS(4, 3, 3);
+ ADD_MILLISECS(5678);
+ CHECK_RATE_CTRS(9, 9, 8);
+ FLAG(false);
+ CHECK_RATE_CTRS(9, 9, 8);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+ printf("\n----------- test forget_sum_usec\n\n");
+ osmo_tdef_set(g_my_obj_tdefs, T_FORGET_SUM, 10, OSMO_TDEF_S);
+
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ my_obj_init(&my_obj);
+
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS(100);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(1000);
+ /* 1 s of being false, forget_sum_usec has not yet occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(8999);
+ /* 9.999 s of being false, forget_sum_usec has not yet occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(1);
+ /* 10 s of being false, forget_sum_usec has occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(1);
+ /* Since previous sums were forgotton, ceil() triggers again */
+ CHECK_RATE_CTRS(2, 0, 0);
+ /* If the sum had not been forgotten, adding 400 ms to the initial 100 ms would have triggered round(). Verify
+ * that this does not occur, since now full 500 ms are required */
+ ADD_MILLISECS(399);
+ CHECK_RATE_CTRS(2, 0, 0);
+ /* Adding another 100 ms will trigger round() */
+ ADD_MILLISECS(99);
+ CHECK_RATE_CTRS(2, 0, 0);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(2, 1, 0);
+ /* If the sum had not been forgotten, adding 900 ms to the initial 100 ms would have triggered floor(). Verify
+ * that this does not occur, since now full 1000 ms are required. We already added 500 ms above. */
+ ADD_MILLISECS(400);
+ CHECK_RATE_CTRS(2, 1, 0);
+ /* Adding another 100 ms will trigger floor() */
+ ADD_MILLISECS(99);
+ CHECK_RATE_CTRS(2, 1, 0);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(2, 1, 1);
+
+ /* Test that durations of false below forget_sum_usec never trigger a forget */
+ ADD_MILLISECS(300);
+ CHECK_RATE_CTRS(3, 1, 1);
+ /* internal counter is now at 0.3s above the last reported rate counter */
+ FLAG(false);
+ ADD_MILLISECS(9999);
+ FLAG(true);
+ ADD_MILLISECS(25);
+ FLAG(false);
+ ADD_MILLISECS(9999);
+ FLAG(true);
+ ADD_MILLISECS(25);
+ FLAG(false);
+ ADD_MILLISECS(9999);
+ FLAG(true);
+ ADD_MILLISECS(25);
+ FLAG(false);
+ ADD_MILLISECS(9999);
+ FLAG(true);
+ ADD_MILLISECS(25);
+ /* internal counter is now at 0.4s above the last reported rate counter */
+ CHECK_RATE_CTRS(3, 1, 1);
+ ADD_MILLISECS(100);
+ CHECK_RATE_CTRS(3, 2, 1);
+ ADD_MILLISECS(500);
+ CHECK_RATE_CTRS(3, 2, 2);
+
+ /* Test that repeated osmo_time_cc_set_flag(false) does not cancel a forget_sum_usec */
+ ADD_MILLISECS(300);
+ /* internal counter is now at 0.3s above the last reported rate counter */
+ CHECK_RATE_CTRS(4, 2, 2);
+ FLAG(false);
+ ADD_MILLISECS(5000);
+ /* Repeat 'false', must not affect forget_sum_usec */
+ FLAG(false);
+ ADD_MILLISECS(5000);
+ CHECK_RATE_CTRS(4, 2, 2);
+ /* 10 s have passed, forget_sum_usec has occurred.
+ * Hence ceil() will trigger again right away: */
+ FLAG(true);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(5, 2, 2);
+ /* Adding 200 ms to the initial 300 ms would have triggered round(), but no more after forget_sum_usec */
+ ADD_MILLISECS(199);
+ CHECK_RATE_CTRS(5, 2, 2);
+ /* Adding another 300 ms will trigger round() */
+ ADD_MILLISECS(299);
+ CHECK_RATE_CTRS(5, 2, 2);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(5, 3, 2);
+ /* Adding 700 ms to the initial 300 ms would have triggered ceil(), but no more after forget_sum_usec */
+ ADD_MILLISECS(200);
+ CHECK_RATE_CTRS(5, 3, 2);
+ /* Adding another 300 ms will trigger ceil() */
+ ADD_MILLISECS(299);
+ CHECK_RATE_CTRS(5, 3, 2);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(5, 3, 3);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+
+ /* Verify correctness when select() lags and runs timer callbacks too late */
+ printf("\n----------- cumulating time, without forget_sum, when timer cb are invoked late\n\n");
+ osmo_tdef_set(g_my_obj_tdefs, T_FORGET_SUM, 0, OSMO_TDEF_S);
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ my_obj_init(&my_obj);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(100);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS_NO_SELECT(100);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(100);
+
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS_NO_SELECT(100);
+ /* flag has been true for 0.2s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(300);
+
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS_NO_SELECT(799);
+ /* flag has been true for 0.999s */
+ CHECK_RATE_CTRS(1, 1, 0);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* flag has been true for 1.0s */
+ CHECK_RATE_CTRS(1, 1, 1);
+ ADD_MILLISECS_NO_SELECT(300);
+ /* flag has been true for 1.3s */
+ CHECK_RATE_CTRS(2, 1, 1);
+ FLAG(false);
+ CHECK_RATE_CTRS(2, 1, 1);
+
+ ADD_MILLISECS_NO_SELECT(400);
+
+ CHECK_RATE_CTRS(2, 1, 1);
+ FLAG(true);
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS_NO_SELECT(699);
+ /* flag has been true for 1.999s */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* flag has been true for 2.0s */
+ CHECK_RATE_CTRS(2, 2, 2);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* flag has been true for 2.001s */
+ CHECK_RATE_CTRS(3, 2, 2);
+ ADD_MILLISECS_NO_SELECT(499);
+ /* flag has been true for 2.5s */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS_NO_SELECT(499);
+ /* flag has been true for 2.999s */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* flag has been true for 3.0s */
+ CHECK_RATE_CTRS(3, 3, 3);
+ ADD_MILLISECS_NO_SELECT(200);
+ /* flag has been true for 3.2s */
+ CHECK_RATE_CTRS(4, 3, 3);
+ FLAG(false);
+ CHECK_RATE_CTRS(4, 3, 3);
+
+ ADD_MILLISECS_NO_SELECT(4321);
+ CHECK_RATE_CTRS(4, 3, 3);
+
+ FLAG(true);
+ CHECK_RATE_CTRS(4, 3, 3);
+ ADD_MILLISECS_NO_SELECT(5678);
+ CHECK_RATE_CTRS(9, 9, 8);
+ FLAG(false);
+ CHECK_RATE_CTRS(9, 9, 8);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+
+ printf("\n----------- test forget_sum, when timer cb are invoked late\n\n");
+ osmo_tdef_set(g_my_obj_tdefs, T_FORGET_SUM, 10, OSMO_TDEF_S);
+
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ my_obj_init(&my_obj);
+
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS_NO_SELECT(100);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(1000);
+ /* 1 s of being false, forget_sum_usec has not yet occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(8999);
+ /* 9.999 s of being false, forget_sum_usec has not yet occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(1);
+ /* 10 s of being false, forget_sum_usec has occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* Since previous sums were forgotton, ceil() triggers again */
+ CHECK_RATE_CTRS(2, 0, 0);
+ /* If the sum had not been forgotten, adding 400 ms to the initial 100 ms would have triggered round(). Verify
+ * that this does not occur, since now full 500 ms are required */
+ ADD_MILLISECS_NO_SELECT(399);
+ CHECK_RATE_CTRS(2, 0, 0);
+ /* Adding another 100 ms will trigger round() */
+ ADD_MILLISECS_NO_SELECT(99);
+ CHECK_RATE_CTRS(2, 0, 0);
+ ADD_MILLISECS_NO_SELECT(1);
+ CHECK_RATE_CTRS(2, 1, 0);
+ /* If the sum had not been forgotten, adding 900 ms to the initial 100 ms would have triggered floor(). Verify
+ * that this does not occur, since now full 1000 ms are required. We already added 500 ms above. */
+ ADD_MILLISECS_NO_SELECT(400);
+ CHECK_RATE_CTRS(2, 1, 0);
+ /* Adding another 100 ms will trigger floor() */
+ ADD_MILLISECS_NO_SELECT(99);
+ CHECK_RATE_CTRS(2, 1, 0);
+ ADD_MILLISECS_NO_SELECT(1);
+ CHECK_RATE_CTRS(2, 1, 1);
+
+ /* Test that durations of false below forget_sum_usec never trigger a forget */
+ ADD_MILLISECS_NO_SELECT(300);
+ CHECK_RATE_CTRS(3, 1, 1);
+ /* internal counter is now at 0.3s above the last reported rate counter */
+ FLAG(false);
+ ADD_MILLISECS_NO_SELECT(9999);
+ FLAG(true);
+ ADD_MILLISECS_NO_SELECT(25);
+ FLAG(false);
+ ADD_MILLISECS_NO_SELECT(9999);
+ FLAG(true);
+ ADD_MILLISECS_NO_SELECT(25);
+ FLAG(false);
+ ADD_MILLISECS_NO_SELECT(9999);
+ FLAG(true);
+ ADD_MILLISECS_NO_SELECT(25);
+ FLAG(false);
+ ADD_MILLISECS_NO_SELECT(9999);
+ FLAG(true);
+ ADD_MILLISECS_NO_SELECT(25);
+ /* internal counter is now at 0.4s above the last reported rate counter */
+ CHECK_RATE_CTRS(3, 1, 1);
+ ADD_MILLISECS_NO_SELECT(100);
+ CHECK_RATE_CTRS(3, 2, 1);
+ ADD_MILLISECS_NO_SELECT(500);
+ CHECK_RATE_CTRS(3, 2, 2);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+
+#define SET_TDEFS(gran, round_thresh, forget_sum) do { \
+ osmo_tdef_set(g_my_obj_tdefs, T_GRAN, gran, OSMO_TDEF_MS); \
+ osmo_tdef_set(g_my_obj_tdefs, T_ROUND_THRESH, round_thresh, OSMO_TDEF_MS); \
+ osmo_tdef_set(g_my_obj_tdefs, T_FORGET_SUM, forget_sum, OSMO_TDEF_S); \
+ printf("T_defs: T_gran=%luusec T_round_threshold=%luusec T_forget_sum=%luusec\n", \
+ osmo_tdef_get(g_my_obj_tdefs, T_GRAN, OSMO_TDEF_US, -1), \
+ osmo_tdef_get(g_my_obj_tdefs, T_ROUND_THRESH, OSMO_TDEF_US, -1), \
+ osmo_tdef_get(g_my_obj_tdefs, T_FORGET_SUM, OSMO_TDEF_US, -1)); \
+ } while (0)
+
+ printf("\n----------- test T_defs\n\n");
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ SET_TDEFS(100, 10, 0);
+
+ my_obj_init(&my_obj);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ ADD_MILLISECS(100);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS(9);
+ /* flag has been true for 0.009s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(1);
+ /* flag has been true for 0.010s */
+ CHECK_RATE_CTRS(1, 1, 0);
+ ADD_MILLISECS(90);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 1, 1);
+
+ SET_TDEFS(200, 190, 1);
+ /* gran is changed to 200ms, but still continues until the next scheduled event until the change is picked up.
+ * For ceil(), it is 1 ms ahead.
+ * For round(), it is 10 ms ahead.
+ * For floor(), it is at the next full (previous) gran 100 ms ahead.
+ * When T_defs change, all internal sums are reset to zero without reporting.
+ */
+ CHECK_RATE_CTRS(1, 1, 1);
+ ADD_MILLISECS(1);
+ /* 1ms elapsed: ceil() picks up the T_gran change, starts anew. */
+ /* elapsed: ceil 0 ms */
+ CHECK_RATE_CTRS(1, 1, 1);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 1 ms */
+ /* ceil() increments because flag has been true for more than 1 us after reset */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(8);
+ /* 10 ms elapsed: round() picks up the T_gran change, starts anew */
+ /* elapsed: ceil 9 ms, round 0 ms */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(90);
+ /* 100 ms elapsed: floor() picks up the T_gran change, starts anew */
+ /* elapsed: ceil 99 ms, round 90 ms, floor 0 ms */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(99);
+ /* elapsed: ceil 198 ms, round 189 ms, floor 99 ms */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 199 ms, round 190 ms, floor 100 ms */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 200 ms, round 191 ms, floor 101 ms */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 201 ms, round 192 ms, floor 102 ms */
+ CHECK_RATE_CTRS(3, 2, 1);
+ ADD_MILLISECS(98);
+ /* elapsed: ceil 299 ms, round 290 ms, floor 200 ms */
+ CHECK_RATE_CTRS(3, 2, 2);
+ ADD_MILLISECS(99);
+ /* elapsed: ceil 398 ms, round 389 ms, floor 299 ms */
+ CHECK_RATE_CTRS(3, 2, 2);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 399 ms, round 390 ms, floor 300 ms */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 400 ms, round 391 ms, floor 301 ms */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 401 ms, round 392 ms, floor 302 ms */
+ CHECK_RATE_CTRS(4, 3, 2);
+ ADD_MILLISECS(98);
+ /* elapsed: ceil 499 ms, round 490 ms, floor 400 ms */
+ CHECK_RATE_CTRS(4, 3, 3);
+
+
+ SET_TDEFS(100, 0, 0);
+ /* T_defs change, but they only get picked up upon the next event:
+ * For ceil(), it is 102 ms ahead.
+ * For round(), it is 100 ms ahead (thresh is still 190, currently at 90).
+ * For floor(), it is 200 ms ahead.
+ * When T_defs change, all internal sums are reset to zero without reporting.
+ */
+ CHECK_RATE_CTRS(4, 3, 3);
+ ADD_MILLISECS(100);
+ CHECK_RATE_CTRS(4, 3, 3);
+ /* round() picks up the new T_defs. Internal sum resets, nothing else happens yet.
+ * round() schedules the next event 50 ms ahead. */
+ ADD_MILLISECS(2);
+ CHECK_RATE_CTRS(4, 3, 3);
+ /* ceil() picks up the change, its next event is 1 ms ahead. */
+ ADD_MILLISECS(1);
+ /* ceil: 0.001
+ * round: 0.003
+ * floor: still 97 ms until it picks up the change */
+ CHECK_RATE_CTRS(5, 3, 3);
+ ADD_MILLISECS(46);
+ CHECK_RATE_CTRS(5, 3, 3);
+ ADD_MILLISECS(1);
+ /* round() has first counter trigger after T_defs change. */
+ CHECK_RATE_CTRS(5, 4, 3);
+ /* ceil: 0.048
+ * round: 0.050
+ * floor: still 50 ms until it picks up the change */
+ ADD_MILLISECS(50);
+ /* floor() picks up the change. nothing happens yet. */
+ /* ceil: 0.098
+ * round: 0.100
+ * floor: 0.0 */
+ ADD_MILLISECS(2);
+ /* ceil: 0.100
+ * round: 0.102
+ * floor: 0.002 */
+ CHECK_RATE_CTRS(5, 4, 3);
+ ADD_MILLISECS(1);
+ /* ceil: 0.101
+ * round: 0.103
+ * floor: 0.003 */
+ CHECK_RATE_CTRS(6, 4, 3);
+ ADD_MILLISECS(46);
+ /* ceil: 0.147
+ * round: 0.149
+ * floor: 0.049 */
+ CHECK_RATE_CTRS(6, 4, 3);
+ ADD_MILLISECS(1);
+ /* ceil: 0.148
+ * round: 0.150
+ * floor: 0.050 */
+ CHECK_RATE_CTRS(6, 5, 3);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+ return 0;
+}
diff --git a/tests/time_cc/time_cc_test.ok b/tests/time_cc/time_cc_test.ok
new file mode 100644
index 00000000..ccf84d95
--- /dev/null
+++ b/tests/time_cc/time_cc_test.ok
@@ -0,0 +1,328 @@
+
+----------- cumulating time, without forget_sum
+
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.100
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23000.101
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(99) --> 23000.200
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.300
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.400
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(300) --> 23000.700
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(299) --> 23000.999
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23001.000
+1 CHECK_RATE_CTRS(1, 1, 0) ok
+1 ADD_MILLISECS(499) --> 23001.499
+1 CHECK_RATE_CTRS(1, 1, 0) ok
+1 ADD_MILLISECS(1) --> 23001.500
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+1 ADD_MILLISECS(1) --> 23001.501
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(299) --> 23001.800
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(2, 1, 1) ok
+0 ADD_MILLISECS(400) --> 23002.200
+0 CHECK_RATE_CTRS(2, 1, 1) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(199) --> 23002.399
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(2) --> 23002.401
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(498) --> 23002.899
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(2) --> 23002.901
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(500) --> 23003.401
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(498) --> 23003.899
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(3) --> 23003.902
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(200) --> 23004.102
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(4, 3, 3) ok
+0 ADD_MILLISECS(4321) --> 23008.423
+0 CHECK_RATE_CTRS(4, 3, 3) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(5678) --> 23014.101
+1 CHECK_RATE_CTRS(9, 9, 8) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(9, 9, 8) ok
+
+----------- test forget_sum_usec
+
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.100
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(1000) --> 23001.100
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(8999) --> 23010.099
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(1) --> 23010.100
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23010.101
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(399) --> 23010.500
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(99) --> 23010.599
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23010.600
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(400) --> 23011.000
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(99) --> 23011.099
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(1) --> 23011.100
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(300) --> 23011.400
+1 CHECK_RATE_CTRS(3, 1, 1) ok
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23021.399
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23021.424
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23031.423
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23031.448
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23041.447
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23041.472
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23051.471
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23051.496
+1 CHECK_RATE_CTRS(3, 1, 1) ok
+1 ADD_MILLISECS(100) --> 23051.596
+1 CHECK_RATE_CTRS(3, 2, 1) ok
+1 ADD_MILLISECS(500) --> 23052.096
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(300) --> 23052.396
+1 CHECK_RATE_CTRS(4, 2, 2) ok
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(5000) --> 23057.396
+ flag: FALSE -> FALSE
+0 ADD_MILLISECS(5000) --> 23062.396
+0 CHECK_RATE_CTRS(4, 2, 2) ok
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(1) --> 23062.397
+1 CHECK_RATE_CTRS(5, 2, 2) ok
+1 ADD_MILLISECS(199) --> 23062.596
+1 CHECK_RATE_CTRS(5, 2, 2) ok
+1 ADD_MILLISECS(299) --> 23062.895
+1 CHECK_RATE_CTRS(5, 2, 2) ok
+1 ADD_MILLISECS(1) --> 23062.896
+1 CHECK_RATE_CTRS(5, 3, 2) ok
+1 ADD_MILLISECS(200) --> 23063.096
+1 CHECK_RATE_CTRS(5, 3, 2) ok
+1 ADD_MILLISECS(299) --> 23063.395
+1 CHECK_RATE_CTRS(5, 3, 2) ok
+1 ADD_MILLISECS(1) --> 23063.396
+1 CHECK_RATE_CTRS(5, 3, 3) ok
+
+----------- cumulating time, without forget_sum, when timer cb are invoked late
+
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.100
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.200
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.300
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.400
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(300) --> 23000.700
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(799) --> 23001.499
+1 CHECK_RATE_CTRS(1, 1, 0) ok
+1 ADD_MILLISECS(1) --> 23001.500
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+1 ADD_MILLISECS(300) --> 23001.800
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(2, 1, 1) ok
+0 ADD_MILLISECS(400) --> 23002.200
+0 CHECK_RATE_CTRS(2, 1, 1) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(699) --> 23002.899
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(1) --> 23002.900
+1 CHECK_RATE_CTRS(2, 2, 2) ok
+1 ADD_MILLISECS(1) --> 23002.901
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(499) --> 23003.400
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(499) --> 23003.899
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(1) --> 23003.900
+1 CHECK_RATE_CTRS(3, 3, 3) ok
+1 ADD_MILLISECS(200) --> 23004.100
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(4, 3, 3) ok
+0 ADD_MILLISECS(4321) --> 23008.421
+0 CHECK_RATE_CTRS(4, 3, 3) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(5678) --> 23014.099
+1 CHECK_RATE_CTRS(9, 9, 8) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(9, 9, 8) ok
+
+----------- test forget_sum, when timer cb are invoked late
+
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.100
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(1000) --> 23001.100
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(8999) --> 23010.099
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(1) --> 23010.100
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23010.101
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(399) --> 23010.500
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(99) --> 23010.599
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23010.600
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(400) --> 23011.000
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(99) --> 23011.099
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(1) --> 23011.100
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(300) --> 23011.400
+1 CHECK_RATE_CTRS(3, 1, 1) ok
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23021.399
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23021.424
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23031.423
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23031.448
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23041.447
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23041.472
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23051.471
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23051.496
+1 CHECK_RATE_CTRS(3, 1, 1) ok
+1 ADD_MILLISECS(100) --> 23051.596
+1 CHECK_RATE_CTRS(3, 2, 1) ok
+1 ADD_MILLISECS(500) --> 23052.096
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+
+----------- test T_defs
+
+T_defs: T_gran=100000usec T_round_threshold=10000usec T_forget_sum=0usec
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.100
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(9) --> 23000.109
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23000.110
+1 CHECK_RATE_CTRS(1, 1, 0) ok
+1 ADD_MILLISECS(90) --> 23000.200
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+T_defs: T_gran=200000usec T_round_threshold=190000usec T_forget_sum=1000000usec
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+1 ADD_MILLISECS(1) --> 23000.201
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+1 ADD_MILLISECS(1) --> 23000.202
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(8) --> 23000.210
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(90) --> 23000.300
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(99) --> 23000.399
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(1) --> 23000.400
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(1) --> 23000.401
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(1) --> 23000.402
+1 CHECK_RATE_CTRS(3, 2, 1) ok
+1 ADD_MILLISECS(98) --> 23000.500
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(99) --> 23000.599
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(1) --> 23000.600
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(1) --> 23000.601
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(1) --> 23000.602
+1 CHECK_RATE_CTRS(4, 3, 2) ok
+1 ADD_MILLISECS(98) --> 23000.700
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+T_defs: T_gran=100000usec T_round_threshold=0usec T_forget_sum=0usec
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(100) --> 23000.800
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(2) --> 23000.802
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(1) --> 23000.803
+1 CHECK_RATE_CTRS(5, 3, 3) ok
+1 ADD_MILLISECS(46) --> 23000.849
+1 CHECK_RATE_CTRS(5, 3, 3) ok
+1 ADD_MILLISECS(1) --> 23000.850
+1 CHECK_RATE_CTRS(5, 4, 3) ok
+1 ADD_MILLISECS(50) --> 23000.900
+1 ADD_MILLISECS(2) --> 23000.902
+1 CHECK_RATE_CTRS(5, 4, 3) ok
+1 ADD_MILLISECS(1) --> 23000.903
+1 CHECK_RATE_CTRS(6, 4, 3) ok
+1 ADD_MILLISECS(46) --> 23000.949
+1 CHECK_RATE_CTRS(6, 4, 3) ok
+1 ADD_MILLISECS(1) --> 23000.950
+1 CHECK_RATE_CTRS(6, 5, 3) ok
diff --git a/tests/timer/clk_override_test.c b/tests/timer/clk_override_test.c
index 308e8212..e67a6ed6 100644
--- a/tests/timer/clk_override_test.c
+++ b/tests/timer/clk_override_test.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
diff --git a/tests/timer/timer_test.c b/tests/timer/timer_test.c
index d2b0204d..9c51ad94 100644
--- a/tests/timer/timer_test.c
+++ b/tests/timer/timer_test.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -33,7 +29,7 @@
#include <osmocom/core/select.h>
#include <osmocom/core/linuxlist.h>
-#include "../config.h"
+#include "config.h"
static void main_timer_fired(void *data);
static void secondary_timer_fired(void *data);
diff --git a/tests/tlv/tlv_test.c b/tests/tlv/tlv_test.c
index fdd15ab0..8e8bd603 100644
--- a/tests/tlv/tlv_test.c
+++ b/tests/tlv/tlv_test.c
@@ -188,7 +188,7 @@ static void check_lv_shift_data_len(size_t data_len,
}
}
-static void test_tlv_shift_functions()
+static void test_tlv_shift_functions(void)
{
uint8_t test_data[1024];
uint8_t buf[1024];
@@ -250,7 +250,7 @@ static void test_tlv_shift_functions()
/* Most GSM related protocols clearly indicate that in case of duplicate
* IEs, only the first occurrence shall be used, while any further occurrences
* shall be ignored. See e.g. 3GPP TS 24.008 Section 8.6.3 */
-static void test_tlv_repeated_ie()
+static void test_tlv_repeated_ie(void)
{
uint8_t test_data[768];
int i, rc;
@@ -288,7 +288,7 @@ static void test_tlv_repeated_ie()
OSMO_ASSERT(dec3[2].lv[tag].val == &test_data[2 + 3 + 3]);
}
-static void test_tlv_encoder()
+static void test_tlv_encoder(void)
{
const uint8_t enc_ies[] = {
0x17, 0x14, 0x06, 0x2b, 0x12, 0x2b, 0x0b, 0x40, 0x2b, 0xb7, 0x05, 0xd0, 0x63, 0x82, 0x95, 0x03, 0x05, 0x40,
@@ -332,7 +332,7 @@ static void test_tlv_encoder()
msgb_free(msg);
}
-static void test_tlv_parser_bounds()
+static void test_tlv_parser_bounds(void)
{
struct tlv_definition tdef;
struct tlv_parsed dec;
@@ -423,7 +423,7 @@ static void test_tlv_parser_bounds()
OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
}
-static void test_tlv_lens()
+static void test_tlv_lens(void)
{
uint16_t buf_len;
uint8_t buf[512];
@@ -454,6 +454,30 @@ static void test_tlv_lens()
}
}
+static void test_tlv_type_single_tv(void)
+{
+ #define SAMPLE_SINGLE_TV_IE 0x08
+ const struct tlv_definition att_tlvdef = {
+ .def = {
+ [SAMPLE_SINGLE_TV_IE] = { TLV_TYPE_SINGLE_TV, 0 },
+ },
+ };
+ struct tlv_parsed tp;
+ int rc;
+ uint8_t exp_val = 0x03;
+ uint8_t buf[] = { (SAMPLE_SINGLE_TV_IE << 4) | (exp_val & 0x0f) };
+ const uint8_t *val;
+
+ rc = tlv_parse(&tp, &att_tlvdef, buf, sizeof(buf), 0, 0);
+ OSMO_ASSERT(rc == 1);
+ OSMO_ASSERT(TLVP_PRESENT(&tp, SAMPLE_SINGLE_TV_IE));
+ val = TLVP_VAL(&tp, SAMPLE_SINGLE_TV_IE);
+ OSMO_ASSERT(val);
+ OSMO_ASSERT(val == &buf[0]);
+ OSMO_ASSERT(*val == buf[0]);
+ OSMO_ASSERT((*val & 0x0f) == exp_val);
+}
+
int main(int argc, char **argv)
{
//osmo_init_logging2(ctx, &info);
@@ -463,6 +487,7 @@ int main(int argc, char **argv)
test_tlv_encoder();
test_tlv_parser_bounds();
test_tlv_lens();
+ test_tlv_type_single_tv();
printf("Done.\n");
return EXIT_SUCCESS;
diff --git a/tests/use_count/use_count_test.c b/tests/use_count/use_count_test.c
index 95af3082..b784aeb4 100644
--- a/tests/use_count/use_count_test.c
+++ b/tests/use_count/use_count_test.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
@@ -196,7 +192,7 @@ static struct foo *foo_alloc(const char *name, size_t static_entries)
return foo;
}
-void print_foos()
+void print_foos(void)
{
int count = 0;
struct foo *foo;
@@ -208,7 +204,7 @@ void print_foos()
fprintf(stderr, "%d foos\n\n", count);
}
-static void test_use_count_fsm()
+static void test_use_count_fsm(void)
{
struct foo *a, *b, *c;
log("\n%s()\n", __func__);
diff --git a/tests/use_count/use_count_test.err b/tests/use_count/use_count_test.err
index 97e74a51..29ebfb73 100644
--- a/tests/use_count/use_count_test.err
+++ b/tests/use_count/use_count_test.err
@@ -11,9 +11,9 @@ c: 0 (-)
3 foos
A few gets and puts, logging source file information
-DFOO NOTICE foo(a){IN_USE}: a +1 barring: now used by 1 (barring) (use_count_test.c:223)
-DFOO NOTICE foo(b){IN_USE}: b +1 barring: now used by 1 (barring) (use_count_test.c:225)
-DFOO NOTICE foo(b){IN_USE}: b +1 fighting: now used by 2 (barring,fighting) (use_count_test.c:226)
+DFOO NOTICE foo(a){IN_USE}: a +1 barring: now used by 1 (barring) (use_count_test.c:219)
+DFOO NOTICE foo(b){IN_USE}: b +1 barring: now used by 1 (barring) (use_count_test.c:221)
+DFOO NOTICE foo(b){IN_USE}: b +1 fighting: now used by 2 (barring,fighting) (use_count_test.c:222)
all use counts:
a: 1 (barring)
@@ -22,7 +22,7 @@ c: 0 (-)
3 foos
Attempt to get more than one on limited 'barring' user:
-DFOO ERROR foo(b){IN_USE}: Attempt to get more than one barring (use_count_test.c:231)
+DFOO ERROR foo(b){IN_USE}: Attempt to get more than one barring (use_count_test.c:227)
osmo_use_count_get_put(b, barring, 1) returned error: -34 Numerical result out of range
all use counts:
@@ -32,7 +32,7 @@ c: 0 (-)
3 foos
Put away one user of b
-DFOO NOTICE foo(b){IN_USE}: b -1 barring: now used by 1 (fighting) (use_count_test.c:235)
+DFOO NOTICE foo(b){IN_USE}: b -1 barring: now used by 1 (fighting) (use_count_test.c:231)
all use counts:
a: 1 (barring)
diff --git a/tests/ussd/ussd_test.c b/tests/ussd/ussd_test.c
index 80250711..2b8321d3 100644
--- a/tests/ussd/ussd_test.c
+++ b/tests/ussd/ussd_test.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/application.h>
diff --git a/tests/utils/utils_test.c b/tests/utils/utils_test.c
index 108bf5a2..9ab12a15 100644
--- a/tests/utils/utils_test.c
+++ b/tests/utils/utils_test.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/gsm/ipa.h>
@@ -35,6 +31,7 @@
#include <errno.h>
#include <limits.h>
#include <inttypes.h>
+#include <string.h>
static void hexdump_test(void)
{
@@ -346,7 +343,7 @@ static struct {
{ "DeafBeddedBabeAcceededFadedDecaff", 32, 32, false, false },
};
-bool test_is_hexstr()
+bool test_is_hexstr(void)
{
int i;
bool pass = true;
@@ -770,6 +767,65 @@ static void isqrt_test(void)
}
}
+static void mod_test_mod(int x, int y, int expected_result)
+{
+ int result;
+ result = x % y;
+ printf(" %d mod %d = %d = %d\n", x, y, result, expected_result);
+ OSMO_ASSERT(result == expected_result);
+}
+
+static void mod_test_mod_flr(int x, int y, int expected_result)
+{
+ int result;
+ result = OSMO_MOD_FLR(x, y);
+ printf(" %d mod_flr %d = %d = %d\n", x, y, result, expected_result);
+ OSMO_ASSERT(result == expected_result);
+}
+
+static void mod_test_mod_euc(int x, int y, int expected_result)
+{
+ int result;
+ result = OSMO_MOD_EUC(x, y);
+ printf(" %d mod_euc %d = %d = %d\n", x, y, result, expected_result);
+ OSMO_ASSERT(result == expected_result);
+}
+
+static void mod_test(void)
+{
+ /* See also: Daan Leijen, Division and Modulus for Computer
+ * Scientists, section 1.3 */
+
+ printf("\nTesting built in truncated modulo for comparison:\n");
+ mod_test_mod(8, 3, 2);
+ mod_test_mod(8, -3, 2);
+ mod_test_mod(-8, 3, -2);
+ mod_test_mod(-8, -3, -2);
+ mod_test_mod(1, 2, 1);
+ mod_test_mod(1, -2, 1);
+ mod_test_mod(-1, 2, -1);
+ mod_test_mod(-1, -2, -1);
+
+ printf("\nTesting OSMO_MOD_FLR():\n");
+ mod_test_mod_flr(8, 3, 2);
+ mod_test_mod_flr(8, -3, -1);
+ mod_test_mod_flr(-8, 3, 1);
+ mod_test_mod_flr(-8, -3, -2);
+ mod_test_mod_flr(1, 2, 1);
+ mod_test_mod_flr(1, -2, -1);
+ mod_test_mod_flr(-1, 2, 1);
+ mod_test_mod_flr(-1, -2, -1);
+
+ printf("\nTesting OSMO_MOD_EUC():\n");
+ mod_test_mod_euc(8, 3, 2);
+ mod_test_mod_euc(8, -3, 2);
+ mod_test_mod_euc(-8, 3, 1);
+ mod_test_mod_euc(-8, -3, 1);
+ mod_test_mod_euc(1, 2, 1);
+ mod_test_mod_euc(1, -2, 1);
+ mod_test_mod_euc(-1, 2, 1);
+ mod_test_mod_euc(-1, -2, 1);
+}
struct osmo_sockaddr_to_str_and_uint_test_case {
uint16_t port;
@@ -1027,7 +1083,7 @@ struct osmo_str_tolowupper_test_data osmo_str_tolowupper_tests[] = {
};
-static void osmo_str_tolowupper_test()
+static void osmo_str_tolowupper_test(void)
{
int i;
char buf[128];
@@ -1201,7 +1257,7 @@ int strbuf_cascade(char *buf, size_t buflen)
return sb.chars_needed;
}
-void strbuf_test()
+void strbuf_test(void)
{
char buf[256];
int rc;
@@ -1224,9 +1280,6 @@ void strbuf_test()
snprintf(buf, sizeof(buf), "0x2b 0x2b 0x2b...");
printf("4: (need %d chars, had size=0) %s\n", rc, buf);
- rc = strbuf_example2(NULL, 99);
- printf("5: (need %d chars, had NULL buffer)\n", rc);
-
printf("\ncascade:\n");
rc = strbuf_cascade(buf, sizeof(buf));
printf("(need %d chars)\n%s\n", rc, buf);
@@ -1234,7 +1287,7 @@ void strbuf_test()
printf("(need %d chars, had size=63) %s\n", rc, buf);
}
-void strbuf_test_nolen()
+void strbuf_test_nolen(void)
{
char buf[20];
struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
@@ -1251,6 +1304,67 @@ void strbuf_test_nolen()
printf("%zu: %s (need=%zu)\n", sb.len, buf, sb.chars_needed);
}
+void strbuf_test_tail_for_buflen(size_t buflen)
+{
+ char buf[buflen];
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ printf("\n%s(%zu)\n", __func__, buflen);
+
+#define SHOW(N) \
+ printf(#N ": %s sb.chars_needed=%zu sb.pos=&sb.buf[%d]\n", \
+ osmo_quote_str(buf, -1), sb.chars_needed, (int)(sb.pos - sb.buf))
+
+ /* shorten in steps using OSMO_STRBUF_DROP_TAIL(), removing and re-adding a trailing newline. */
+ OSMO_STRBUF_PRINTF(sb, "banananana\n");
+ SHOW(1);
+ OSMO_STRBUF_DROP_TAIL(sb, 3);
+ SHOW(2);
+ OSMO_STRBUF_PRINTF(sb, "\n");
+ SHOW(3);
+ OSMO_STRBUF_DROP_TAIL(sb, 3);
+ SHOW(4);
+ OSMO_STRBUF_PRINTF(sb, "\n");
+ SHOW(5);
+
+ /* drop trailing newline */
+ OSMO_STRBUF_DROP_TAIL(sb, 1);
+ SHOW(6);
+
+ /* test writing something to the end and letting OSMO_STRBUF_ADDED_TAIL() know later */
+ int n = OSMO_MIN(6, OSMO_STRBUF_REMAIN(sb));
+ if (n)
+ memcpy(sb.pos, "bread\n", n);
+ OSMO_STRBUF_ADDED_TAIL(sb, 6);
+ SHOW(7);
+}
+
+void strbuf_test_tail(void)
+{
+ strbuf_test_tail_for_buflen(64);
+ strbuf_test_tail_for_buflen(32);
+ strbuf_test_tail_for_buflen(16);
+ strbuf_test_tail_for_buflen(8);
+ strbuf_test_tail_for_buflen(4);
+ strbuf_test_tail_for_buflen(1);
+}
+
+void strbuf_test_remain_char_count(void)
+{
+ char buf[20];
+ struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
+
+ printf("\n%s\n", __func__);
+
+ printf("remaining space: %zu\n", OSMO_STRBUF_REMAIN(sb));
+ printf("current char count: %zu\n", OSMO_STRBUF_CHAR_COUNT(sb));
+
+ printf("populating the buffer\n");
+ OSMO_STRBUF_PRINTF(sb, "osmocom");
+
+ printf("remaining space: %zu\n", OSMO_STRBUF_REMAIN(sb));
+ printf("current char count: %zu\n", OSMO_STRBUF_CHAR_COUNT(sb));
+}
+
static void startswith_test_str(const char *str, const char *startswith_str, bool expect_rc)
{
bool rc = osmo_str_startswith(str, startswith_str);
@@ -1260,7 +1374,7 @@ static void startswith_test_str(const char *str, const char *startswith_str, boo
printf(" ERROR: EXPECTED %s\n", expect_rc ? "true" : "false");
}
-static void startswith_test()
+static void startswith_test(void)
{
printf("\n%s()\n", __func__);
startswith_test_str(NULL, NULL, true);
@@ -1305,7 +1419,7 @@ static char *foo_name_c_zero_null(void *ctx, const char *arg)
OSMO_NAME_C_IMPL(ctx, 0, NULL, foo_name_buf, arg)
}
-static void name_c_impl_test()
+static void name_c_impl_test(void)
{
char *test_strs[] = {
"test",
@@ -1694,13 +1808,20 @@ struct float_str_to_int_test float_str_to_int_tests[] = {
};
const char *errno_str(int rc)
{
- if (rc == -EINVAL)
+ switch (rc) {
+ case -EINVAL:
return "=-EINVAL";
- if (rc == -ERANGE)
+ case -ERANGE:
return "=-ERANGE";
- return "";
+ case -E2BIG:
+ return "=-E2BIG";
+ case -EOVERFLOW:
+ return "=-EOVERFLOW";
+ default:
+ return "";
+ }
}
-void test_float_str_to_int()
+void test_float_str_to_int(void)
{
const struct float_str_to_int_test *t;
printf("--- %s\n", __func__);
@@ -1862,7 +1983,7 @@ struct int_to_float_str_test int_to_float_str_tests[] = {
{ 23, -9223372036854775807, "-0.00009223372036854775807" },
{ 23, INT64_MIN, "-ERR" },
};
-void test_int_to_float_str()
+void test_int_to_float_str(void)
{
const struct int_to_float_str_test *t;
printf("--- %s\n", __func__);
@@ -1884,6 +2005,190 @@ void test_int_to_float_str()
}
}
+struct str_to_int_test {
+ const char *str;
+ int base;
+ int min_val;
+ int max_val;
+ int expect_rc;
+ int expect_val;
+};
+/* Avoid using INT_MAX and INT_MIN because that would produce different test output on different architectures */
+struct str_to_int_test str_to_int_tests[] = {
+ { NULL, 10, -1000, 1000, -EINVAL, 0 },
+ { "", 10, -1000, 1000, -EINVAL, 0 },
+ { " ", 10, -1000, 1000, -EINVAL, 0 },
+ { "-", 10, -1000, 1000, -EINVAL, 0 },
+ { "--", 10, -1000, 1000, -EINVAL, 0 },
+ { "+", 10, -1000, 1000, -EINVAL, 0 },
+ { "++", 10, -1000, 1000, -EINVAL, 0 },
+
+ { "0", 10, -1000, 1000, 0, 0 },
+ { "1", 10, -1000, 1000, 0, 1 },
+ { "+1", 10, -1000, 1000, 0, 1 },
+ { "-1", 10, -1000, 1000, 0, -1 },
+ { "1000", 10, -1000, 1000, 0, 1000 },
+ { "+1000", 10, -1000, 1000, 0, 1000 },
+ { "-1000", 10, -1000, 1000, 0, -1000 },
+ { "1001", 10, -1000, 1000, -ERANGE, 1001 },
+ { "+1001", 10, -1000, 1000, -ERANGE, 1001 },
+ { "-1001", 10, -1000, 1000, -ERANGE, -1001 },
+
+ { "0", 16, -1000, 1000, 0, 0 },
+ { "1", 16, -1000, 1000, 0, 1 },
+ { "0x1", 16, -1000, 1000, 0, 1 },
+ { "+1", 16, -1000, 1000, 0, 1 },
+ { "-1", 16, -1000, 1000, 0, -1 },
+ { "+0x1", 16, -1000, 1000, 0, 1 },
+ { "-0x1", 16, -1000, 1000, 0, -1 },
+ { "3e8", 16, -1000, 1000, 0, 1000 },
+ { "3E8", 16, -1000, 1000, 0, 1000 },
+ { "0x3e8", 16, -1000, 1000, 0, 1000 },
+ { "0x3E8", 16, -1000, 1000, 0, 1000 },
+ { "+3e8", 16, -1000, 1000, 0, 1000 },
+ { "+3E8", 16, -1000, 1000, 0, 1000 },
+ { "+0x3e8", 16, -1000, 1000, 0, 1000 },
+ { "+0x3E8", 16, -1000, 1000, 0, 1000 },
+ { "-3e8", 16, -1000, 1000, 0, -1000 },
+ { "-3E8", 16, -1000, 1000, 0, -1000 },
+ { "-0x3e8", 16, -1000, 1000, 0, -1000 },
+ { "-0x3E8", 16, -1000, 1000, 0, -1000 },
+ { "3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "0x3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "0x3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+0x3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+0x3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "-3e9", 16, -1000, 1000, -ERANGE, -1001 },
+ { "-3E9", 16, -1000, 1000, -ERANGE, -1001 },
+ { "-0x3e9", 16, -1000, 1000, -ERANGE, -1001 },
+ { "-0x3E9", 16, -1000, 1000, -ERANGE, -1001 },
+
+ { "garble", 10, -1000, 1000, -EINVAL, 0 },
+ { "-garble", 10, -1000, 1000, -EINVAL, 0 },
+ { "0x123", 10, -1000, 1000, -E2BIG, 0 },
+ { "123potatoes", 10, -1000, 1000, -E2BIG, 123 },
+ { "123 potatoes", 10, -1000, 1000, -E2BIG, 123 },
+ { "123 ", 10, -1000, 1000, -E2BIG, 123 },
+ { "123.4", 10, -1000, 1000, -E2BIG, 123 },
+};
+void test_str_to_int(void)
+{
+ const struct str_to_int_test *t;
+ printf("--- %s\n", __func__);
+ for (t = str_to_int_tests; (t - str_to_int_tests) < ARRAY_SIZE(str_to_int_tests); t++) {
+ int rc;
+ int val;
+ rc = osmo_str_to_int(&val, t->str, t->base, t->min_val, t->max_val);
+ printf("osmo_str_to_int(%s, %d, %d, %d) -> rc=%d%s val=%d\n",
+ osmo_quote_str(t->str, -1), t->base, t->min_val, t->max_val, rc, errno_str(rc), val);
+
+ if (rc != t->expect_rc)
+ printf(" ERROR: expected rc=%d%s\n", t->expect_rc, errno_str(t->expect_rc));
+ if (val != t->expect_val)
+ printf(" ERROR: expected val=%d\n", t->expect_val);
+ }
+}
+
+struct str_to_int64_test {
+ const char *str;
+ int base;
+ int64_t min_val;
+ int64_t max_val;
+ int expect_rc;
+ int64_t expect_val;
+};
+struct str_to_int64_test str_to_int64_tests[] = {
+ { NULL, 10, -1000, 1000, -EINVAL, 0 },
+ { "", 10, -1000, 1000, -EINVAL, 0 },
+ { " ", 10, -1000, 1000, -EINVAL, 0 },
+ { "-", 10, -1000, 1000, -EINVAL, 0 },
+ { "--", 10, -1000, 1000, -EINVAL, 0 },
+ { "+", 10, -1000, 1000, -EINVAL, 0 },
+ { "++", 10, -1000, 1000, -EINVAL, 0 },
+
+ { "0", 10, -1000, 1000, 0, 0 },
+ { "1", 10, -1000, 1000, 0, 1 },
+ { "+1", 10, -1000, 1000, 0, 1 },
+ { "-1", 10, -1000, 1000, 0, -1 },
+ { "1000", 10, -1000, 1000, 0, 1000 },
+ { "+1000", 10, -1000, 1000, 0, 1000 },
+ { "-1000", 10, -1000, 1000, 0, -1000 },
+ { "1001", 10, -1000, 1000, -ERANGE, 1001 },
+ { "+1001", 10, -1000, 1000, -ERANGE, 1001 },
+ { "-1001", 10, -1000, 1000, -ERANGE, -1001 },
+
+ { "0", 16, -1000, 1000, 0, 0 },
+ { "1", 16, -1000, 1000, 0, 1 },
+ { "0x1", 16, -1000, 1000, 0, 1 },
+ { "+1", 16, -1000, 1000, 0, 1 },
+ { "-1", 16, -1000, 1000, 0, -1 },
+ { "+0x1", 16, -1000, 1000, 0, 1 },
+ { "-0x1", 16, -1000, 1000, 0, -1 },
+ { "3e8", 16, -1000, 1000, 0, 1000 },
+ { "3E8", 16, -1000, 1000, 0, 1000 },
+ { "0x3e8", 16, -1000, 1000, 0, 1000 },
+ { "0x3E8", 16, -1000, 1000, 0, 1000 },
+ { "+3e8", 16, -1000, 1000, 0, 1000 },
+ { "+3E8", 16, -1000, 1000, 0, 1000 },
+ { "+0x3e8", 16, -1000, 1000, 0, 1000 },
+ { "+0x3E8", 16, -1000, 1000, 0, 1000 },
+ { "-3e8", 16, -1000, 1000, 0, -1000 },
+ { "-3E8", 16, -1000, 1000, 0, -1000 },
+ { "-0x3e8", 16, -1000, 1000, 0, -1000 },
+ { "-0x3E8", 16, -1000, 1000, 0, -1000 },
+ { "3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "0x3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "0x3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+0x3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+0x3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "-3e9", 16, -1000, 1000, -ERANGE, -1001 },
+ { "-3E9", 16, -1000, 1000, -ERANGE, -1001 },
+ { "-0x3e9", 16, -1000, 1000, -ERANGE, -1001 },
+ { "-0x3E9", 16, -1000, 1000, -ERANGE, -1001 },
+
+ { "garble", 10, -1000, 1000, -EINVAL, 0 },
+ { "-garble", 10, -1000, 1000, -EINVAL, 0 },
+ { "0x123", 10, -1000, 1000, -E2BIG, 0 },
+ { "123potatoes", 10, -1000, 1000, -E2BIG, 123 },
+ { "123 potatoes", 10, -1000, 1000, -E2BIG, 123 },
+ { "123 ", 10, -1000, 1000, -E2BIG, 123 },
+ { "123.4", 10, -1000, 1000, -E2BIG, 123 },
+
+ { "-9223372036854775808", 10, INT64_MIN, INT64_MAX, 0, INT64_MIN },
+ { "9223372036854775807", 10, INT64_MIN, INT64_MAX, 0, INT64_MAX },
+
+ { "-9223372036854775809", 10, INT64_MIN, INT64_MAX, -EOVERFLOW, INT64_MIN },
+ { "9223372036854775808", 10, INT64_MIN, INT64_MAX, -EOVERFLOW, INT64_MAX },
+
+ { "-9223372036854775808", 10, -1000, 1000, -ERANGE, INT64_MIN },
+ { "9223372036854775807", 10, -1000, 1000, -ERANGE, INT64_MAX },
+ { "-9223372036854775809", 10, -1000, 1000, -EOVERFLOW, INT64_MIN },
+ { "9223372036854775808", 10, -1000, 1000, -EOVERFLOW, INT64_MAX },
+};
+void test_str_to_int64(void)
+{
+ const struct str_to_int64_test *t;
+ printf("--- %s\n", __func__);
+ for (t = str_to_int64_tests; (t - str_to_int64_tests) < ARRAY_SIZE(str_to_int64_tests); t++) {
+ int rc;
+ int64_t val;
+ rc = osmo_str_to_int64(&val, t->str, t->base, t->min_val, t->max_val);
+ printf("osmo_str_to_int64(%s, %d, %"PRId64", %"PRId64") -> rc=%d%s val=%"PRId64"\n",
+ osmo_quote_str(t->str, -1), t->base, t->min_val, t->max_val, rc, errno_str(rc), val);
+
+ if (rc != t->expect_rc)
+ printf(" ERROR: expected rc=%d%s\n", t->expect_rc, errno_str(t->expect_rc));
+ if (val != t->expect_val)
+ printf(" ERROR: expected val=%"PRId64"\n", t->expect_val);
+ }
+}
+
int main(int argc, char **argv)
{
static const struct log_info log_info = {};
@@ -1901,15 +2206,20 @@ int main(int argc, char **argv)
str_escape3_test();
str_quote3_test();
isqrt_test();
+ mod_test();
osmo_sockaddr_to_str_and_uint_test();
osmo_str_tolowupper_test();
strbuf_test();
strbuf_test_nolen();
+ strbuf_test_tail();
+ strbuf_test_remain_char_count();
startswith_test();
name_c_impl_test();
osmo_print_n_test();
osmo_strnchr_test();
test_float_str_to_int();
test_int_to_float_str();
+ test_str_to_int();
+ test_str_to_int64();
return 0;
}
diff --git a/tests/utils/utils_test.ok b/tests/utils/utils_test.ok
index 0c71b8fa..8a66ba81 100644
--- a/tests/utils/utils_test.ok
+++ b/tests/utils/utils_test.ok
@@ -354,6 +354,36 @@ strcmp("NULL", osmo_quote_cstr_c(ctx, NULL, -1)) == 0
Testing integer square-root
+Testing built in truncated modulo for comparison:
+ 8 mod 3 = 2 = 2
+ 8 mod -3 = 2 = 2
+ -8 mod 3 = -2 = -2
+ -8 mod -3 = -2 = -2
+ 1 mod 2 = 1 = 1
+ 1 mod -2 = 1 = 1
+ -1 mod 2 = -1 = -1
+ -1 mod -2 = -1 = -1
+
+Testing OSMO_MOD_FLR():
+ 8 mod_flr 3 = 2 = 2
+ 8 mod_flr -3 = -1 = -1
+ -8 mod_flr 3 = 1 = 1
+ -8 mod_flr -3 = -2 = -2
+ 1 mod_flr 2 = 1 = 1
+ 1 mod_flr -2 = -1 = -1
+ -1 mod_flr 2 = 1 = 1
+ -1 mod_flr -2 = -1 = -1
+
+Testing OSMO_MOD_EUC():
+ 8 mod_euc 3 = 2 = 2
+ 8 mod_euc -3 = 2 = 2
+ -8 mod_euc 3 = 1 = 1
+ -8 mod_euc -3 = 1 = 1
+ 1 mod_euc 2 = 1 = 1
+ 1 mod_euc -2 = 1 = 1
+ -1 mod_euc 2 = 1 = 1
+ -1 mod_euc -2 = 1 = 1
+
osmo_sockaddr_to_str_and_uint_test
[0] [0.0.0.0]:0 addr_len=20 --> [0.0.0.0]:0 rc=7
[1] [255.255.255.255]:65535 addr_len=20 --> [255.255.255.255]:65535 rc=15
@@ -428,7 +458,6 @@ OSMO_STRBUF_PRINTF():
2: (need 42 chars, had size=42) T minus 10 9 8 7 6 5 4 3 2 1 ... Lift off
3: (need 42 chars, had size=42+1) T minus 10 9 8 7 6 5 4 3 2 1 ... Lift off!
4: (need 42 chars, had size=0) 0x2b 0x2b 0x2b...
-5: (need 42 chars, had NULL buffer)
cascade:
(need 134 chars)
@@ -440,6 +469,67 @@ strbuf_test_nolen
more: 0001011100101010000 (need=19)
10: 000101110 (need=9)
+strbuf_test_tail_for_buflen(64)
+1: "banananana\n" sb.chars_needed=11 sb.pos=&sb.buf[11]
+2: "bananana" sb.chars_needed=8 sb.pos=&sb.buf[8]
+3: "bananana\n" sb.chars_needed=9 sb.pos=&sb.buf[9]
+4: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+5: "banana\n" sb.chars_needed=7 sb.pos=&sb.buf[7]
+6: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+7: "bananabread\n" sb.chars_needed=12 sb.pos=&sb.buf[12]
+
+strbuf_test_tail_for_buflen(32)
+1: "banananana\n" sb.chars_needed=11 sb.pos=&sb.buf[11]
+2: "bananana" sb.chars_needed=8 sb.pos=&sb.buf[8]
+3: "bananana\n" sb.chars_needed=9 sb.pos=&sb.buf[9]
+4: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+5: "banana\n" sb.chars_needed=7 sb.pos=&sb.buf[7]
+6: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+7: "bananabread\n" sb.chars_needed=12 sb.pos=&sb.buf[12]
+
+strbuf_test_tail_for_buflen(16)
+1: "banananana\n" sb.chars_needed=11 sb.pos=&sb.buf[11]
+2: "bananana" sb.chars_needed=8 sb.pos=&sb.buf[8]
+3: "bananana\n" sb.chars_needed=9 sb.pos=&sb.buf[9]
+4: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+5: "banana\n" sb.chars_needed=7 sb.pos=&sb.buf[7]
+6: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+7: "bananabread\n" sb.chars_needed=12 sb.pos=&sb.buf[12]
+
+strbuf_test_tail_for_buflen(8)
+1: "bananan" sb.chars_needed=11 sb.pos=&sb.buf[8]
+2: "bananan" sb.chars_needed=8 sb.pos=&sb.buf[8]
+3: "bananan" sb.chars_needed=9 sb.pos=&sb.buf[8]
+4: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+5: "banana\n" sb.chars_needed=7 sb.pos=&sb.buf[7]
+6: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+7: "bananab" sb.chars_needed=12 sb.pos=&sb.buf[7]
+
+strbuf_test_tail_for_buflen(4)
+1: "ban" sb.chars_needed=11 sb.pos=&sb.buf[4]
+2: "ban" sb.chars_needed=8 sb.pos=&sb.buf[4]
+3: "ban" sb.chars_needed=9 sb.pos=&sb.buf[4]
+4: "ban" sb.chars_needed=6 sb.pos=&sb.buf[4]
+5: "ban" sb.chars_needed=7 sb.pos=&sb.buf[4]
+6: "ban" sb.chars_needed=6 sb.pos=&sb.buf[4]
+7: "ban" sb.chars_needed=12 sb.pos=&sb.buf[4]
+
+strbuf_test_tail_for_buflen(1)
+1: "" sb.chars_needed=11 sb.pos=&sb.buf[1]
+2: "" sb.chars_needed=8 sb.pos=&sb.buf[1]
+3: "" sb.chars_needed=9 sb.pos=&sb.buf[1]
+4: "" sb.chars_needed=6 sb.pos=&sb.buf[1]
+5: "" sb.chars_needed=7 sb.pos=&sb.buf[1]
+6: "" sb.chars_needed=6 sb.pos=&sb.buf[1]
+7: "" sb.chars_needed=12 sb.pos=&sb.buf[1]
+
+strbuf_test_remain_char_count
+remaining space: 20
+current char count: 0
+populating the buffer
+remaining space: 13
+current char count: 7
+
startswith_test()
osmo_str_startswith(NULL, NULL) == true
osmo_str_startswith("", NULL) == true
@@ -869,3 +959,123 @@ osmo_int_to_float_str_buf(-100000000, 23) -> rc=18 str="-0.000000000000001"
osmo_int_to_float_str_buf(9223372036854775807, 23) -> rc=25 str="0.00009223372036854775807"
osmo_int_to_float_str_buf(-9223372036854775807, 23) -> rc=26 str="-0.00009223372036854775807"
osmo_int_to_float_str_buf(-9223372036854775808, 23) -> rc=4 str="-ERR"
+--- test_str_to_int
+osmo_str_to_int(NULL, 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int(" ", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("-", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("--", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("+", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("++", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("0", 10, -1000, 1000) -> rc=0 val=0
+osmo_str_to_int("1", 10, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int("+1", 10, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int("-1", 10, -1000, 1000) -> rc=0 val=-1
+osmo_str_to_int("1000", 10, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("+1000", 10, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("-1000", 10, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int("1001", 10, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("+1001", 10, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("-1001", 10, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int("0", 16, -1000, 1000) -> rc=0 val=0
+osmo_str_to_int("1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int("0x1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int("+1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int("-1", 16, -1000, 1000) -> rc=0 val=-1
+osmo_str_to_int("+0x1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int("-0x1", 16, -1000, 1000) -> rc=0 val=-1
+osmo_str_to_int("3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("0x3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("0x3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("+3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("+3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("+0x3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("+0x3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("-3e8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int("-3E8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int("-0x3e8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int("-0x3E8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int("3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("0x3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("0x3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("+3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("+3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("+0x3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("+0x3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("-3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int("-3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int("-0x3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int("-0x3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int("garble", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("-garble", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("0x123", 10, -1000, 1000) -> rc=-7=-E2BIG val=0
+osmo_str_to_int("123potatoes", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int("123 potatoes", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int("123 ", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int("123.4", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+--- test_str_to_int64
+osmo_str_to_int64(NULL, 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64(" ", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("-", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("--", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("+", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("++", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("0", 10, -1000, 1000) -> rc=0 val=0
+osmo_str_to_int64("1", 10, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int64("+1", 10, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int64("-1", 10, -1000, 1000) -> rc=0 val=-1
+osmo_str_to_int64("1000", 10, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("+1000", 10, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("-1000", 10, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int64("1001", 10, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("+1001", 10, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("-1001", 10, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int64("0", 16, -1000, 1000) -> rc=0 val=0
+osmo_str_to_int64("1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int64("0x1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int64("+1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int64("-1", 16, -1000, 1000) -> rc=0 val=-1
+osmo_str_to_int64("+0x1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int64("-0x1", 16, -1000, 1000) -> rc=0 val=-1
+osmo_str_to_int64("3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("0x3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("0x3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("+3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("+3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("+0x3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("+0x3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("-3e8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int64("-3E8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int64("-0x3e8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int64("-0x3E8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int64("3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("0x3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("0x3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("+3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("+3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("+0x3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("+0x3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("-3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int64("-3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int64("-0x3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int64("-0x3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int64("garble", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("-garble", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("0x123", 10, -1000, 1000) -> rc=-7=-E2BIG val=0
+osmo_str_to_int64("123potatoes", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int64("123 potatoes", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int64("123 ", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int64("123.4", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int64("-9223372036854775808", 10, -9223372036854775808, 9223372036854775807) -> rc=0 val=-9223372036854775808
+osmo_str_to_int64("9223372036854775807", 10, -9223372036854775808, 9223372036854775807) -> rc=0 val=9223372036854775807
+osmo_str_to_int64("-9223372036854775809", 10, -9223372036854775808, 9223372036854775807) -> rc=-75=-EOVERFLOW val=-9223372036854775808
+osmo_str_to_int64("9223372036854775808", 10, -9223372036854775808, 9223372036854775807) -> rc=-75=-EOVERFLOW val=9223372036854775807
+osmo_str_to_int64("-9223372036854775808", 10, -1000, 1000) -> rc=-34=-ERANGE val=-9223372036854775808
+osmo_str_to_int64("9223372036854775807", 10, -1000, 1000) -> rc=-34=-ERANGE val=9223372036854775807
+osmo_str_to_int64("-9223372036854775809", 10, -1000, 1000) -> rc=-75=-EOVERFLOW val=-9223372036854775808
+osmo_str_to_int64("9223372036854775808", 10, -1000, 1000) -> rc=-75=-EOVERFLOW val=9223372036854775807
diff --git a/tests/v110/frame_test.c b/tests/v110/frame_test.c
new file mode 100644
index 00000000..ebc617c0
--- /dev/null
+++ b/tests/v110/frame_test.c
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/isdn/v110.h>
+
+
+static void test_frame_enc(void)
+{
+ struct osmo_v110_decoded_frame fr;
+ ubit_t bits[80];
+ unsigned int i;
+
+ memset(&fr, 0, sizeof(fr));
+
+ /* we abuse the fact that ubit_t is 8bit so we can actually
+ * store integer values to clearly identify which bit ends up where */
+
+ /* D1..D48: 101..148 */
+ for (i = 0; i < ARRAY_SIZE(fr.d_bits); i++)
+ fr.d_bits[i] = 101 + i;
+ /* E1..E7: 201..207 */
+ for (i = 0; i < ARRAY_SIZE(fr.e_bits); i++)
+ fr.e_bits[i] = 201 + i;
+ /* S1..S9: 211..219 */
+ for (i = 0; i < ARRAY_SIZE(fr.s_bits); i++)
+ fr.s_bits[i] = 211 + i;
+ /* X1..X2: 221..222 */
+ for (i = 0; i < ARRAY_SIZE(fr.x_bits); i++)
+ fr.x_bits[i] = 221 + i;
+
+ /* run encoder and dump to stdout */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_v110_encode_frame(bits, sizeof(bits), &fr);
+ osmo_v110_ubit_dump(stdout, bits, sizeof(bits));
+
+ /* run decoder on what we just encoded */
+ memset(&fr, 0, sizeof(fr));
+ osmo_v110_decode_frame(&fr, bits, sizeof(bits));
+
+ /* re-encode and dump again 'expout' will match it. */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_v110_encode_frame(bits, sizeof(bits), &fr);
+ osmo_v110_ubit_dump(stdout, bits, sizeof(bits));
+}
+
+
+int main(int argc, char **argv)
+{
+ test_frame_enc();
+}
+
diff --git a/tests/v110/frame_test.ok b/tests/v110/frame_test.ok
new file mode 100644
index 00000000..ecefaa87
--- /dev/null
+++ b/tests/v110/frame_test.ok
@@ -0,0 +1,20 @@
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 211
+1 107 108 109 110 111 112 221
+1 113 114 115 116 117 118 213
+1 119 120 121 122 123 124 214
+1 201 202 203 204 205 206 207
+1 125 126 127 128 129 130 216
+1 131 132 133 134 135 136 222
+1 137 138 139 140 141 142 218
+1 143 144 145 146 147 148 219
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 211
+1 107 108 109 110 111 112 221
+1 113 114 115 116 117 118 213
+1 119 120 121 122 123 124 214
+1 201 202 203 204 205 206 207
+1 125 126 127 128 129 130 216
+1 131 132 133 134 135 136 222
+1 137 138 139 140 141 142 218
+1 143 144 145 146 147 148 219
diff --git a/tests/v110/ra1_test.c b/tests/v110/ra1_test.c
new file mode 100644
index 00000000..48db553d
--- /dev/null
+++ b/tests/v110/ra1_test.c
@@ -0,0 +1,74 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/isdn/v110.h>
+
+
+static void test_ra1(enum osmo_v100_sync_ra1_rate rate)
+{
+ int user_rate = osmo_v110_sync_ra1_get_user_data_rate(rate);
+ int user_data_chunk_bits;
+ struct osmo_v110_decoded_frame fr;
+ ubit_t user_bits[48];
+ ubit_t bits[80];
+ unsigned int i;
+ int rc;
+
+ printf("\n======= User data rate %u\n", user_rate);
+
+ user_data_chunk_bits = osmo_v110_sync_ra1_get_user_data_chunk_bitlen(rate);
+ OSMO_ASSERT(user_data_chunk_bits >= 0);
+
+ /* we abuse the fact that ubit_t is 8bit so we can actually
+ * store integer values to clearly identify which bit ends up where */
+ memset(user_bits, 0xFE, sizeof(user_bits));
+ for (i = 0; i < user_data_chunk_bits; i++)
+ user_bits[i] = 101 + i;
+
+ printf("user_bits: ");
+ for (i = 0; i < user_data_chunk_bits; i++)
+ printf("%03d ", user_bits[i]);
+ printf("\n");
+
+ /* generate the decoded v.110 frame */
+ memset(&fr, 0, sizeof(fr));
+ rc = osmo_v110_sync_ra1_user_to_ir(rate, &fr, user_bits, user_data_chunk_bits);
+ OSMO_ASSERT(rc == 0);
+
+ /* run encoder and dump to stdout */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_v110_encode_frame(bits, sizeof(bits), &fr);
+ printf("dumping %u encoded bits in V.110 frame:\n", user_data_chunk_bits);
+ osmo_v110_ubit_dump(stdout, bits, sizeof(bits));
+
+ /* run decoder on what we just encoded */
+ memset(&fr, 0, sizeof(fr));
+ osmo_v110_decode_frame(&fr, bits, sizeof(bits));
+ printf("dumping re-decoded V.110 frame:\n");
+ printf("E-bits: %s\n", osmo_hexdump(fr.e_bits, sizeof(fr.e_bits)));
+ printf("S-bits: %s\n", osmo_hexdump(fr.s_bits, sizeof(fr.s_bits)));
+
+ /* re-encode and dump again 'expout' will match it. */
+ memset(user_bits, 0xff, sizeof(user_bits));
+ rc = osmo_v110_sync_ra1_ir_to_user(rate, user_bits, sizeof(user_bits), &fr);
+ if (rc != user_data_chunk_bits) {
+ fprintf(stderr, "ERROR: adapt_ir_to_user() returned %d, expected %u\n", rc,
+ user_data_chunk_bits);
+ exit(23);
+ }
+ fprintf(stdout, "re-decoded user bits: ");
+ for (i = 0; i < user_data_chunk_bits; i++)
+ printf("%03d ", user_bits[i]);
+ printf("\n");
+}
+
+
+int main(int argc, char **argv)
+{
+ for (int i = 0; i < _NUM_OSMO_V110_SYNC_RA1; i++)
+ test_ra1(i);
+}
+
diff --git a/tests/v110/ra1_test.ok b/tests/v110/ra1_test.ok
new file mode 100644
index 00000000..8a61fc3f
--- /dev/null
+++ b/tests/v110/ra1_test.ok
@@ -0,0 +1,216 @@
+
+======= User data rate 600
+user_bits: 101 102 103 104 105 106
+dumping 6 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 101 101 101 101 101 0
+1 101 101 102 102 102 102 0
+1 102 102 102 102 103 103 0
+1 103 103 103 103 103 103 0
+1 1 0 0 0 0 0 0
+1 104 104 104 104 104 104 0
+1 104 104 105 105 105 105 0
+1 105 105 105 105 106 106 0
+1 106 106 106 106 106 106 0
+dumping re-decoded V.110 frame:
+E-bits: 01 00 00 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106
+
+======= User data rate 1200
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112
+dumping 12 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 101 101 101 102 102 0
+1 102 102 103 103 103 103 0
+1 104 104 104 104 105 105 0
+1 105 105 106 106 106 106 0
+1 0 1 0 0 0 0 0
+1 107 107 107 107 108 108 0
+1 108 108 109 109 109 109 0
+1 110 110 110 110 111 111 0
+1 111 111 112 112 112 112 0
+dumping re-decoded V.110 frame:
+E-bits: 00 01 00 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112
+
+======= User data rate 2400
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
+dumping 24 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 101 102 102 103 103 0
+1 104 104 105 105 106 106 0
+1 107 107 108 108 109 109 0
+1 110 110 111 111 112 112 0
+1 1 1 0 0 0 0 0
+1 113 113 114 114 115 115 0
+1 116 116 117 117 118 118 0
+1 119 119 120 120 121 121 0
+1 122 122 123 123 124 124 0
+dumping re-decoded V.110 frame:
+E-bits: 01 01 00 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
+
+======= User data rate 4800
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+dumping 48 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 111 112 0
+1 113 114 115 116 117 118 0
+1 119 120 121 122 123 124 0
+1 0 1 1 0 0 0 0
+1 125 126 127 128 129 130 0
+1 131 132 133 134 135 136 0
+1 137 138 139 140 141 142 0
+1 143 144 145 146 147 148 0
+dumping re-decoded V.110 frame:
+E-bits: 00 01 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+
+======= User data rate 7200
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
+dumping 36 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 1 1 0
+1 111 112 1 1 113 114 0
+1 1 1 115 116 117 118 0
+1 1 0 1 0 0 0 0
+1 119 120 121 122 123 124 0
+1 125 126 127 128 1 1 0
+1 129 130 1 1 131 132 0
+1 1 1 133 134 135 136 0
+dumping re-decoded V.110 frame:
+E-bits: 01 00 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
+
+======= User data rate 9600
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+dumping 48 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 111 112 0
+1 113 114 115 116 117 118 0
+1 119 120 121 122 123 124 0
+1 0 1 1 0 0 0 0
+1 125 126 127 128 129 130 0
+1 131 132 133 134 135 136 0
+1 137 138 139 140 141 142 0
+1 143 144 145 146 147 148 0
+dumping re-decoded V.110 frame:
+E-bits: 00 01 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+
+======= User data rate 12000
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
+dumping 30 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 1 1 0
+1 111 112 1 1 113 114 0
+1 1 1 115 1 1 1 0
+1 0 0 1 0 0 0 0
+1 116 117 118 119 120 121 0
+1 122 123 124 125 1 1 0
+1 126 127 1 1 128 129 0
+1 1 1 130 1 1 1 0
+dumping re-decoded V.110 frame:
+E-bits: 00 00 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
+
+======= User data rate 14400
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
+dumping 36 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 1 1 0
+1 111 112 1 1 113 114 0
+1 1 1 115 116 117 118 0
+1 1 0 1 0 0 0 0
+1 119 120 121 122 123 124 0
+1 125 126 127 128 1 1 0
+1 129 130 1 1 131 132 0
+1 1 1 133 134 135 136 0
+dumping re-decoded V.110 frame:
+E-bits: 01 00 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
+
+======= User data rate 19200
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+dumping 48 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 111 112 0
+1 113 114 115 116 117 118 0
+1 119 120 121 122 123 124 0
+1 0 1 1 0 0 0 0
+1 125 126 127 128 129 130 0
+1 131 132 133 134 135 136 0
+1 137 138 139 140 141 142 0
+1 143 144 145 146 147 148 0
+dumping re-decoded V.110 frame:
+E-bits: 00 01 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+
+======= User data rate 24000
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
+dumping 30 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 1 1 0
+1 111 112 1 1 113 114 0
+1 1 1 115 1 1 1 0
+1 0 0 1 0 0 0 0
+1 116 117 118 119 120 121 0
+1 122 123 124 125 1 1 0
+1 126 127 1 1 128 129 0
+1 1 1 130 1 1 1 0
+dumping re-decoded V.110 frame:
+E-bits: 00 00 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
+
+======= User data rate 28800
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
+dumping 36 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 1 1 0
+1 111 112 1 1 113 114 0
+1 1 1 115 116 117 118 0
+1 1 0 1 0 0 0 0
+1 119 120 121 122 123 124 0
+1 125 126 127 128 1 1 0
+1 129 130 1 1 131 132 0
+1 1 1 133 134 135 136 0
+dumping re-decoded V.110 frame:
+E-bits: 01 00 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
+
+======= User data rate 38400
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+dumping 48 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 111 112 0
+1 113 114 115 116 117 118 0
+1 119 120 121 122 123 124 0
+1 0 1 1 0 0 0 0
+1 125 126 127 128 129 130 0
+1 131 132 133 134 135 136 0
+1 137 138 139 140 141 142 0
+1 143 144 145 146 147 148 0
+dumping re-decoded V.110 frame:
+E-bits: 00 01 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
diff --git a/tests/v110/ta_test.c b/tests/v110/ta_test.c
new file mode 100644
index 00000000..833830ef
--- /dev/null
+++ b/tests/v110/ta_test.c
@@ -0,0 +1,459 @@
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/application.h>
+
+#include <osmocom/isdn/v110.h>
+#include <osmocom/isdn/v110_ta.h>
+
+static void *test_ctx = NULL;
+
+/* inverse logic: ON = binary 0; OFF = binary 1 */
+#define V110_SX_BIT_ON 0
+#define V110_SX_BIT_OFF 1
+
+/*********************************************************************************
+ * V.110 TA configuration and callbacks
+ *********************************************************************************/
+
+static void v110_ta_test_rx_cb(void *priv, const ubit_t *buf, size_t buf_size)
+{
+ fprintf(stderr, "%s(buf_size=%zu): %s\n",
+ __func__, buf_size, osmo_ubit_dump(buf, buf_size));
+}
+
+static void v110_ta_test_tx_cb(void *priv, ubit_t *buf, size_t buf_size)
+{
+ for (size_t i = 0; i < buf_size; i++)
+ buf[i] = (i & 1);
+ fprintf(stderr, "%s(buf_size=%zu): %s\n",
+ __func__, buf_size, osmo_ubit_dump(buf, buf_size));
+}
+
+static void v110_ta_test_status_update_cb(void *priv, unsigned int status)
+{
+ fprintf(stderr, "%s(status=0x%08x)\n", __func__, status);
+}
+
+static const struct osmo_v110_ta_cfg v110_ta_test_cfg = {
+ .rate = OSMO_V110_SYNC_RA1_9600,
+ .rx_cb = &v110_ta_test_rx_cb,
+ .tx_cb = &v110_ta_test_tx_cb,
+ .status_update_cb = &v110_ta_test_status_update_cb,
+};
+
+/*********************************************************************************
+ * various helper functions
+ *********************************************************************************/
+
+static void v110_ta_test_init_df(struct osmo_v110_decoded_frame *df)
+{
+ /* quickly set all the bits to binary '1' */
+ memset(df, 1, sizeof(*df));
+ /* D-bits: 0101... pattern */
+ for (unsigned int i = 0; i < MAX_D_BITS; i += 2)
+ df->d_bits[i] = 0;
+ /* E-bits: E1/E2/E3 indicate 9600 bps */
+ df->e_bits[0] = 0;
+}
+
+static void v110_ta_test_dump_df(const struct osmo_v110_decoded_frame *df)
+{
+ fprintf(stderr, " D-bits: %s\n", osmo_ubit_dump(&df->d_bits[0], MAX_D_BITS));
+ fprintf(stderr, " E-bits: %s\n", osmo_ubit_dump(&df->e_bits[0], MAX_E_BITS));
+ fprintf(stderr, " S-bits: %s\n", osmo_ubit_dump(&df->s_bits[0], MAX_S_BITS));
+ fprintf(stderr, " X-bits: %s\n", osmo_ubit_dump(&df->x_bits[0], MAX_X_BITS));
+}
+
+static void v110_ta_test_dump_circuit(const struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_circuit circuit,
+ bool exp_state)
+{
+ bool state = osmo_v110_ta_get_circuit(ta, circuit);
+
+ fprintf(stderr, "circuit %s (%s) is %s (expected to be %s)\n",
+ osmo_v110_ta_circuit_name(circuit),
+ osmo_v110_ta_circuit_desc(circuit),
+ state ? "ON" : "OFF",
+ exp_state ? "ON" : "OFF");
+}
+
+static void v110_ta_test_set_circuit(struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_circuit circuit,
+ bool active)
+{
+ int rc;
+
+ fprintf(stderr, "setting circuit %s (%s) %s\n",
+ osmo_v110_ta_circuit_name(circuit),
+ osmo_v110_ta_circuit_desc(circuit),
+ active ? "ON" : "OFF");
+
+ rc = osmo_v110_ta_set_circuit(ta, circuit, active);
+ fprintf(stderr, "osmo_v110_ta_set_circuit() returns %d\n", rc);
+}
+
+/*********************************************************************************
+ * the actual tests
+ *********************************************************************************/
+
+static void test_idle_ready(void)
+{
+ struct osmo_v110_decoded_frame df = { 0 };
+ struct osmo_v110_ta *ta;
+ int rc;
+
+ fprintf(stderr, "\n==== Running %s()\n", __func__);
+
+ ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
+ OSMO_ASSERT(ta != NULL);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ fprintf(stderr, "Initial status: 0x%08x\n", osmo_v110_ta_get_status(ta));
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, false);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): all bits set to binary '1'\n");
+ memset(&df, 1, sizeof(df));
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): expecting all bits set to binary '1'\n");
+ rc = osmo_v110_ta_frame_out(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, true);
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, false);
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, true);
+
+ osmo_v110_ta_free(ta);
+}
+
+static void test_conn_ta_line(void)
+{
+ struct osmo_v110_decoded_frame df = { 0 };
+ struct osmo_v110_ta *ta;
+ int rc;
+
+ fprintf(stderr, "\n==== Running %s()\n", __func__);
+
+ ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
+ OSMO_ASSERT(ta != NULL);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, true);
+
+ /* we expect the TA FSM to be in V110_TA_ST_CON_TA_TO_LINE */
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): S-/X-bits are expected to be 1 (OFF)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): D-/E-bits are all expected to be 1\n");
+ rc = osmo_v110_ta_frame_out(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ /* TODO: test implicit sync by sending V110_TA_EV_RX_FRAME_IND */
+
+ fprintf(stderr, "osmo_v110_ta_sync_ind(): the lower layer indicates sync event\n");
+ osmo_v110_ta_sync_ind(ta);
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): S-/X-bits are expected to be 0 (ON)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): D-/E-bits are all expected to be 1\n");
+ rc = osmo_v110_ta_frame_out(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): S-/X-bits are OFF, expect no state change\n");
+ v110_ta_test_init_df(&df);
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change\n");
+ memset(&df.s_bits[0], V110_SX_BIT_ON, sizeof(df.s_bits));
+ memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+
+ osmo_v110_ta_free(ta);
+}
+
+static void _test_data_transfer_enter(struct osmo_v110_ta *ta)
+{
+ struct osmo_v110_decoded_frame df;
+ int rc;
+
+ OSMO_ASSERT(osmo_v110_ta_get_circuit(ta, OSMO_V110_TA_C_108) == false);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, true);
+
+ /* we expect the TA FSM to be in V110_TA_ST_CON_TA_TO_LINE */
+
+ fprintf(stderr, "osmo_v110_ta_sync_ind(): the lower layer indicates sync event\n");
+ osmo_v110_ta_sync_ind(ta);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change\n");
+ v110_ta_test_init_df(&df);
+ memset(&df.s_bits[0], V110_SX_BIT_ON, sizeof(df.s_bits));
+ memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+}
+
+static void test_data_transfer(void)
+{
+ struct osmo_v110_decoded_frame df = { 0 };
+ struct osmo_v110_ta *ta;
+ int rc;
+
+ fprintf(stderr, "\n==== Running %s()\n", __func__);
+
+ ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
+ OSMO_ASSERT(ta != NULL);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ _test_data_transfer_enter(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, true);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, true);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, true);
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): S-/X-bits are expected to be 0 (ON)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): E1..E3-bits are expected to be 011 (9600)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): we also expect the .tx_cb() to be called\n");
+ rc = osmo_v110_ta_frame_out(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): feed that frame that we pulled out back into the TA\n");
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ osmo_v110_ta_free(ta);
+}
+
+static void test_data_transfer_disc_local(void)
+{
+ struct osmo_v110_decoded_frame df = { 0 };
+ struct osmo_v110_ta *ta;
+ int rc;
+
+ fprintf(stderr, "\n==== Running %s()\n", __func__);
+
+ ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
+ OSMO_ASSERT(ta != NULL);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ _test_data_transfer_enter(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+
+ fprintf(stderr, "local TE initiates disconnection\n");
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, false);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DISCONNECTING */
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): S-bits are expected to be 1 (OFF)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): X-bits are expected to be 0 (ON)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): D-bits are all expected to be 0\n");
+ rc = osmo_v110_ta_frame_out(ta, &df); /* TODO: what E-bits do we expect? */
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, true);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, true);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): S-/X-bits are ON, expect no state change\n");
+ v110_ta_test_init_df(&df);
+ memset(&df.s_bits[0], V110_SX_BIT_ON, sizeof(df.s_bits));
+ memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): S-bits are OFF, expect state change\n");
+ v110_ta_test_init_df(&df);
+ memset(&df.s_bits[0], V110_SX_BIT_OFF, sizeof(df.s_bits));
+ memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, false);
+
+ osmo_v110_ta_free(ta);
+}
+
+static void test_data_transfer_disc_remote(void)
+{
+ struct osmo_v110_decoded_frame df = { 0 };
+ struct osmo_v110_ta *ta;
+ int rc;
+
+ fprintf(stderr, "\n==== Running %s()\n", __func__);
+
+ ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
+ OSMO_ASSERT(ta != NULL);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ _test_data_transfer_enter(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+
+ fprintf(stderr, "remote TE initiates disconnection\n");
+ fprintf(stderr, "osmo_v110_ta_frame_in(): S-bits are OFF, X-bits are ON\n");
+ fprintf(stderr, "osmo_v110_ta_frame_in(): D-bits are all set to 0\n");
+ v110_ta_test_init_df(&df);
+ memset(&df.s_bits[0], V110_SX_BIT_OFF, sizeof(df.s_bits));
+ memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
+ memset(&df.d_bits[0], 0, sizeof(df.d_bits));
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, false);
+
+ fprintf(stderr, "local TE confirms disconnection\n");
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, false);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DISCONNECTING */
+
+ osmo_v110_ta_desync_ind(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, false);
+
+ osmo_v110_ta_free(ta);
+}
+
+static void test_syncing(void)
+{
+ struct osmo_v110_decoded_frame df = { 0 };
+ struct osmo_v110_ta *ta;
+ int rc;
+
+ fprintf(stderr, "\n==== Running %s()\n", __func__);
+
+ ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
+ OSMO_ASSERT(ta != NULL);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ _test_data_transfer_enter(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+
+ fprintf(stderr, "osmo_v110_ta_sync_ind(): the lower layer indicates out-of-sync event\n");
+ osmo_v110_ta_desync_ind(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_RESYNCING */
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): S-bits are expected to be 0 (ON)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): X-bits are expected to be 1 (OFF)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): D-bits are to be set by .tx_cb()\n");
+ rc = osmo_v110_ta_frame_out(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ fprintf(stderr, "osmo_v110_ta_sync_ind(): the lower layer indicates sync event\n");
+ osmo_v110_ta_sync_ind(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): S-bits are expected to be 0 (ON)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): X-bits are expected to be 0 (ON)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): D-bits are to be set by .tx_cb()\n");
+ rc = osmo_v110_ta_frame_out(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ osmo_v110_ta_free(ta);
+}
+
+int main(int argc, char **argv)
+{
+ test_ctx = talloc_named_const(NULL, 0, __FILE__);
+
+ osmo_init_logging2(test_ctx, NULL);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_level(osmo_stderr_target, 1);
+ log_set_print_category(osmo_stderr_target, 1);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_use_color(osmo_stderr_target, 0);
+
+ osmo_fsm_log_addr(false);
+ osmo_fsm_log_timeouts(true);
+
+ log_set_category_filter(osmo_stderr_target, DLGLOBAL, 1, LOGL_DEBUG);
+
+ test_idle_ready();
+ test_conn_ta_line();
+ /* TODO: test_conn_ta_line_timeout() */
+ test_data_transfer();
+ test_data_transfer_disc_local();
+ test_data_transfer_disc_remote();
+ /* TODO: test_disc_timeout() */
+ test_syncing();
+ /* TODO: test_syncing_timeout() */
+
+ log_fini();
+ OSMO_ASSERT(talloc_total_blocks(test_ctx) == 1);
+ talloc_free(test_ctx);
+
+ return 0;
+}
diff --git a/tests/v110/ta_test.err b/tests/v110/ta_test.err
new file mode 100644
index 00000000..e8f80e62
--- /dev/null
+++ b/tests/v110/ta_test.err
@@ -0,0 +1,270 @@
+
+==== Running test_idle_ready()
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Allocated
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: State change to IDLE_READY (no timeout)
+Initial status: 0x00000000
+circuit 106/CTS (Clear to Send) is OFF (expected to be OFF)
+circuit 107/DSR (Data Set Ready) is OFF (expected to be OFF)
+circuit 109/DCD (Data Carrier Detect) is OFF (expected to be OFF)
+osmo_v110_ta_frame_in(): all bits set to binary '1'
+ D-bits: 111111111111111111111111111111111111111111111111
+ E-bits: 1111111
+ S-bits: 111111111
+ X-bits: 11
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Received Event RX_FRAME_IND
+v110_ta_test_rx_cb(buf_size=48): 111111111111111111111111111111111111111111111111
+osmo_v110_ta_frame_in() returns 0
+osmo_v110_ta_frame_out(): expecting all bits set to binary '1'
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Received Event TX_FRAME_RTS
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 111111111111111111111111111111111111111111111111
+ E-bits: 1111111
+ S-bits: 111111111
+ X-bits: 11
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+setting circuit 108/DTR (Data Terminal Ready) OFF
+DLGLOBAL DEBUG V110-TA(test_idle_ready){CONNECT_TA_TO_LINE}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_idle_ready){CONNECT_TA_TO_LINE}: State change to IDLE_READY (no timeout)
+osmo_v110_ta_set_circuit() returns 0
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+DLGLOBAL DEBUG V110-TA(test_idle_ready){CONNECT_TA_TO_LINE}: Deallocated
+
+==== Running test_conn_ta_line()
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){IDLE_READY}: Allocated
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){IDLE_READY}: State change to IDLE_READY (no timeout)
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+osmo_v110_ta_frame_out(): S-/X-bits are expected to be 1 (OFF)
+osmo_v110_ta_frame_out(): D-/E-bits are all expected to be 1
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event TX_FRAME_RTS
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 111111111111111111111111111111111111111111111111
+ E-bits: 1111111
+ S-bits: 111111111
+ X-bits: 11
+osmo_v110_ta_sync_ind(): the lower layer indicates sync event
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
+osmo_v110_ta_frame_out(): S-/X-bits are expected to be 0 (ON)
+osmo_v110_ta_frame_out(): D-/E-bits are all expected to be 1
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event TX_FRAME_RTS
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 111111111111111111111111111111111111111111111111
+ E-bits: 1111111
+ S-bits: 000000000
+ X-bits: 00
+osmo_v110_ta_frame_in(): S-/X-bits are OFF, expect no state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 111111111
+ X-bits: 11
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
+v110_ta_test_rx_cb(buf_size=48): 111111111111111111111111111111111111111111111111
+osmo_v110_ta_frame_in() returns 0
+osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
+v110_ta_test_status_update_cb(status=0x0000001e)
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){DATA_TRANSFER}: Deallocated
+
+==== Running test_data_transfer()
+DLGLOBAL DEBUG V110-TA(test_data_transfer){IDLE_READY}: Allocated
+DLGLOBAL DEBUG V110-TA(test_data_transfer){IDLE_READY}: State change to IDLE_READY (no timeout)
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_data_transfer){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_data_transfer){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+osmo_v110_ta_sync_ind(): the lower layer indicates sync event
+DLGLOBAL DEBUG V110-TA(test_data_transfer){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
+osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_data_transfer){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
+v110_ta_test_status_update_cb(status=0x0000001e)
+DLGLOBAL DEBUG V110-TA(test_data_transfer){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+circuit 106/CTS (Clear to Send) is ON (expected to be ON)
+circuit 107/DSR (Data Set Ready) is ON (expected to be ON)
+circuit 109/DCD (Data Carrier Detect) is ON (expected to be ON)
+osmo_v110_ta_frame_out(): S-/X-bits are expected to be 0 (ON)
+osmo_v110_ta_frame_out(): E1..E3-bits are expected to be 011 (9600)
+osmo_v110_ta_frame_out(): we also expect the .tx_cb() to be called
+DLGLOBAL DEBUG V110-TA(test_data_transfer){DATA_TRANSFER}: Received Event TX_FRAME_RTS
+v110_ta_test_tx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+osmo_v110_ta_frame_in(): feed that frame that we pulled out back into the TA
+DLGLOBAL DEBUG V110-TA(test_data_transfer){DATA_TRANSFER}: Received Event RX_FRAME_IND
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+DLGLOBAL DEBUG V110-TA(test_data_transfer){DATA_TRANSFER}: Deallocated
+
+==== Running test_data_transfer_disc_local()
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: Allocated
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: State change to IDLE_READY (no timeout)
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+osmo_v110_ta_sync_ind(): the lower layer indicates sync event
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
+osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
+v110_ta_test_status_update_cb(status=0x0000001e)
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+local TE initiates disconnection
+setting circuit 108/DTR (Data Terminal Ready) OFF
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DATA_TRANSFER}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DATA_TRANSFER}: State change to DISCONNECTING (T2, 5s)
+v110_ta_test_status_update_cb(status=0x00000014)
+osmo_v110_ta_set_circuit() returns 0
+osmo_v110_ta_frame_out(): S-bits are expected to be 1 (OFF)
+osmo_v110_ta_frame_out(): X-bits are expected to be 0 (ON)
+osmo_v110_ta_frame_out(): D-bits are all expected to be 0
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DISCONNECTING}: Received Event TX_FRAME_RTS
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 000000000000000000000000000000000000000000000000
+ E-bits: 1111111
+ S-bits: 111111111
+ X-bits: 00
+circuit 106/CTS (Clear to Send) is OFF (expected to be OFF)
+circuit 107/DSR (Data Set Ready) is ON (expected to be ON)
+circuit 109/DCD (Data Carrier Detect) is ON (expected to be ON)
+osmo_v110_ta_frame_in(): S-/X-bits are ON, expect no state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DISCONNECTING}: Received Event RX_FRAME_IND
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+osmo_v110_ta_frame_in(): S-bits are OFF, expect state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 111111111
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DISCONNECTING}: Received Event RX_FRAME_IND
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DISCONNECTING}: State change to IDLE_READY (no timeout)
+v110_ta_test_status_update_cb(status=0x00000000)
+v110_ta_test_rx_cb(buf_size=48): 111111111111111111111111111111111111111111111111
+osmo_v110_ta_frame_in() returns 0
+circuit 106/CTS (Clear to Send) is OFF (expected to be OFF)
+circuit 107/DSR (Data Set Ready) is OFF (expected to be OFF)
+circuit 109/DCD (Data Carrier Detect) is OFF (expected to be OFF)
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: Deallocated
+
+==== Running test_data_transfer_disc_remote()
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: Allocated
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: State change to IDLE_READY (no timeout)
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+osmo_v110_ta_sync_ind(): the lower layer indicates sync event
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
+osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
+v110_ta_test_status_update_cb(status=0x0000001e)
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+remote TE initiates disconnection
+osmo_v110_ta_frame_in(): S-bits are OFF, X-bits are ON
+osmo_v110_ta_frame_in(): D-bits are all set to 0
+ D-bits: 000000000000000000000000000000000000000000000000
+ E-bits: 0111111
+ S-bits: 111111111
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DATA_TRANSFER}: Received Event RX_FRAME_IND
+v110_ta_test_status_update_cb(status=0x0000000a)
+osmo_v110_ta_frame_in() returns 0
+circuit 107/DSR (Data Set Ready) is OFF (expected to be OFF)
+circuit 109/DCD (Data Carrier Detect) is OFF (expected to be OFF)
+local TE confirms disconnection
+setting circuit 108/DTR (Data Terminal Ready) OFF
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DATA_TRANSFER}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DATA_TRANSFER}: State change to DISCONNECTING (T2, 5s)
+v110_ta_test_status_update_cb(status=0x00000000)
+osmo_v110_ta_set_circuit() returns 0
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DISCONNECTING}: Received Event DESYNC_IND
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DISCONNECTING}: State change to IDLE_READY (no timeout)
+circuit 106/CTS (Clear to Send) is OFF (expected to be OFF)
+circuit 107/DSR (Data Set Ready) is OFF (expected to be OFF)
+circuit 109/DCD (Data Carrier Detect) is OFF (expected to be OFF)
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: Deallocated
+
+==== Running test_syncing()
+DLGLOBAL DEBUG V110-TA(test_syncing){IDLE_READY}: Allocated
+DLGLOBAL DEBUG V110-TA(test_syncing){IDLE_READY}: State change to IDLE_READY (no timeout)
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_syncing){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_syncing){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+osmo_v110_ta_sync_ind(): the lower layer indicates sync event
+DLGLOBAL DEBUG V110-TA(test_syncing){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
+osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_syncing){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
+v110_ta_test_status_update_cb(status=0x0000001e)
+DLGLOBAL DEBUG V110-TA(test_syncing){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+osmo_v110_ta_sync_ind(): the lower layer indicates out-of-sync event
+DLGLOBAL DEBUG V110-TA(test_syncing){DATA_TRANSFER}: Received Event DESYNC_IND
+DLGLOBAL DEBUG V110-TA(test_syncing){DATA_TRANSFER}: State change to RESYNCING (X1, 3s)
+osmo_v110_ta_frame_out(): S-bits are expected to be 0 (ON)
+osmo_v110_ta_frame_out(): X-bits are expected to be 1 (OFF)
+osmo_v110_ta_frame_out(): D-bits are to be set by .tx_cb()
+DLGLOBAL DEBUG V110-TA(test_syncing){RESYNCING}: Received Event TX_FRAME_RTS
+v110_ta_test_tx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 11
+osmo_v110_ta_sync_ind(): the lower layer indicates sync event
+DLGLOBAL DEBUG V110-TA(test_syncing){RESYNCING}: Received Event SYNC_IND
+DLGLOBAL DEBUG V110-TA(test_syncing){RESYNCING}: State change to DATA_TRANSFER (no timeout)
+osmo_v110_ta_frame_out(): S-bits are expected to be 0 (ON)
+osmo_v110_ta_frame_out(): X-bits are expected to be 0 (ON)
+osmo_v110_ta_frame_out(): D-bits are to be set by .tx_cb()
+DLGLOBAL DEBUG V110-TA(test_syncing){DATA_TRANSFER}: Received Event TX_FRAME_RTS
+v110_ta_test_tx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_syncing){DATA_TRANSFER}: Deallocated
diff --git a/tests/vty/vty_test.c b/tests/vty/vty_test.c
index 1db0d5ce..01e323ed 100644
--- a/tests/vty/vty_test.c
+++ b/tests/vty/vty_test.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -452,7 +448,35 @@ DEFUN(cfg_numeric_range, cfg_numeric_range_cmd,
return CMD_SUCCESS;
}
-void test_vty_add_cmds()
+DEFUN(cfg_range_base10, cfg_range_base10_cmd,
+ "range-base10 <0-999999>",
+ "testing decimal range\n"
+ "the decimal range\n")
+{
+ printf("Called: 'return-success'\n");
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_range_base16, cfg_range_base16_cmd,
+ "range-base16 <0x0-0x8888>",
+ "testing hexadecimal range\n"
+ "the hexadecimal range\n")
+{
+ printf("Called: 'return-success'\n");
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_range_baseboth, cfg_range_baseboth_cmd,
+ "range-baseboth (<0-999999>|<0x0-0x8888>)",
+ "testing both ranges\n"
+ "the decimal range\n"
+ "the hexadecimal range\n")
+{
+ printf("Called: 'return-success'\n");
+ return CMD_SUCCESS;
+}
+
+void test_vty_add_cmds(void)
{
install_element(CONFIG_NODE, &cfg_ret_warning_cmd);
install_element(CONFIG_NODE, &cfg_ret_success_cmd);
@@ -477,9 +501,13 @@ void test_vty_add_cmds()
install_element_ve(&cfg_ambiguous_str_2_cmd);
install_element_ve(&cfg_numeric_range_cmd);
+
+ install_element_ve(&cfg_range_base10_cmd);
+ install_element_ve(&cfg_range_base16_cmd);
+ install_element_ve(&cfg_range_baseboth_cmd);
}
-void test_is_cmd_ambiguous()
+void test_is_cmd_ambiguous(void)
{
struct vty *vty;
struct vty_test test;
@@ -498,7 +526,7 @@ void test_is_cmd_ambiguous()
destroy_test_vty(&test, vty);
}
-void test_numeric_range()
+void test_numeric_range(void)
{
struct vty *vty;
struct vty_test test;
@@ -513,6 +541,40 @@ void test_numeric_range()
destroy_test_vty(&test, vty);
}
+void test_ranges(void)
+{
+ struct vty *vty;
+ struct vty_test test;
+
+ printf("Going to test test_ranges()\n");
+ vty = create_test_vty(&test);
+
+ printf("test range-base10\n");
+ OSMO_ASSERT(do_vty_command(vty, "range-base10 0") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-base10 40000") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-base10 -400000") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-base10 0x0") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-base10 0x343") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-base10 -0x343") == CMD_ERR_NO_MATCH);
+
+ printf("test range-base16\n");
+ OSMO_ASSERT(do_vty_command(vty, "range-base16 0") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-base16 40000") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-base16 -400000") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-base16 0x0") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-base16 0x343") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-base16 -0x343") == CMD_ERR_NO_MATCH);
+
+ printf("test range-baseboth\n");
+ OSMO_ASSERT(do_vty_command(vty, "range-baseboth 0") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-baseboth 40000") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-baseboth -400000") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-baseboth 0x0") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-baseboth 0x343") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-baseboth -0x343") == CMD_ERR_NO_MATCH);
+
+ destroy_test_vty(&test, vty);
+}
/* Application specific attributes */
enum vty_test_attr {
VTY_TEST_ATTR_FOO = 0,
@@ -542,9 +604,9 @@ int main(int argc, char **argv)
/* Reserved for libraries */
[VTY_TEST_ATTR_UPPER] = 'X',
/* Reserved for global attribues */
- [VTY_TEST_ATTR_RAFC_DOT] '.',
- [VTY_TEST_ATTR_RAFC_EXCL] '!',
- [VTY_TEST_ATTR_RAFC_AT] '@',
+ [VTY_TEST_ATTR_RAFC_DOT] = '.',
+ [VTY_TEST_ATTR_RAFC_EXCL] = '!',
+ [VTY_TEST_ATTR_RAFC_AT] = '@',
},
};
@@ -595,6 +657,7 @@ int main(int argc, char **argv)
test_is_cmd_ambiguous();
test_numeric_range();
+ test_ranges();
/* Leak check */
OSMO_ASSERT(talloc_total_blocks(stats_ctx) == 1);
diff --git a/tests/vty/vty_test.err b/tests/vty/vty_test.err
index 1cb4190c..b021425d 100644
--- a/tests/vty/vty_test.err
+++ b/tests/vty/vty_test.err
@@ -65,3 +65,13 @@ Got VTY event: 2
Got VTY event: 2
Got VTY event: 1
Got VTY event: 3
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 1
+Got VTY event: 3
diff --git a/tests/vty/vty_test.ok b/tests/vty/vty_test.ok
index 5f509f65..e97fbfc4 100644
--- a/tests/vty/vty_test.ok
+++ b/tests/vty/vty_test.ok
@@ -320,4 +320,52 @@ Called: 'return-success'
Returned: 0, Current node: 1 '%s> '
Going to execute 'numeric-range -400000'
Returned: 2, Current node: 1 '%s> '
+Going to test test_ranges()
+test range-base10
+Going to execute 'range-base10 0'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-base10 40000'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-base10 -400000'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-base10 0x0'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-base10 0x343'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-base10 -0x343'
+Returned: 2, Current node: 1 '%s> '
+test range-base16
+Going to execute 'range-base16 0'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-base16 40000'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-base16 -400000'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-base16 0x0'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-base16 0x343'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-base16 -0x343'
+Returned: 2, Current node: 1 '%s> '
+test range-baseboth
+Going to execute 'range-baseboth 0'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-baseboth 40000'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-baseboth -400000'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-baseboth 0x0'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-baseboth 0x343'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-baseboth -0x343'
+Returned: 2, Current node: 1 '%s> '
All tests passed
diff --git a/tests/vty/vty_transcript_test.c b/tests/vty/vty_transcript_test.c
index c9ecf186..5602c505 100644
--- a/tests/vty/vty_transcript_test.c
+++ b/tests/vty/vty_transcript_test.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
@@ -41,7 +37,7 @@
void *root_ctx = NULL;
-static void print_help()
+static void print_help(void)
{
printf( "options:\n"
" -h --help this text\n"
@@ -215,6 +211,9 @@ DEFUN(multi2, multi2_cmd,
enum {
ATTR_TEST_NODE = _LAST_OSMOVTY_NODE + 1,
+ NEST_A_NODE,
+ NEST_B_NODE,
+ NEST_C_NODE,
};
static struct cmd_node attr_test_node = {
@@ -319,7 +318,63 @@ DEFUN_ATTR_USRATTR(cfg_attr_hidden_app_attr_unbelievable,
return CMD_SUCCESS;
}
-static void init_vty_cmds()
+static struct cmd_node nest_a_node = {
+ NEST_A_NODE,
+ "%s(config-a)# ",
+ 1
+};
+
+static struct cmd_node nest_b_node = {
+ NEST_B_NODE,
+ "%s(config-b)# ",
+ 1
+};
+
+static struct cmd_node nest_c_node = {
+ NEST_C_NODE,
+ "%s(config-c)# ",
+ 1
+};
+
+DEFUN(cfg_nest_a, cfg_nest_a_cmd,
+ "nest NAME",
+ "Enter nest level a\n"
+ "Set a name to mark the node's state\n")
+{
+ vty->index = talloc_strdup(root_ctx, argv[0]);
+ vty->node = NEST_A_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nest_b, cfg_nest_b_cmd,
+ "nest NAME",
+ "Enter nest level b\n"
+ "Set a name to mark the node's state\n")
+{
+ vty->index = talloc_strdup(root_ctx, argv[0]);
+ vty->node = NEST_B_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nest_c, cfg_nest_c_cmd,
+ "nest NAME",
+ "Enter nest level c\n"
+ "Set a name to mark the node's state\n")
+{
+ vty->index = talloc_strdup(root_ctx, argv[0]);
+ vty->node = NEST_C_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nest_state, cfg_nest_state_cmd,
+ "state",
+ "Show this node's mark\n")
+{
+ vty_out(vty, "%s%s", (const char *)vty->index, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+static void init_vty_cmds(void)
{
install_element_ve(&single0_cmd);
install_element_ve(&multi0_cmd);
@@ -340,6 +395,17 @@ static void init_vty_cmds()
install_element(ATTR_TEST_NODE, &cfg_app_attr_unbelievable_magnificent_cmd);
install_element(ATTR_TEST_NODE, &cfg_app_attr_unbelievable_wonderful_cmd);
install_element(ATTR_TEST_NODE, &cfg_attr_hidden_app_attr_unbelievable_cmd);
+
+ install_element(CONFIG_NODE, &cfg_nest_a_cmd);
+ install_node(&nest_a_node, NULL);
+ install_element(NEST_A_NODE, &cfg_nest_b_cmd);
+ install_node(&nest_b_node, NULL);
+ install_element(NEST_B_NODE, &cfg_nest_c_cmd);
+ install_node(&nest_c_node, NULL);
+
+ install_element(NEST_A_NODE, &cfg_nest_state_cmd);
+ install_element(NEST_B_NODE, &cfg_nest_state_cmd);
+ install_element(NEST_C_NODE, &cfg_nest_state_cmd);
}
int main(int argc, char **argv)
@@ -365,7 +431,7 @@ int main(int argc, char **argv)
}
}
- rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);
+ rc = telnet_init_default(root_ctx, NULL, 42042);
if (rc < 0)
return 2;
diff --git a/tests/vty/vty_transcript_test.vty b/tests/vty/vty_transcript_test.vty
index 7b8241eb..7df2a606 100644
--- a/tests/vty/vty_transcript_test.vty
+++ b/tests/vty/vty_transcript_test.vty
@@ -177,3 +177,33 @@ vty_transcript_test(config-attr-test)# foo-hidden ?
[expert-mode] But can be seen in the expert mode
vty_transcript_test(config-attr-test)# app-hidden-unbelievable?
app-hidden-unbelievable Hidden, but still unbelievable help message
+
+vty_transcript_test(config-attr-test)# exit
+
+vty_transcript_test(config)# nest A
+vty_transcript_test(config-a)# state
+A
+vty_transcript_test(config-a)# nest B
+vty_transcript_test(config-b)# state
+B
+vty_transcript_test(config-b)# nest C
+vty_transcript_test(config-c)# state
+C
+vty_transcript_test(config-c)# exit
+vty_transcript_test(config-b)# state
+B
+vty_transcript_test(config-b)# exit
+vty_transcript_test(config-a)# state
+A
+vty_transcript_test(config-a)# nest B2
+vty_transcript_test(config-b)# state
+B2
+vty_transcript_test(config-b)# nest C2
+vty_transcript_test(config-c)# state
+C2
+vty_transcript_test(config-c)# exit
+vty_transcript_test(config-b)# state
+B2
+vty_transcript_test(config-b)# exit
+vty_transcript_test(config-a)# state
+A
diff --git a/tests/write_queue/wqueue_test.c b/tests/write_queue/wqueue_test.c
index 3823ef5b..d4476f16 100644
--- a/tests/write_queue/wqueue_test.c
+++ b/tests/write_queue/wqueue_test.c
@@ -1,3 +1,14 @@
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH.
+ * Authors: Holger Hans Peter Freyther
+ * Alexander Rehbein
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/write_queue.h>
@@ -15,6 +26,7 @@ static void test_wqueue_limit(void)
struct msgb *msg;
struct osmo_wqueue wqueue;
int rc;
+ size_t dropped_msgs;
osmo_wqueue_init(&wqueue, 0);
OSMO_ASSERT(wqueue.max_length == 0);
@@ -63,6 +75,46 @@ static void test_wqueue_limit(void)
OSMO_ASSERT(wqueue.current_length == 2);
msgb_free(msg);
osmo_wqueue_clear(&wqueue);
+
+ /* Update limit */
+ OSMO_ASSERT(osmo_wqueue_set_maxlen(&wqueue, 5) == 0);
+ OSMO_ASSERT(osmo_wqueue_set_maxlen(&wqueue, 1) == 0);
+ OSMO_ASSERT(osmo_wqueue_set_maxlen(&wqueue, 4) == 0);
+
+ /* Add three, update limit to 1 */
+ OSMO_ASSERT(wqueue.max_length == 4);
+ msg = msgb_alloc(4096, "msg6");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(wqueue.current_length == 1);
+ msg = msgb_alloc(4096, "msg7");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(wqueue.current_length == 2);
+ msg = msgb_alloc(4096, "msg8");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(wqueue.current_length == 3);
+ dropped_msgs = osmo_wqueue_set_maxlen(&wqueue, 1);
+ OSMO_ASSERT(dropped_msgs == 2);
+ osmo_wqueue_clear(&wqueue);
+
+ /* Add three, reduce limit to 3 from 6 */
+ OSMO_ASSERT(osmo_wqueue_set_maxlen(&wqueue, 6) == 0);
+ OSMO_ASSERT(wqueue.max_length == 6);
+ msg = msgb_alloc(4096, "msg9");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(wqueue.current_length == 1);
+ msg = msgb_alloc(4096, "msg10");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(wqueue.current_length == 2);
+ msg = msgb_alloc(4096, "msg11");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(wqueue.current_length == 3);
+ dropped_msgs = osmo_wqueue_set_maxlen(&wqueue, 3);
+ OSMO_ASSERT(dropped_msgs == 0);
+ osmo_wqueue_clear(&wqueue);
}
int main(int argc, char **argv)
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 3f2b13f6..3ec71ea3 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -1,14 +1,14 @@
bin_PROGRAMS =
noinst_PROGRAMS =
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS)
-AM_CFLAGS = -Wall $(PTHREAD_CFLAGS)
-LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la $(PTHREAD_LIBS)
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS)
+LDADD = $(top_builddir)/src/core/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la $(PTHREAD_LIBS)
if ENABLE_UTILITIES
EXTRA_DIST = conv_gen.py conv_codes_gsm.py
-bin_PROGRAMS += osmo-arfcn osmo-auc-gen osmo-config-merge osmo-aka-verify
+bin_PROGRAMS += osmo-arfcn osmo-auc-gen osmo-config-merge osmo-aka-verify osmo-gsmtap-logsend
osmo_arfcn_SOURCES = osmo-arfcn.c
@@ -16,26 +16,31 @@ osmo_auc_gen_SOURCES = osmo-auc-gen.c
osmo_aka_verify_SOURCES = osmo-aka-verify.c
+osmo_gsmtap_logsend_SOURCES = gsmtap-logsend.c
+
osmo_config_merge_SOURCES = osmo-config-merge.c
osmo_config_merge_LDADD = $(LDADD) $(TALLOC_LIBS)
-osmo_config_merge_CFLAGS = $(TALLOC_CFLAGS)
if ENABLE_PCSC
noinst_PROGRAMS += osmo-sim-test
osmo_sim_test_SOURCES = osmo-sim-test.c
osmo_sim_test_LDADD = $(LDADD) $(top_builddir)/src/sim/libosmosim.la $(PCSC_LIBS)
-osmo_sim_test_CFLAGS = $(PCSC_CFLAGS)
+osmo_sim_test_CFLAGS = $(AM_CFLAGS) $(PCSC_CFLAGS)
endif
endif
if ENABLE_EXT_TESTS
+SUBDIRS = \
+ osmo-stat-dummy \
+ $(NULL)
+endif
+
if ENABLE_GB
noinst_PROGRAMS += osmo-ns-dummy
osmo_ns_dummy_SOURCES = osmo-ns-dummy.c osmo-ns-dummy-vty.c
osmo_ns_dummy_LDADD = $(LDADD) $(TALLOC_LIBS) \
$(top_builddir)/src/gb/libosmogb.la \
$(top_builddir)/src/vty/libosmovty.la \
- $(top_builddir)/src/gsm/libosmogsm.la
-osmo_ns_dummy_CFLAGS = $(TALLOC_CFLAGS)
-endif
+ $(top_builddir)/src/ctrl/libosmoctrl.la \
+ $(NULL)
endif
diff --git a/utils/conv_codes_gsm.py b/utils/conv_codes_gsm.py
index 42f340b9..7f621b8d 100644
--- a/utils/conv_codes_gsm.py
+++ b/utils/conv_codes_gsm.py
@@ -42,6 +42,111 @@ conv_codes = [
]
),
+ # TCH/F2.4 definition
+ ConvolutionalCode(
+ 72,
+ [
+ (G1, 1),
+ (G2, 1),
+ (G3, 1),
+ (G1, 1),
+ (G2, 1),
+ (G3, 1),
+ ],
+ name = "tch_f24",
+ description = [
+ "TCH/F2.4 convolutional code:",
+ "72 bits blocks, rate 1/6, k = 5",
+ "G1 = 1 + D + D3 + D4",
+ "G2 = 1 + D2 + D4",
+ "G3 = 1 + D + D2 + D3 + D4",
+ "G1 = 1 + D + D3 + D4",
+ "G2 = 1 + D2 + D4",
+ "G3 = 1 + D + D2 + D3 + D4",
+ ]
+ ),
+
+ # TCH/H2.4 definition
+ ConvolutionalCode(
+ 72,
+ [
+ (G1, 1),
+ (G2, 1),
+ (G3, 1),
+ ],
+ name = "tch_h24",
+ description = [
+ "TCH/H2.4 convolutional code:",
+ "72 bits blocks, rate 1/3, k = 5",
+ "G1 = 1 + D + D3 + D4",
+ "G2 = 1 + D2 + D4",
+ "G3 = 1 + D + D2 + D3 + D4",
+ ]
+ ),
+
+ # TCH/F4.8 definition
+ ConvolutionalCode(
+ 148,
+ [
+ (G1, 1),
+ (G2, 1),
+ (G3, 1),
+ ],
+ name = "tch_f48",
+ description = [
+ "TCH/F4.8 convolutional code:",
+ "148 bits blocks, rate 1/3, k = 5",
+ "G1 = 1 + D + D3 + D4",
+ "G2 = 1 + D2 + D4",
+ "G3 = 1 + D + D2 + D3 + D4",
+ ]
+ ),
+
+ # TCH/F9.6 definition
+ ConvolutionalCode(
+ 240,
+ shared_polys["xcch"],
+ puncture = [
+ 11, 26, 41, 56, 71, 86, 101, 116, 131, 146, 161, 176,
+ 191, 206, 221, 236, 251, 266, 281, 296, 311, 326, 341, 356,
+ 371, 386, 401, 416, 431, 446, 461, 476, -1
+ ],
+ name = "tch_f96",
+ description = [
+ "TCH/F9.6 convolutional code:",
+ "240 bits blocks, rate 1/2, k = 5",
+ "G0 = 1 + D3 + D4",
+ "G1 = 1 + D + D3 + D4",
+ ]
+ ),
+
+ # TCH/F14.4 definition
+ ConvolutionalCode(
+ 290,
+ shared_polys["xcch"],
+ puncture = [
+ 1, 6, 11, 15, 19, 24, 29, 33, 37, 42, 47, 51,
+ 55, 60, 65, 69, 73, 78, 83, 87, 91, 96, 101, 105,
+ 109, 114, 119, 123, 127, 132, 137, 141, 145, 150, 155, 159,
+ 163, 168, 173, 177, 181, 186, 191, 195, 199, 204, 209, 213,
+ 217, 222, 227, 231, 235, 240, 245, 249, 253, 258, 263, 267,
+ 271, 276, 281, 285, 289, 294, 299, 303, 307, 312, 317, 321,
+ 325, 330, 335, 339, 343, 348, 353, 357, 361, 366, 371, 375,
+ 379, 384, 389, 393, 397, 402, 407, 411, 415, 420, 425, 429,
+ 433, 438, 443, 447, 451, 456, 461, 465, 469, 474, 479, 483,
+ 487, 492, 497, 501, 505, 510, 515, 519, 523, 528, 533, 537,
+ 541, 546, 551, 555, 559, 564, 569, 573, 577, 582, 584, 587,
+ -1
+ ],
+ name = "tch_f144",
+ description = [
+ "TCH/F14.4 convolutional code:",
+ "290 bits blocks, rate 1/2, k = 5",
+ "G0 = 1 + D3 + D4",
+ "G1 = 1 + D + D3 + D4",
+ ]
+ ),
+
# RACH definition
ConvolutionalCode(
14,
@@ -50,11 +155,11 @@ conv_codes = [
description = ["RACH convolutional code"]
),
- # Extended RACH definition from 3GPP TS 45.003 §5.3.2
+ # Extended RACH definition from 3GPP TS 45.003 §5.3.2
ConvolutionalCode(
17,
shared_polys["xcch"],
- puncture = [ 0, 2, 5, 37, 39, 41, -1 ],
+ puncture = [ 0, 2, 5, 37, 39, 41, -1 ],
name = "rach_ext",
description = ["Extended RACH (11 bit) convolutional code"]
),
diff --git a/utils/conv_gen.py b/utils/conv_gen.py
index d2eda152..27ffe625 100644
--- a/utils/conv_gen.py
+++ b/utils/conv_gen.py
@@ -16,10 +16,6 @@ mod_license = """
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
"""
diff --git a/utils/gsmtap-logsend.c b/utils/gsmtap-logsend.c
new file mode 100644
index 00000000..97a838b5
--- /dev/null
+++ b/utils/gsmtap-logsend.c
@@ -0,0 +1,139 @@
+/* Small program to read an input file / stdin and send each line via GSMTAP logging */
+/* (C) 2023 by Harald Welte <laforge@osmocom.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <getopt.h>
+
+#include <osmocom/core/byteswap.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/gsmtap.h>
+#include <osmocom/core/gsmtap_util.h>
+
+static char *proc_name = "gsmtap-logsend";
+static char *subsys_name = "unknown";
+static char *dest_host = "localhost";
+static int dest_port = GSMTAP_UDP_PORT;
+
+static void help(void)
+{
+ printf("osmo-gsmtap-logsend Usage:\n"
+ "\t[ -r DESTADDR ] [ -p PORTNR ] [ -n PROC_NAME ] [ -s SUBSYS ] [ INFILE ]\n"
+ "\n"
+ " -a --remote-address HOSTNAME Destination IP destination address (default: localhost)\n"
+ " -p --remote-port PORTNR Destination UDP Port number (default: 4729)\n"
+ " -n --process-name PROC_NAME Process name to include in GSMTAP LOG header\n"
+ " -s --subsys-name SUBSYS Subsystem name to include in GSMTAP LOG header\n"
+ " -h --help This help message\n"
+ );
+}
+
+int main(int argc, char **argv)
+{
+ char buf[1024];
+ int gsmtap_fd;
+ FILE *infile;
+ char *line;
+ int rc;
+
+ while (1) {
+ static const struct option long_options[] = {
+ { "remote-address", 1, 0, 'a' },
+ { "remote-port", 1, 0, 'p' },
+ { "process-name", 1, 0, 'n' },
+ { "subsys-name", 1, 0, 's' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+ };
+ int c, option_index;
+
+ c = getopt_long(argc, argv, "a:p:n:s:h", long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'a':
+ dest_host = optarg;
+ break;
+ case 'p':
+ dest_port = atoi(optarg);
+ break;
+ case 'n':
+ proc_name = optarg;
+ break;
+ case 's':
+ subsys_name = optarg;
+ break;
+ case 'h':
+ help();
+ exit(0);
+ break;
+ default:
+ help();
+ exit(1);
+ }
+ }
+
+ if (argc <= optind) {
+ infile = stdin;
+ } else {
+ infile = fopen(argv[optind], "r");
+ if (!infile) {
+ fprintf(stderr, "Unable to open %s: %s\n", argv[optind], strerror(errno));
+ exit(2);
+ }
+ }
+
+ gsmtap_fd = gsmtap_source_init_fd(dest_host, dest_port);
+ if (gsmtap_fd < 0) {
+ fprintf(stderr, "Unable to create GSMTAP soicket: %s\n", strerror(errno));
+ exit(2);
+ }
+
+ /* prepare all the data structures that don't change for each line */
+ struct {
+ struct gsmtap_hdr gsmtap;
+ struct gsmtap_osmocore_log_hdr log;
+ } __attribute__ ((packed)) hdr;
+ struct timeval tv;
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.gsmtap.version = GSMTAP_VERSION;
+ hdr.gsmtap.hdr_len = sizeof(hdr.gsmtap)/4;
+ hdr.gsmtap.type = GSMTAP_TYPE_OSMOCORE_LOG;
+
+ OSMO_STRLCPY_ARRAY(hdr.log.proc_name, proc_name);
+ OSMO_STRLCPY_ARRAY(hdr.log.subsys, subsys_name);
+ hdr.log.level = LOGL_INFO;
+
+ while ((line = fgets(buf, sizeof(buf), infile))) {
+ struct iovec iov[2] = {
+ { .iov_base = &hdr, .iov_len = sizeof(hdr) },
+ { .iov_base = buf, .iov_len = strlen(line) + 1 },
+ };
+ osmo_gettimeofday(&tv, NULL);
+ hdr.log.ts.sec = osmo_htonl(tv.tv_sec);
+ hdr.log.ts.usec = osmo_htonl(tv.tv_usec);
+
+ rc = writev(gsmtap_fd, iov, ARRAY_SIZE(iov));
+ if (rc <= 0) {
+ fprintf(stderr, "Short write on GSMTAP socket: %d (%s)\n", rc, strerror(errno));
+ exit(1);
+ }
+ }
+}
diff --git a/utils/osmo-aka-verify.c b/utils/osmo-aka-verify.c
index 086add55..f23c349b 100644
--- a/utils/osmo-aka-verify.c
+++ b/utils/osmo-aka-verify.c
@@ -88,7 +88,7 @@ static int milenage_check(const uint8_t *opc, const uint8_t *k, const uint8_t *s
}
-static void help()
+static void help(void)
{
printf( "Static SIM card parameters:\n"
"-k --key\tSpecify Ki / K\n"
@@ -123,6 +123,7 @@ static int handle_options(int argc, char **argv)
bool opc_is_set = false;
bool amf_is_set = false;
bool opc_is_op = false;
+ int64_t val64;
while (1) {
int c;
@@ -169,7 +170,8 @@ static int handle_options(int argc, char **argv)
amf_is_set = true;
break;
case 's':
- g_sqn = strtoull(optarg, 0, 10);
+ rc = osmo_str_to_int64(&val64, optarg, 10, 0, INT64_MAX);
+ g_sqn = (unsigned long long)val64;
sqn_is_set = true;
break;
case 'r':
diff --git a/utils/osmo-arfcn.c b/utils/osmo-arfcn.c
index 5f138f8c..3d4fb507 100644
--- a/utils/osmo-arfcn.c
+++ b/utils/osmo-arfcn.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -97,7 +93,7 @@ int main(int argc, char **argv)
{
int arfcn, freq, pcs = 0, uplink = -1;
int opt;
- char *param;
+ char *param = NULL;
enum program_mode mode = MODE_NONE;
while ((opt = getopt(argc, argv, "pa:f:udh")) != -1) {
diff --git a/utils/osmo-auc-gen.c b/utils/osmo-auc-gen.c
index 65cfa310..50419a43 100644
--- a/utils/osmo-auc-gen.c
+++ b/utils/osmo-auc-gen.c
@@ -1,7 +1,7 @@
/*! \file osmo-auc-gen.c
* GSM/GPRS/3G authentication testing tool. */
/*
- * (C) 2010-2012 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010-2023 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -34,8 +30,20 @@
#include <osmocom/crypt/auth.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/base64.h>
#include <osmocom/gsm/gsm_utils.h>
+static void print_base64(const char *fmt, const uint8_t *data, unsigned int len)
+{
+ uint8_t outbuf[256];
+ size_t olen;
+
+ OSMO_ASSERT(osmo_base64_encode(outbuf, sizeof(outbuf), &olen, data, len) == 0);
+ OSMO_ASSERT(sizeof(outbuf) > olen);
+ outbuf[olen] = '\0';
+ printf(fmt, outbuf);
+}
+
static void dump_triplets_dat(struct osmo_auth_vector *vec)
{
if (vec->auth_types & OSMO_AUTH_TYPE_UMTS) {
@@ -53,10 +61,17 @@ static void dump_auth_vec(struct osmo_auth_vector *vec)
printf("RAND:\t%s\n", osmo_hexdump_nospc(vec->rand, sizeof(vec->rand)));
if (vec->auth_types & OSMO_AUTH_TYPE_UMTS) {
+ uint8_t inbuf[sizeof(vec->rand) + sizeof(vec->autn)];
+
printf("AUTN:\t%s\n", osmo_hexdump_nospc(vec->autn, sizeof(vec->autn)));
printf("IK:\t%s\n", osmo_hexdump_nospc(vec->ik, sizeof(vec->ik)));
printf("CK:\t%s\n", osmo_hexdump_nospc(vec->ck, sizeof(vec->ck)));
printf("RES:\t%s\n", osmo_hexdump_nospc(vec->res, vec->res_len));
+
+ memcpy(inbuf, vec->rand, sizeof(vec->rand));
+ memcpy(inbuf + sizeof(vec->rand), vec->autn, sizeof(vec->autn));
+ print_base64("IMS nonce:\t%s\n", inbuf, sizeof(inbuf));
+ print_base64("IMS res:\t%s\n", vec->res, vec->res_len);
}
if (vec->auth_types & OSMO_AUTH_TYPE_GSM) {
@@ -65,12 +80,12 @@ static void dump_auth_vec(struct osmo_auth_vector *vec)
}
}
-static struct osmo_sub_auth_data test_aud = {
+static struct osmo_sub_auth_data2 test_aud = {
.type = OSMO_AUTH_TYPE_NONE,
.algo = OSMO_AUTH_ALG_NONE,
};
-static void help()
+static void help(void)
{
int alg;
printf( "-2 --2g\tUse 2G (GSM) authentication\n"
@@ -83,6 +98,7 @@ static void help()
"-s --sqn\tSpecify SQN (only for 3G)\n"
"-i --ind\tSpecify IND slot for new SQN after AUTS (only for 3G)\n"
"-l --ind-len\tSpecify IND bit length (default=5) (only for 3G)\n"
+ "-L --res-len\tSpecify RES byte length (default=8) (only for 3G)\n"
"-A --auts\tSpecify AUTS (only for 3G)\n"
"-r --rand\tSpecify random value\n"
"-I --ipsec\tOutput in triplets.dat format for strongswan\n");
@@ -108,10 +124,12 @@ int main(int argc, char **argv)
int fmt_triplets_dat = 0;
uint64_t ind_mask = 0;
- printf("osmo-auc-gen (C) 2011-2012 by Harald Welte\n");
+ printf("osmo-auc-gen (C) 2011-2023 by Harald Welte\n");
printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
memset(_auts, 0, sizeof(_auts));
+ memset(vec, 0, sizeof(*vec));
+ vec->res_len = 8; /* default */
while (1) {
int c;
@@ -126,6 +144,7 @@ int main(int argc, char **argv)
{ "sqn", 1, 0, 's' },
{ "ind", 1, 0, 'i' },
{ "ind-len", 1, 0, 'l' },
+ { "res-len", 1, 0, 'L' },
{ "rand", 1, 0, 'r' },
{ "auts", 1, 0, 'A' },
{ "help", 0, 0, 'h' },
@@ -134,7 +153,7 @@ int main(int argc, char **argv)
rc = 0;
- c = getopt_long(argc, argv, "23a:k:o:f:s:i:l:r:hO:A:I", long_options,
+ c = getopt_long(argc, argv, "23a:k:o:f:s:i:l:L:r:hO:A:I", long_options,
&option_index);
if (c == -1)
@@ -159,10 +178,21 @@ int main(int argc, char **argv)
case OSMO_AUTH_TYPE_GSM:
rc = osmo_hexparse(optarg, test_aud.u.gsm.ki,
sizeof(test_aud.u.gsm.ki));
+ if (rc != sizeof(test_aud.u.gsm.ki)) {
+ fprintf(stderr, "Invalid Ki length %d\n", rc);
+ exit(2);
+ }
break;
case OSMO_AUTH_TYPE_UMTS:
rc = osmo_hexparse(optarg, test_aud.u.umts.k,
sizeof(test_aud.u.umts.k));
+ /* 3GPP TS 33.102 6.3.7: "The authentication key (K) shall have a length of
+ * 128 bits or 256 bits." */
+ if (rc != 16 && rc != 32) {
+ fprintf(stderr, "Invalid K length %d\n", rc);
+ exit(2);
+ }
+ test_aud.u.umts.k_len = rc;
break;
default:
fprintf(stderr, "please specify 2g/3g first!\n");
@@ -175,6 +205,11 @@ int main(int argc, char **argv)
}
rc = osmo_hexparse(optarg, test_aud.u.umts.opc,
sizeof(test_aud.u.umts.opc));
+ if (rc != 16 && rc != 32) {
+ fprintf(stderr, "Invalid OPC length %d\n", rc);
+ exit(2);
+ }
+ test_aud.u.umts.opc_len = rc;
test_aud.u.umts.opc_is_op = 0;
break;
case 'O':
@@ -184,6 +219,11 @@ int main(int argc, char **argv)
}
rc = osmo_hexparse(optarg, test_aud.u.umts.opc,
sizeof(test_aud.u.umts.opc));
+ if (rc != 16 && rc != 32) {
+ fprintf(stderr, "Invalid OP length %d\n", rc);
+ exit(2);
+ }
+ test_aud.u.umts.opc_len = rc;
test_aud.u.umts.opc_is_op = 1;
break;
case 'A':
@@ -201,13 +241,17 @@ int main(int argc, char **argv)
}
rc = osmo_hexparse(optarg, test_aud.u.umts.amf,
sizeof(test_aud.u.umts.amf));
+ if (rc != 2) {
+ fprintf(stderr, "Invalid AMF length %d\n", rc);
+ exit(2);
+ }
break;
case 's':
if (test_aud.type != OSMO_AUTH_TYPE_UMTS) {
fprintf(stderr, "Only UMTS has SQN\n");
exit(2);
}
- sqn = strtoull(optarg, 0, 10);
+ sqn = strtoull(optarg, 0, 0);
sqn_is_set = 1;
break;
case 'i':
@@ -225,8 +269,20 @@ int main(int argc, char **argv)
}
test_aud.u.umts.ind_bitlen = atoi(optarg);
break;
+ case 'L':
+ rc = atoi(optarg);
+ if (rc != 4 && rc != 8 && rc != 16) {
+ fprintf(stderr, "Invalid RES length %u\n", rc);
+ exit(2);
+ }
+ vec->res_len = rc;
+ break;
case 'r':
rc = osmo_hexparse(optarg, _rand, sizeof(_rand));
+ if (rc != sizeof(_rand)) {
+ fprintf(stderr, "Invalid RAND length %d\n", rc);
+ exit(2);
+ }
rand_is_set = 1;
break;
case 'I':
@@ -269,8 +325,6 @@ int main(int argc, char **argv)
exit(2);
}
- memset(vec, 0, sizeof(*vec));
-
if (test_aud.type == OSMO_AUTH_TYPE_UMTS) {
uint64_t seq_1 = 1LL << test_aud.u.umts.ind_bitlen;
ind_mask = seq_1 - 1;
@@ -301,9 +355,9 @@ int main(int argc, char **argv)
}
if (!auts_is_set)
- rc = osmo_auth_gen_vec(vec, &test_aud, _rand);
+ rc = osmo_auth_gen_vec2(vec, &test_aud, _rand);
else
- rc = osmo_auth_gen_vec_auts(vec, &test_aud, _auts, _rand, _rand);
+ rc = osmo_auth_gen_vec_auts2(vec, &test_aud, _auts, _rand, _rand);
if (rc < 0) {
if (!auts_is_set)
fprintf(stderr, "error generating auth vector\n");
diff --git a/utils/osmo-config-merge.c b/utils/osmo-config-merge.c
index 477bb028..ed2039ac 100644
--- a/utils/osmo-config-merge.c
+++ b/utils/osmo-config-merge.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*
diff --git a/utils/osmo-ns-dummy-vty.c b/utils/osmo-ns-dummy-vty.c
index f4a270cf..2e59b11b 100644
--- a/utils/osmo-ns-dummy-vty.c
+++ b/utils/osmo-ns-dummy-vty.c
@@ -21,7 +21,6 @@
*
*/
-#include <talloc.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
@@ -30,6 +29,7 @@
#include <inttypes.h>
+#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/application.h>
#include <osmocom/core/stats.h>
diff --git a/utils/osmo-ns-dummy.c b/utils/osmo-ns-dummy.c
index 0af1a897..890444cf 100644
--- a/utils/osmo-ns-dummy.c
+++ b/utils/osmo-ns-dummy.c
@@ -8,7 +8,8 @@
#include <osmocom/core/select.h>
#include <osmocom/core/application.h>
#include <osmocom/core/stats.h>
-
+#include <osmocom/ctrl/control_if.h>
+#include <osmocom/ctrl/control_vty.h>
#include <osmocom/gprs/gprs_ns2.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/telnet_interface.h>
@@ -27,6 +28,7 @@ static bool quit = false;
static bool config_given = false;
static bool daemonize = false;
static int vty_port = 0;
+static int ctrl_port = 0;
static char *config_file = NULL;
struct gprs_ns2_inst *g_nsi;
@@ -43,7 +45,7 @@ static struct vty_app_info vty_info = {
.copyright = vty_copyright,
};
-static void print_help()
+static void print_help(void)
{
printf( "Some useful options:\n"
" -h --help This text\n"
@@ -51,6 +53,7 @@ static void print_help()
" -V --version Print version\n"
" -D --daemonize Fork the process into a background daemon\n"
" -p --vty-port PORT Set the vty port to listen on.\n"
+ " -r --ctrl-port PORT Set the ctrl port to listen on.\n"
"\nVTY reference generation:\n"
" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"
" --vty-ref-xml Generate the VTY reference XML output and exit.\n"
@@ -93,12 +96,13 @@ static void handle_options(int argc, char **argv)
{ "version", 0, 0, 'V' },
{ "daemonize", 0, 0, 'D' },
{ "vty-port", 1, 0, 'p' },
+ { "ctrl-port", 1, 0, 'r' },
{ "vty-ref-mode", 1, &long_option, 1 },
{ "vty-ref-xml", 0, &long_option, 2 },
{ 0, 0, 0, 0 }
};
- c = getopt_long(argc, argv, "hc:p:VD",
+ c = getopt_long(argc, argv, "hc:p:r:VD",
long_options, &option_idx);
if (c == -1)
break;
@@ -120,7 +124,14 @@ static void handle_options(int argc, char **argv)
case 'p':
vty_port = atoi(optarg);
if (vty_port < 0 || vty_port > 65535) {
- fprintf(stderr, "Invalid port %d given!\n", vty_port);
+ fprintf(stderr, "Invalid VTY port %d given!\n", vty_port);
+ exit(1);
+ }
+ break;
+ case 'r':
+ ctrl_port = atoi(optarg);
+ if (ctrl_port < 0 || ctrl_port > 65535) {
+ fprintf(stderr, "Invalid CTRL port %d given!\n", ctrl_port);
exit(1);
}
break;
@@ -223,6 +234,7 @@ extern int nsdummy_vty_init(void);
int main (int argc, char *argv[])
{
void *ctx = tall_nsdummy_ctx = talloc_named_const(NULL, 0, "osmo-ns-dummy");
+ struct ctrl_handle *ctrl;
int rc = 0;
osmo_init_logging2(ctx, &log_info);
@@ -235,6 +247,7 @@ int main (int argc, char *argv[])
vty_info.tall_ctx = ctx;
vty_init(&vty_info);
+ ctrl_vty_init(ctx);
logging_vty_add_cmds();
osmo_stats_vty_add_cmds();
osmo_talloc_vty_add_cmds();
@@ -259,13 +272,20 @@ int main (int argc, char *argv[])
fprintf(stderr, "No config file: '%s' Using default config.\n",
config_file);
- rc = telnet_init_dynif(ctx, NULL, vty_get_bind_addr(),
- vty_port);
+ rc = telnet_init_default(ctx, NULL, vty_port);
if (rc < 0) {
fprintf(stderr, "Error initializing telnet\n");
exit(1);
}
+ if (ctrl_port > 0) {
+ ctrl = ctrl_interface_setup(NULL, ctrl_port, NULL);
+ if (!ctrl) {
+ fprintf(stderr, "Failed to initialize control interface. Exiting.\n");
+ exit(1);
+ }
+ }
+
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
signal(SIGPIPE, sighandler);
diff --git a/utils/osmo-sim-test.c b/utils/osmo-sim-test.c
index ae55b83e..2c6c7641 100644
--- a/utils/osmo-sim-test.c
+++ b/utils/osmo-sim-test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
diff --git a/utils/osmo-stat-dummy/Makefile.am b/utils/osmo-stat-dummy/Makefile.am
new file mode 100644
index 00000000..0232736b
--- /dev/null
+++ b/utils/osmo-stat-dummy/Makefile.am
@@ -0,0 +1,10 @@
+if ENABLE_UTILITIES
+noinst_PROGRAMS = osmo-stat-dummy
+osmo_stat_dummy_SOURCES = osmo-stat-dummy.c
+osmo_stat_dummy_LDADD = $(TALLOC_LIBS) \
+ $(top_builddir)/src/vty/libosmovty.la \
+ $(top_builddir)/src/ctrl/libosmoctrl.la \
+ $(top_builddir)/src/core/libosmocore.la
+osmo_stat_dummy_CFLAGS = -Wall $(TALLOC_CFLAGS)
+osmo_stat_dummy_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+endif
diff --git a/utils/osmo-stat-dummy/README.md b/utils/osmo-stat-dummy/README.md
new file mode 100644
index 00000000..34ffbb4c
--- /dev/null
+++ b/utils/osmo-stat-dummy/README.md
@@ -0,0 +1,12 @@
+# Osmocom utilities
+
+* osmo-stat-dummy: utility for rate counter and statsd testing
+
+It has 2 rate counters: one ticks twice a seconds, another one can be manually updated with 'update-rate-ctr' command via vty.
+
+The raw value is sent via statsd protocol. If you install "netdata" monitoring tool than you can open http://localhost:19999 in browser
+and observe live counters monitoring under "StatsD dummy" without any additional setup.
+
+Opening osmo-stat-dummy.html in browser while both netdata and osmo-stat-dummy are running will show dimensioned (per min/hour/day) rate counters as well as raw data.
+
+The latter is handy for troubleshooting and comparing libosmocore's internal rate counter computation.
diff --git a/utils/osmo-stat-dummy/osmo-stat-dummy.c b/utils/osmo-stat-dummy/osmo-stat-dummy.c
new file mode 100644
index 00000000..37da7b32
--- /dev/null
+++ b/utils/osmo-stat-dummy/osmo-stat-dummy.c
@@ -0,0 +1,335 @@
+/* Rate counter and statsd test application */
+/* (C) 2022 by 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 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 General Public License for more details.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <signal.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/ctrl/control_if.h>
+#include <osmocom/ctrl/control_vty.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/ports.h>
+#include <osmocom/vty/tdef_vty.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/stats.h>
+#include <osmocom/vty/misc.h>
+
+#include "config.h"
+
+void *tall_statdummy_ctx = NULL;
+static bool quit = false;
+static bool config_given = false;
+struct rate_ctr_group *g_ctrg;
+
+enum dummy_rate_ctr_idx {
+ DUMMY_VTY = 0,
+ DUMMY_AUTO,
+};
+
+static void print_help(void)
+{
+ printf("Some useful options:\n"
+ " -h --help This text\n"
+ " -c --config-file Specify the filename of the config file\n"
+ " -V --version Print version\n"
+ "\nVTY reference generation:\n"
+ " --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"
+ " --vty-ref-xml Generate the VTY reference XML output and exit.\n"
+ );
+}
+
+static void handle_long_options(const char *prog_name, const int long_option)
+{
+ static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
+
+ switch (long_option) {
+ case 1:
+ vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
+ if (vty_ref_mode < 0) {
+ fprintf(stderr, "%s: Unknown VTY reference generation mode '%s'\n", prog_name, optarg);
+ exit(2);
+ }
+ break;
+ case 2:
+ fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
+ get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
+ get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
+ vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
+ exit(0);
+ default:
+ fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
+ exit(2);
+ }
+}
+
+static char *handle_options(int argc, char **argv)
+{
+ char *config_file = NULL;
+
+ while (1) {
+ int option_idx = 0, c;
+ static int long_option = 0;
+ static const struct option long_options[] = {
+ { "help", 0, 0, 'h' },
+ { "config-file", 1, 0, 'c' },
+ { "version", 0, 0, 'V' },
+ { "vty-ref-mode", 1, &long_option, 1 },
+ { "vty-ref-xml", 0, &long_option, 2 },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "hc:V", long_options, &option_idx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_help();
+ exit(0);
+ break;
+ case 0:
+ handle_long_options(argv[0], long_option);
+ break;
+ case 'c':
+ if (config_file)
+ free(config_file);
+ config_file = optarg;
+ config_given = true;
+ break;
+ case 'V':
+ print_version(1);
+ exit(0);
+ break;
+ default:
+ fprintf(stderr, "Unknown option '%c'\n", c);
+ exit(0);
+ break;
+ }
+ }
+
+ if (!config_file)
+ return "osmo-stat-dummy.cfg";
+
+ return config_file;
+}
+
+void sighandler(int sigset)
+{
+ if (sigset == SIGPIPE)
+ return;
+
+ fprintf(stderr, "Signal %d received.\n", sigset);
+
+ switch (sigset) {
+ case SIGINT:
+ case SIGTERM:
+ /* If another signal is received afterwards, the program
+ * is terminated without finishing shutdown process.
+ */
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGABRT, SIG_DFL);
+ signal(SIGUSR1, SIG_DFL);
+ signal(SIGUSR2, SIG_DFL);
+
+ quit = 1;
+ break;
+ case SIGABRT:
+ /* in case of abort, we want to obtain a talloc report and
+ * then run default SIGABRT handler, who will generate coredump
+ * and abort the process. abort() should do this for us after we
+ * return, but program wouldn't exit if an external SIGABRT is
+ * received.
+ */
+ talloc_report_full(tall_statdummy_ctx, stderr);
+ signal(SIGABRT, SIG_DFL);
+ raise(SIGABRT);
+ break;
+ case SIGUSR1:
+ case SIGUSR2:
+ talloc_report_full(tall_statdummy_ctx, stderr);
+ break;
+ }
+}
+
+static int rate_ctr_timer_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ uint64_t expire_count;
+ int rc;
+
+ /* check that the timer has actually expired */
+ if (!(what & OSMO_FD_READ))
+ return 0;
+
+ /* read from timerfd: number of expirations of periodic timer */
+ rc = read(ofd->fd, (void *) &expire_count, sizeof(expire_count));
+ if (rc < 0 && errno == EAGAIN)
+ return 0;
+
+ OSMO_ASSERT(rc == sizeof(expire_count));
+
+ if (expire_count > 1)
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Stats timer expire_count=%" PRIu64 ": We missed %" PRIu64 " timers\n",
+ expire_count, expire_count-1);
+
+ /* Increment the counter value */
+ rate_ctr_inc(rate_ctr_group_get_ctr(g_ctrg, DUMMY_AUTO));
+
+ return 0;
+}
+
+DEFUN(update_rate_ctr, update_rate_ctr_cmd,
+ "update-rate-ctr <0-100000>",
+ "Update dummy rate counter\n"
+ "Value to add to rate counter\n")
+{
+ rate_ctr_add(rate_ctr_group_get_ctr(g_ctrg, DUMMY_VTY), atoi(argv[0]));
+
+ return CMD_SUCCESS;
+}
+
+static int statdummy_vty_init(void)
+{
+ install_element_ve(&update_rate_ctr_cmd);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct log_info log_info = {};
+ char *config_file;
+ void *ctx = tall_statdummy_ctx = talloc_named_const(NULL, 0, "osmo-stat-dummy");
+ struct ctrl_handle *ctrl;
+ struct osmo_fd rate_ctr_timer = { .fd = -1 };
+ struct timespec ts_interval = { .tv_sec = 0, .tv_nsec = 500000000 }; /* 0.5 seconds */
+ int rc = 0;
+
+ const char vty_copyright[] =
+ "Copyright (C) 2022 by by sysmocom - s.f.m.c. GmbH\r\n"
+ "Author: Max Suraev <msuraev@sysmocom.de>\r\n"
+ "License GNU GPL version 3 or later\r\n"
+ "This is free software: you are free to change and redistribute it.\r\n"
+ "There is NO WARRANTY, to the extent permitted by law.\r\n";
+
+ struct vty_app_info vty_info = {
+ .name = "OsmoSTATdummy",
+ .version = PACKAGE_VERSION,
+ .copyright = vty_copyright,
+ .tall_ctx = ctx
+ };
+
+ const struct rate_ctr_desc dummy_ctr_desc[] = {
+ [DUMMY_VTY] = { "dummy:vty", "Dummy counter updated via VTY" },
+ [DUMMY_AUTO] = { "dummy:auto", "Dummy counter autoupdated via timer" },
+ };
+
+ const struct rate_ctr_group_desc dummy_ctrg_desc = {
+ "dummy",
+ "dummy stat tester",
+ OSMO_STATS_CLASS_GLOBAL,
+ ARRAY_SIZE(dummy_ctr_desc),
+ dummy_ctr_desc,
+ };
+
+ osmo_init_logging2(ctx, &log_info);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_log_level(osmo_stderr_target, LOGL_INFO);
+
+ msgb_talloc_ctx_init(ctx, 0);
+
+ vty_init(&vty_info);
+ ctrl_vty_init(ctx);
+ logging_vty_add_cmds();
+ osmo_stats_vty_add_cmds();
+ osmo_talloc_vty_add_cmds();
+
+ config_file = handle_options(argc, argv);
+
+ statdummy_vty_init();
+ rc = vty_read_config_file(config_file, NULL);
+ if (rc < 0) {
+ if (config_given) {
+ fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
+ exit(1);
+ }
+ fprintf(stderr, "No config file: '%s' Using default config.\n", config_file);
+ }
+
+ rc = telnet_init_default(ctx, NULL, -1);
+ if (rc < 0) {
+ fprintf(stderr, "Error initializing telnet\n");
+ exit(1);
+ }
+
+ ctrl = ctrl_interface_setup(NULL, 1234, NULL);
+ if (!ctrl) {
+ fprintf(stderr, "Failed to initialize control interface. Exiting.\n");
+ exit(1);
+ }
+
+ g_ctrg = rate_ctr_group_alloc(ctx, &dummy_ctrg_desc, 0);
+ if (!g_ctrg) {
+ fprintf(stderr, "Failed to initialize rate counters. Exiting.\n");
+ return -1;
+ }
+
+ osmo_stats_init(ctx);
+ rate_ctr_init(ctx);
+
+ rc = osmo_timerfd_setup(&rate_ctr_timer, rate_ctr_timer_cb, NULL);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Failed to setup the timer with error code %d (fd=%d)\n",
+ rc, rate_ctr_timer.fd);
+ return rc;
+ }
+
+ rc = osmo_timerfd_schedule(&rate_ctr_timer, NULL, &ts_interval);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Failed to schedule the timer with error code %d (fd=%d)\n",
+ rc, rate_ctr_timer.fd);
+ }
+
+ signal(SIGINT, sighandler);
+ signal(SIGTERM, sighandler);
+ signal(SIGPIPE, sighandler);
+ signal(SIGABRT, sighandler);
+ signal(SIGUSR1, sighandler);
+ signal(SIGUSR2, sighandler);
+ osmo_init_ignore_signals();
+
+ while (!quit) {
+ osmo_select_main(0);
+ }
+
+ telnet_exit();
+
+ talloc_report_full(tall_statdummy_ctx, stderr);
+ talloc_free(tall_statdummy_ctx);
+
+ return 0;
+}
diff --git a/utils/osmo-stat-dummy/osmo-stat-dummy.cfg b/utils/osmo-stat-dummy/osmo-stat-dummy.cfg
new file mode 100644
index 00000000..559fe4ed
--- /dev/null
+++ b/utils/osmo-stat-dummy/osmo-stat-dummy.cfg
@@ -0,0 +1,40 @@
+log stderr
+ logging filter all 1
+ logging color 0
+ logging timestamp 0
+ logging print extended-timestamp 0
+ logging print category-hex 0
+ logging print category 1
+ !logging print file basename
+ ! log-levels defined by libosmocore and hence available everywhere, can be overridden by inidividual per-app configs below
+ logging level lstats notice
+ logging level lglobal notice
+ logging level llapd notice
+ logging level linp notice
+ logging level lmux notice
+ logging level lmi notice
+ logging level lmib notice
+ logging level lsms notice
+ logging level lctrl notice
+ logging level lgtp notice
+ logging level lgsup notice
+ logging level loap notice
+ logging level lss7 error
+ logging level lsccp notice
+ logging level lsua notice
+ logging level lm3ua notice
+ logging level lmgcp notice
+ logging level ljibuf notice
+ logging level lrspro notice
+stats reporter statsd
+ !use default https://learn.netdata.cloud/ statsd plugin:
+ remote-ip 127.0.0.1
+ remote-port 8125
+ level global
+ no prefix
+ enable
+stats interval 1
+line vty
+ bind 127.0.0.2 6969
+ctrl
+ bind 127.0.0.11 1234
diff --git a/utils/osmo-stat-dummy/osmo-stat-dummy.html b/utils/osmo-stat-dummy/osmo-stat-dummy.html
new file mode 100644
index 00000000..fc772573
--- /dev/null
+++ b/utils/osmo-stat-dummy/osmo-stat-dummy.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+</head>
+<body>
+ <div data-netdata="statsd_dummy.0.dummy.auto_counter"
+ data-chart-library="dygraph"
+ data-title="raw data (timerfd ticks twice per second)"
+ data-width="600"
+ data-height="200"
+ data-after="-600"
+ ></div>
+ <div data-netdata="statsd_dummy.0.dummy.auto_counter"
+ data-chart-library="dygraph"
+ data-title="aggregated per minute (timerfd ticks twice per second)"
+ data-width="600"
+ data-height="200"
+ data-after="-600"
+ data-method="average"
+ data-gtime="60"
+ data-units="events/min"
+ ></div>
+ <div data-netdata="statsd_dummy.0.dummy.auto_counter"
+ data-chart-library="dygraph"
+ data-title="aggregated per hour (timerfd ticks twice per second)"
+ data-width="600"
+ data-height="200"
+ data-after="-600"
+ data-method="average"
+ data-gtime="3600"
+ data-units="events/hour"
+ ></div>
+ <div data-netdata="statsd_dummy.0.dummy.auto_counter"
+ data-chart-library="dygraph"
+ data-title="aggregated per day (timerfd ticks twice per second)"
+ data-width="600"
+ data-height="200"
+ data-after="-600"
+ data-method="average"
+ data-gtime="86400"
+ data-units="events/day"
+ ></div>
+
+ <div data-netdata="statsd_dummy.0.dummy.vty_counter"
+ data-chart-library="dygraph"
+ data-title="raw data (updated manually via vty command)"
+ data-width="600"
+ data-height="200"
+ data-after="-600"
+ ></div>
+</body>
+<script type="text/javascript" src="http://localhost:19999/dashboard.js"></script>
+</html>